ラベル MLE の投稿を表示しています。 すべての投稿を表示
ラベル MLE の投稿を表示しています。 すべての投稿を表示

2025年11月28日金曜日

ORDSで実行するMCPサーバーをMLE/JavaScriptで記述する

先日の記事「ORDSのRESTサービスをClaude Desktopのカスタムコネクタとして登録して呼び出す」で、ORDSにMCPサーバーを実装しました。今回はPL/SQLの代わりにMLE/JavaScriptでMCPサーバーを実装してみます。

Oracle REST Data ServicesのProduct ManagerであるPeter O'Brienさんの、以下のブログ記事を参考に実装しています。

Implement an MCP Server with ORDS and MLE JavaScript
https://peterobrien.blog/2025/08/16/implement-an-mcp-server-with-ords-and-mle-javascript/

作成するリモートMCPサーバーはPL/SQLの実装と同じく、ツールとして任意のSELECT文を実行するrun_sqlとスキーマ情報を取得するget_schemaを含みます。

PL/SQLでの実装では、ツールを画面で定義したり、ORDSのRESTサービスとツールの紐付けを画面上でできるようにAPEXアプリを作成していました。MLE/JavaScriptによる実装は、Peter O'Brienさんの記事を参考にして、すべてをスクリプトで実装しています。

元記事にあるtools/list、tools/callの処理は、ツールであるrun_sqlおよびget_schemaが含まれるように書き直しています。

PL/SQLでは実装していた、ヘッダーMcp-Session-Idを使ったセッション管理については省略しています。そのためDELETEハンドラはありません。


今回はAutonomous AI DatabaseのSQL Developer Webより実装します。PL/SQLのときと同様にリモートMCPサーバーには認証による保護がかけられません。対応としてORDSのRESTサービスの事前認証済リクエストを活用してみます。

Autonomous AI DatabaseのSQL Developer Web(Database Actions)に接続し、管理者ユーザーのADMINにてサインインします。


RESTサービスを実装するデータベース・ユーザーを作成します。

管理データベース・ユーザーを開きます。


ユーザーの作成をクリックします。


作成するユーザー名は任意です。本記事ではMCPSERVERとしました。パスワードを設定します。

REST、GraphQL、MongoDB APIおよびWebアクセスオンにし、Webアクセス拡張機能承認が必要オフにします。REST別名は小文字のmcpserverとします。このREST別名はリモートMCPサーバーのURLに含まれます。

以上でユーザーの作成を実行します。


データベース・ユーザーMCPSERVERが作成されます。


本記事ではRESTハンドラをMLE/JavaScriptで記述します。そのため、作成したデータベース・ユーザーMCPSERVERに、MLE/JavaScriptを実行する権限を与えます。

開発SQLワークシートを開き、以下の2行のコマンドを実行します。

GRANT EXECUTE ON JAVASCRIPT TO MCPSERVER;
GRANT EXECUTE DYNAMIC MLE TO MCPSERVER;


以上で管理者ユーザーADMINの作業は完了です。

ADMINをサインアウトして、SQL Developer WebにユーザーMCPSERVERでサインインしなおします。


開発SQLワークシートを開き、RESTサービス(MCPサーバー)を実装したコードを貼り付けて実行します。


開発RESTを開きます。コードを実行したことで、Module、Template、Handlerが作成されていることが確認できます。


この状態で、リモートMCPサーバーを保護なしで呼び出すことができます。

権限による保護を適用します。

セキュリティ権限を開きます。


権限の作成をクリックします。


ラベルsamplejs MCP server access名前oracle.mcp.samplejs説明MCPサーバーsamplejsの保護と記述します。


ロールタブを開き、RESTful Servicesを選択します。


保護されたモジュールを開き、samplejsを選択します。

以上で権限を作成します。


権限oracle.mcp.samplejsが作成されました。これでリモートMCPサーバーであるRESTサービスは、認証しないと呼び出せない状態になりました。


ユーザー認証は実装できないので、代わりに事前承認済リクエストを作成します。

モジュールを開きます。

作成済みのモジュールsamplejsの3点メニューより、テンプレートを開きます。


モジュールsamplejsに含まれるテンプレートmcpの3点メニューより、ハンドラを開きます。


テンプレートmcpに含まれるPOSTハンドラの3点メニューより、事前承認済リクエストから事前承認済リクエストの作成を実行します。


トークンの有効期限(秒)を調整し、作成をクリックします。


事前承認済リクエストが作成されます。

使用するのはURLだけなので、URLコピーします。OKをクリックしてダイアログを閉じます。


以上で作業は完了です。

Claude Desktopにカスタムコネクタを追加します。リモートMCPサーバーURLに、上記でコピーした事前承認済リクエストを指定します。

コネクタの設定画面を開き、カスタムコネクタの追加をクリックします。


コネクタの名前samplejsとします。リモートMCPサーバーURL事前承認済リクエストを設定します。

追加をクリックします。


ダイアログが再度開いたら、キャンセルをクリックして閉じます。


カスタムコネクタとしてsamplejsが追加されます。設定をクリックして確認します。


設定を開くと、ツールとしてget_schemarun_sqlが認識されていることが確認できます。


Claude Desktopで新規チャットを開始し、ツールsamplejsの動作を確認します。

「samplejsから確認できるデータを教えて。」

今までの手順通りに作業をしていると、スキーマMCPSERVERはほぼ空です。なので、実装したツールを呼び出して出来ることも少ないのですが、ツール自体が動作することは確認できます。


今回の記事は以上になります。

2025年6月25日水曜日

Oracle Database 23ai MLEにJIMPをロードし画像をグレースケールに変換する

Oracle CorporationのMartin Backさんが、setTimeout()のpolyfillを作成し、画像処理ライブラリのJIMPをMLEにロードして、画像をグレースケールに変換する手順をブログ記事で公開されています。

Multilingual Engine: polyfill timeouts and intervals

JIMPについては以前にMLEにロードを試みたことがありますが、Martin Backさんの記事にあるようにMLEにはsetTimeout()やfsモジュールが無いため諦めました。

今回はMartin Backさんの記事に沿って、MLEにJIMPとsetTimeout()のpolyfillをロードし、それを使用して画像をグレースケールに変換するAPEXアプリケーションを作成します。JIMPのロードやpolyfillの説明については、上記に掲載したMartin Backさんのブログ記事を参照してください。

作成するAPEXアプリケーションは以下のように動作します。


以下より、実施した作業を記述します。

APEXワークスペースに紐づくスキーマで、MLEモジュールのロードやJavaScriptを実行します。そのため、スキーマにJavaScriptを実行する権限を与えます。これらのスクリプトはDBA権限を持つユーザー(Autonomous DatabaseであればADMIN、オンプレミスであればSYS AS SYSDBA)で実行します。
grant execute on javascript to [APEXワークスペース・スキーマ];
grant execute dynamic mle to [APEXワークスペース・スキーマ];
grant create mle to [APEXワークスペース・スキーマ];
続いて、作業に使用するディレクトリを作成し、プロジェクトを初期化します。ディレクトリ名はdemo-jimpとします。今回の作業で使用したnpmのバージョンは11.3.0です。

npm -v
mkdir demo-jimp
cd demo-jimp
npm init -y


% npm -v         

11.3.0

% mkdir demo-jimp

% cd demo-jimp

demo-jimp % npm init -y

Wrote to /Users/**********/Documents/demo-jimp/package.json:


{

  "name": "demo-jimp",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "keywords": [],

  "author": "",

  "license": "ISC",

  "type": "commonjs"

}




demo-jimp % 


jimpをインストールします。

npm install jimp

demo-jimp % npm install jimp


added 70 packages, and audited 71 packages in 6s


10 packages are looking for funding

  run `npm fund` for details


found 0 vulnerabilities

demo-jimp % 


esbuildをインストールします。demo-mle.jsに画像をグレースケールに変換するファンクションimage2Greyscaleを記述し、esbuildを実行してdemo-mle.jsにJIMPをバンドルします。

npm install --save-exact --save-dev esbuild

demo-jimp % npm install --save-exact --save-dev esbuild



added 26 packages, and audited 97 packages in 2s


10 packages are looking for funding

  run `npm fund` for details


found 0 vulnerabilities

demo-jimp % 


Martin BackさんのGitHubのリポジトリよりdemo-mle.jspolyfill.jsをダウンロードし、src/javascript以下に配置します。

curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/demo-mle.js
curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/polyfill.js
mkdir -p src/javascript
mv demo-mle.js polyfill.js src/javascript
ls src/javascript

demo-jimp % curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/demo-mle.js

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  1023  100  1023    0     0   4903      0 --:--:-- --:--:-- --:--:--  4918

demo-jimp % curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/polyfill.js

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  2996  100  2996    0     0   7682      0 --:--:-- --:--:-- --:--:--  7701

demo-jimp % mkdir -p src/javascript

demo-jimp % mv demo-mle.js polyfill.js src/javascript

demo-jimp % ls src/javascript

demo-mle.js polyfill.js

demo-jimp % 


esbuildを実行しダウンロードしたdemo-mle.jsにJIMPをバンドルします。

npx esbuild src/javascript/demo-mle.js --bundle --outfile=dist/bundle.js --format=esm

demo-jimp % npx esbuild src/javascript/demo-mle.js --bundle --outfile=dist/bundle.js --format=esm


  dist/bundle.js  1008.8kb


Done in 44ms

demo-jimp % 


SQLclでAPEXのワークスペース・スキーマに接続し、MLEモジュールを作成します。

以下は、ローカルで実行しているOracle Database 23ai Freeに、ユーザーWKSP_APEXDEVで接続して作業した例になります。

sql wksp_apexdev/[パスワード]@localhost/freepdb1

demo-jimp % sql wksp_apexdev/********@localhost/freepdb1



SQLcl: 水 6月 25 12:31:27 2025のリリース25.1 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.8.0.25.04


SQL> 


demo-mle.jsにJIMPをバンドルしたdist/bundle.jsを、MLEモジュールJIMP_TEST_MODULEとして作成します。

mle create-module -replace -module-name jimp_test_module -filename dist/bundle.js

SQL> mle create-module -replace -module-name jimp_test_module -filename dist/bundle.js

MLEモジュールjimp_test_moduleが作成されました

SQL> 


setTimeout()のpolyfillを、MLEモジュールTIMERS_POLYFILLとして作成します。

mle create-module -replace -module-name timers_polyfill -filename src/javascript/polyfill.js

SQL> mle create-module -replace -module-name timers_polyfill -filename src/javascript/polyfill.js

MLEモジュールtimers_polyfillが作成されました

SQL> 


以上で、demo-mle.jsに含まれているファンクションimage2Greyscaleを、データベース内で呼び出せるようになりました。

画像をグレースケールに変換するAPEXアプリケーションを作成します。

変換する画像および変換後の画像を保存する表EBAJ_DEMO_IMAGESを作成します。

SQLワークショップSQLコマンドで、以下のDDLを実行します。
create table ebaj_demo_images (
    id              number generated by default on null as identity
                    constraint ebaj_demo_images_id_pk primary key,
    filename        varchar2(80 char),
    mime_type       varchar2(80 char),
    operation       varchar2(80 char),
    source_image    blob,
    target_image    blob
);

作成したMLEモジュールJIMP_TEST_MODULEおよびTIMERS_POLYFILLを含むMLE環境としてJIMP_ENVを作成します。以下のCREATE MLE ENV文を実行します。
create mle env jimp_env imports (
    'jimp' module jimp_test_module,
    'polyfill' module timers_polyfill
);

使用するデータベース・オブジェクトの準備ができました。

APEXアプリケーションを作成します。

アプリケーション・ビルダーよりアプリケーション作成ウィザードを開きます。アプリケーションの名前demo jimpとします。デフォルトで追加されているホーム・ページ削除します。


ホーム・ページを削除したのちページの追加をクリックし、代わりとなるフォームのページを追加します。

ページの追加をクリックしたときに開く画面でフォームを選択します。


ページ名グレースケール変換とします。に先ほど作成したEBAJ_DEMO_IMAGESを選択します。

ページの追加をクリックします。


表EBAJ_DEMO_IMAGESをソースとしたフォームのページが追加されました。

アプリケーションの作成をクリックします。


アプリケーションが作成されます。

画像のアップロードやグレースケールへの変換処理は、ページ番号1のグレースケール変換に実装します。


APEXアプリケーションからMLEモジュールを参照できるように、APEXアプリケーションにMLE環境を紐づけます。

アプリケーション定義セキュリティデータベース・セッションのセクションに含まれるMLE環境に、先ほど作成したMLE環境JIMP_ENVを設定します。

変更の適用をクリックします。


ページ・デザイナでページ、グレースケール変換を開きます。


主キー値を保持するページ・アイテムP1_IDセッション・ステートストレージセッションごと(永続)に変更します。

新規に画像がアップロードされる場合、ページ・アイテムP1_IDには空白(null)が渡されます。ページ送信時に実行されるプロセスでは、画像を含む行をデータベースに挿入する際に、INSERT INTO ... RETURNING id INTO :P1_ID という構文を用いて主キーの値を取得し、それをP1_IDに設定します。

しかし、セッション・ステートストレージリクエストごと(メモリのみ)になっていると、プロセスの処理が完了してフォームのページが再描画される際に、P1_IDの値がセッション・ステートに保存されておらず、結果としてP1_IDは空白のままになります。このため、直前にアップロードされた画像をP1_IDの値を用いて参照できません。

通常、フォームはレポート画面から開かれ、フォームを閉じるとレポート画面に戻る構成であるため、このような問題は発生しません。

しかし、今回のアプリケーションでは、フォームを閉じた後に再び同じフォームに戻る動作となっているため、P1_IDの値がページ遷移を超えて維持される必要があります。この対応として、セッション・ステートストレージセッションごと(永続)に変更します。


ページ・アイテムP1_FILENAMEP1_OPERATIONおよびP1_TARGET_IMAGEは非表示に変更します。


今回のAPEXアプリケーションでは使用しない属性ですが、表EBAJ_DEMO_IMAGESの列OPERATIONがつねにgreyscacleになるようにします。

ページ・アイテムP1_OPERATION計算を作成します。

実行ポイントとして送信後を選択し、計算タイプ静的値静的値としてgreyscaleを設定します。これでページ送信時のP1_OPERATIONの値がgreyscaleになります。


アップロードするファイルを選択するページ・アイテムP1_SOURCE_IMAGEタイプファイルのアップロードからイメージ・アップロードに変更します。

ストレージMIMEタイプ列MIME_TYPEファイル名列FILENAMEを設定します。


リージョンEbaj Demo Image外観テンプレートを装飾のないBlank with Attributesに変更します。


テンプレートが変更されたので、ボタンが配置されていたスロットが無くなっています。ボタンについては、Bodyの直下に横並びになるように移動します。

ボタンCANCELは削除し、CREATESAVEDELETEの順に並べます。

すべてのボタンの外観ホットオフにし、テンプレート・オプションWidthStretchに設定します。


ボタンDELETEレイアウト新規行の開始オフにし、ボタンSAVEの右隣に表示させます。


グレースケール変換前の画像を表示するページ・アイテムとしてP1_SOURCEを作成します。

タイプイメージの表示ラベルSource設定基準SQL文で戻されたBLOB列を選択し、SQL文として以下を記述します。
select source_image, null, filename, mime_type
from ebaj_demo_images
where id = :P1_ID

同様にグレースケール変換後の画像を表示するページ・アイテムとしてP1_TARGETを作成します。

ラベルTargetSQL文は以下になります。変換前の画像の右隣に表示させるため、レイアウト新規行の開始オフにします。
select target_image, null, filename, mime_type
from ebaj_demo_images
where id = :P1_ID

以上でユーザー・インターフェースについては完成です。

本記事の本題であるJIMPによるグレースケール処理を実装します。

プロセスを作成し、プロセス・フォームグレースケール変換の下に配置します。

識別名前greyscaleタイプコードを実行とします。ソース言語JavaScript(MLE)を選択し、JavaScriptコードとして以下を記述します。


サーバー側の条件タイプリクエストは値に含まれるを選択し、CREATE SAVEを入力します。ボタンCREATEおよびSAVEをクリックしたときに、プロセスgreyscaleが実行されます。


ボタンDELETEをクリックするとフォームに表示されている行(P1_IDが主キー値である行)が削除されます。フォームに設定されている削除済みの行のデータをリセットするプロセスを作成します。

識別名前resetタイプセッション・ステートのクリアを選択し、設定タイプとして現在のページのすべてのアイテムのクリアを選択します。

サーバー側の条件ボタン押下時DELETEです。


以上でアプリケーションは完成です。

アプリケーションを実行すると記事の先頭のGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/demo-jimp-in-apex-app.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。