2026年4月15日水曜日

MCP AppsのサンプルアプリMap Serverをデータベースで実行する

GitHubのリポジトリmodelcontextprotocol/ext-appsに含まれているMap Serverをデータベースでホストしてみます。CesiumJSを使って地図を表示するMCPアプリです。

オリジナルのMCPアプリを実行すると、以下のように表示されます。


データベースのリモートMCPサーバーとして実装し、MCPアプリをMCP Inspectorで呼び出してみました。


記事「MCP Appsのサンプルアプリbasic-server-vanillajsをデータベースで実行する」で作成したリモートMCPサーバーext-appにMap Serverを追加します。

オリジナルのMap Serverは2つのツールshow-mapおよびgeocodeを呼び出しますが、ツールgeocodeについては省略し、show-mapのみを実装します。

最初にツールshow-mapの本体となる、PL/SQLのファンクションshow_mapを作成します。


APEXのSQLコマンドから実行するとよいでしょう。TypeScriptの実装をPL/SQLに、そのまま書き直しただけです。


APEXアプリケーションを実行し、UIリソースを作成します。

Resourcesを開き、以下を設定してMap ServerのUIリソースを作成します。

Uri: ui://map-server/mcp-app.html
Name: Map Server
Description: Display an interactive world map zoomed to a specific bounding box.


Bundleを開き、Resource IDに先ほど作成したMap Serverを選択します。bundle html fileext-apps/examples/map-server/dist/mcp-app.htmlを選択し、Updateします。


以上でUIリソースが作成されました。

Toolsを開き、ツールとしてshow-mapを作成します。

Code: show-map
Description: Display an interactive world map zoomed to a specific bounding box.
Function Call: return show_map(:parameters);
Tags: ext-apps
Resource Id: Map Server

Input Schemaには以下を記述します。



MCPアプリのMap Serverは外部へのネットワーク接続を行うようです。そのため、CSPのconnectDomainおよびresourceDomainを設定します。

CSPを設定する表はデータベースに作成済みですが、ユーザー・インターフェースがありません。以下のSQLを実行し、CSP関連の設定を表OJ_MCP_CSP_DOMAINSに設定します。

APEXのSQLコマンドから実行します。


以上でMap Serverの実装は完了です。

MCP InspectorからMCPアプリを実行します。

npx -y @modelcontextprotocol/inspector

リモートMCPサーバーに接続し、Appsタブを開いてshow-mapを選択します。

東京近辺を表示するように、以下を入力します。

west: 138.5
south: 34.8
east: 140.9
north: 36.8

Open Appをクリックします。


CesiumJSの地図に東京近辺が表示されます。Back to Inputをクリックします。


シンガポール付近を表示してみます。以下の値を入力し、Open Appをクリックします。

west: 103.0
south: 0.8
east: 104.6
north: 1.9

シンガポール近辺が表示されます。


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

MCP Appsのサンプルアプリbasic-serverのPreact,React,Solid,Svelte,Vue版をデータベースで実行する

先日の記事「MCP Appsのサンプルアプリbasic-server-vanillajsをデータベースで実行する」では、VanillaJS(UIフレームワークを使用しない)で作成したMCPアプリをデータベースでホストしました。

GitHubのリポジトリmodelcontextprotocol/ext-appsには、VanillaJS以外にUIフレームワークとしてPreact、React、Solid、Svelte、Vueを使ったMCPアプリのサンプルが含まれています。これらのアプリケーションはすべてツールget-timeを呼び出しています。また、UI自体の見かけはVanillaJSのMCPアプリと同じになっています。

これらのMCPアプリをデータベースでホストしてみます。データベースでのリモートMCPサーバーの実装はresources/read呼び出しを受けて、データベースに保存しているバンドル化されたMCPアプリのソース(HTML+JavaScript+CSS)をクライアントに戻すだけです。そのため、ツールの実装さえ同じであればMCPアプリは一切変更なしで動作するはずです。

結果ですが、modelcontextprotocol/ext-appsにあるbasic-server-preact、basic-server-react、basic-server-solid、basic-server-svelte、basic-server-vueのすべてのMCPアプリが変更せずに動作しています。

作業自体は記事「MCP Appsのサンプルアプリbasic-server-vanillajsをデータベースで実行する」と同じ手順で、作成したリソースにそれぞれのMCPアプリのバンドルを登録します。MCPアプリのUIバンドルはext-apps/examples/basic-server-xxxx/dist/mcp-app.htmlとして作成されています。


Preact、React、Solid、Svelte、Vueの5つのMCPサーバーを登録しますが、ひとつひとつ手作業で登録するのはつらいので、以下のスクリプトを書きました。


APEXのSQLコマンドから上記のスクリプトを実行すると、ファンクションget_timeを呼び出すツールとしてget-time-preact、get-time-react、get-time-solid、get-time-svelte、get-time-vueが作成されます。また同名のリソースが作成され、ツールに紐づけられます。


以上でツールとリソースは作成されます。この後に、リソースにそれぞれのUIバンドルmcp-app.htmlを登録します。

登録後にMCP Inspectorを使って動作を確認します。

npx -y @modelcontextprotocol/inspector

MCPサーバーext-appsに接続し、MCP Appsを一覧します。全部で6つのアプリが選択できます。


アプリの作成に使用しているUIフレームワークが異なるだけで、MCPアプリ自体はどれでも同じ見かけと動作をします。


ただし、VanillaJSではServer Timeは以下のように表示されます。


他のアプリは以下のように表示されます。


コードを確認すると、VanillaJSでの時刻の取り出しは以下のファンクションで行われていました。構造化出力を優先して参照しています。
function extractTime(result: CallToolResult): string {
  const { time } = (result.structuredContent as { time?: string }) ?? {};
  return time ?? "[ERROR]";
}
Preactを確認すると以下でした。構造化出力を参照していません。
function extractTime(callToolResult: CallToolResult): string {
  const { text } = callToolResult.content?.find((c) => c.type === "text")!;
  return text;
}
現在時刻の表示の違いは、ファンクションget_timeのレスポンスの取得方法が異なることが原因でした。

この他Reactについては、Received a response for an unknown message IDというエラーが発生しています。Reactのみで発生していることと、エラーメッセージにあるmessage IDはMCPアプリとホストの間でやり取りするメッセージに割り振られているIDで、ホストとデータベースで実行されるMCPサーバー(ツール)の間のメッセージに割り振られているIDではありません。よって、これはMCPアプリ側の問題だと思われます。

以上でデータベースに実装したリモートMCPサーバーで、各種UIフレームワークで作成したMCPアプリをホストできることが確認できました。

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

2026年4月14日火曜日

MCP Appsのサンプルアプリbasic-server-vanillajsをデータベースで実行する

GitHubのリポジトリmodelcontextprotocol/ext-appsでは、 複数のMCPアプリの実装例が提供されています。このサンプルのうち、examples/basic-server-vanillajsをデータベースに実装し、MCP Inspectorから動作を確認します。

MCP Inspectorでは以下のように動作します。MCPアプリのbasic-server-vanillajsから呼び出されるツールは、サーバーの現在時刻を返すget-timeのみです。


MCP Appsはその仕組みより、MCPサーバーとのやり取りはWindow.postMessageを介したツール呼び出しに限定されます。そのため、MCPサーバーが何で実装されていても、ツール呼び出しの仕様が同じであればMCPアプリは変更せずに動作します。

今回はbasic-server-vanillajsから呼び出されるツールget-timeをOracle Databaseに実装し、basic-server-vanillajsをデータベースでMCP Appsとしてホストします。

以下の記事にそって作成した環境で、作業を実施します。
リモートMCPサーバーを実装するコードはリポジトリmcp-appにあります。basic-server-vanillajsを実装するにあたってコードを修正しているため、これからに作業には最新のコードが必要です。

作業はmacOSで実施しています。


ext-appsのbasic-server-vanillajsの実行



リポジトリext-appsをクローンし、ext-appsのexamplesを実行します。

git clone https://github.com/modelcontextprotocol/ext-apps
cd ext-apps

% git clone https://github.com/modelcontextprotocol/ext-apps

Cloning into 'ext-apps'...

remote: Enumerating objects: 24263, done.

remote: Counting objects: 100% (5297/5297), done.

remote: Compressing objects: 100% (1139/1139), done.

remote: Total 24263 (delta 4777), reused 4233 (delta 4152), pack-reused 18966 (from 3)

Receiving objects: 100% (24263/24263), 142.89 MiB | 15.78 MiB/s, done.

Resolving deltas: 100% (15369/15369), done.

% cd ext-apps

ext-apps % 


ext-appsに含まれているexamplesをインストールします。bunが無い、zodが無いといった依存性に関するエラーが発生しましたが、その都度ChatGPT(またはClaude)に聞いて解決しました。

npm install

ext-apps % npm install

npm warn deprecated whatwg-encoding@3.1.1: Use @exodus/bytes instead for a more spec-conformant and faster implementation


> @modelcontextprotocol/ext-apps@1.5.0 prepare

> npm run build && husky



> @modelcontextprotocol/ext-apps@1.5.0 build

> npm run generate:schemas && npm run sync:snippets && node scripts/run-bun.mjs build.bun.ts && node scripts/link-self.mjs



> @modelcontextprotocol/ext-apps@1.5.0 generate:schemas

> tsx scripts/generate-schemas.ts && prettier --write "src/generated/**/*"


🔧 Generating Zod schemas from spec.types.ts...


✅ Written: /Users/ynakakoshi/Documents/ext-apps/src/generated/schema.ts

✅ Written: /Users/ynakakoshi/Documents/ext-apps/src/generated/schema.test.ts

✅ Written: /Users/ynakakoshi/Documents/ext-apps/src/generated/schema.json


🎉 Schema generation complete!

src/generated/schema.json 49ms

src/generated/schema.test.ts 24ms

src/generated/schema.ts 26ms


> @modelcontextprotocol/ext-apps@1.5.0 sync:snippets

> bun scripts/sync-snippets.ts


🔧 Syncing code snippets from example files...


✅ No files needed modification


🎉 Snippet sync complete!


added 573 packages, and audited 622 packages in 8s


119 packages are looking for funding

  run `npm fund` for details


3 vulnerabilities (2 moderate, 1 high)


To address all issues, run:

  npm audit fix


Run `npm audit` for details.

ext-apps %


サンプルを実行します。

ポート番号がAPEXの環境とコンフリクトするようなので、APEXのコンテナは停止しておくのが無難です。

npm run start

出力メッセージの中間くらいに、Host serverのURLが表示されます。
デフォルトではhttp://localhost:8080になります。

ext-apps % npm run start


> @modelcontextprotocol/ext-apps@1.5.0 start

> npm run examples:dev



> @modelcontextprotocol/ext-apps@1.5.0 examples:dev

> NODE_ENV=development bun examples/run-all.ts dev


Running command: dev

Server examples: basic-server-preact:3101, basic-server-react:3102, basic-server-solid:3103, basic-server-svelte:3104, basic-server-vanillajs:3105, basic-server-vue:3106, budget-allocator-server:3107, cohort-heatmap-server:3108, customer-segmentation-server:3109, debug-server:3110, integration-server:3111, map-server:3112, pdf-server:3113, qr-server:3114, quickstart:3115, say-server:3116, scenario-modeler-server:3117, shadertoy-server:3118, sheet-music-server:3119, system-monitor-server:3120, threejs-server:3121, transcript-server:3122, video-resource-server:3123, wiki-explorer-server:3124


[basic-server-solid] 

[basic-server-solid] > @modelcontextprotocol/server-basic-solid@1.5.0 dev

[basic-server-solid] > cross-env NODE_ENV=development concurrently "npm run watch" "npm run serve"

[basic-server-solid] 

[map-server] 


[中略]


[cohort-heatmap-server] [1] 

[cohort-heatmap-server] [1] > @modelcontextprotocol/server-cohort-heatmap@1.5.0 serve:http

[cohort-heatmap-server] [1] > bun --watch main.ts

[cohort-heatmap-server] [1] 

[basic-host] [1] Host server:    http://localhost:8080

[basic-host] [1] Sandbox server: http://localhost:8081

[basic-host] [1] 

[basic-host] [1] Press Ctrl+C to stop

[basic-host] [1] 

[integration-server] [1] 

[integration-server] [1] > integration-server@1.5.0 serve:http

[integration-server] [1] > bun --watch main.ts

[integration-server] [1] 


[中略]


[threejs-server] [0] dist/mcp-app.html  10,374.90 kB │ gzip: 2,151.32 kB

[threejs-server] [0] built in 3070ms.

[pdf-server] [0] dist/mcp-app.html  10,436.10 kB │ gzip: 2,377.31 kB

[say-server] npm error Lifecycle script `dev` failed with error:

[say-server] npm error code 1

[say-server] npm error path /Users/ynakakoshi/Documents/ext-apps/examples/say-server

[say-server] npm error workspace @modelcontextprotocol/server-say@1.5.0

[say-server] npm error location /Users/ynakakoshi/Documents/ext-apps/examples/say-server

[say-server] npm error command failed

[say-server] npm error command sh -c uv run --index https://pypi.org/simple server.py

[pdf-server] [0] built in 3304ms.

[say-server] npm run --workspace examples/say-server dev exited with code 1


ブラウザよりサンプルを呼び出します。

http://localhost:8080

ServerBasic MCP App Server(Vanilla JS)を選択し、Call Toolをクリックします。呼び出せるツールはget-timeのみです。


ブラウザにMCPアプリが表示されます。

Get Server Timeをクリックすると、Server Timeが更新されます。


このMCPアプリbasic-server-vanillajsをデータベースでホストします。

これからはOracle APEXでの作業に移ります。Oracle APEXをホストしているデータベースやORDSを起動しておきます。

アプリケーション・ビルダーからインポートを実行します。


リポジトリmcp-appに含まれるMCP App Helperアプリmcp-app/app/mcp-app-helper.sqlをインポートします。


アプリケーションのインストールを実行します。


アプリケーションがインストールされます。

アプリケーションの編集を開き、アプリケーションの名前と別名を更新します。


アプリケーションの名前ext-apps別名ext-appsとします。


アプリケーションを実行します。

最初にHandlersを開き、リモートMCPサーバーのエンドポイントとなるORDS RESTサービスをインストールします。

作成をクリックします。


ORDSのRESTモジュール名はAPEXアプリの別名と同じにします。これはリモートMCPサーバーの実装上、必須になっています。

リモートMCPサーバーのエンドポイントURLは以下になります。

http://localhost:8181/ords/ORDS別名/ext-apps/mcp

デフォルトの設定のまま、作成をクリックします。


ORDSのRESTモジュールとしてext-apps、テンプレートとしてmcp、そのテンプレートにPOSTハンドラとDELETEハンドラが作成されます。


システムの現在時刻を返すPL/SQLファンクションget_timeを作成します。

SQLコマンドより実行します。



ext-appsに含まれるget-timeは、テキスト出力と構造化出力の両方で現在時刻を返します。
{
  "content": [
    {
      "type": "text",
      "text": "2026-04-14T06:09:37.819Z"
    }
  ],
  "structuredContent": {
    "time": "2026-04-14T06:09:37.819Z"
  }
}
データベースでの実装ではテキスト出力と構造化出力を別々に出力する方法を実装していないため、テキスト出力もJSON形式で返すようになってしまっています。ツールにoutputSchema定義を含んでいると構造化出力を参照するので、テキスト出力がJSONでも問題なさそうです。

Toolsを開き、ツールとしてget-timeを作成します。

作成をクリックします。


ツールget-timeとして以下を設定し、作成します。

Code: get-time
Description: Returns the current server time as an ISO 8601 string.
Input Schema: {"$schema":"http://json-schema.org/draft-07/schema#","type":"object"}
Function Call: return get_time();
Tags: ext-apps
Output Schema:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "time": {
      "type": "string"
    }
  }
}
UIリソースとの紐付けは、UIリソースの作成後に実施します。


Resourcesを開き、UIリソースとしてGet Timeを作成します。

作成をクリックします。


Uriui://get-time/mcp-app.htmlNameGet TimeDescriptionReturns the current server time as an ISO 8601 string.とします。

以上でUIリソースを作成します。


作成したUIリソースGet Timeに実際のアプリを登録します。Bundleを開きます。


Resource IDとしてGet Timeを選択し、bundle html fileにext-apps以下にあるexamples/basic-server-vanillajs/dist/mcp-app.htmlを選択します。

Updateをクリックし、MCPアプリをデータベースにアップロードします。


Toolsを開き、ツールget-timeにUIリソースGet Timeを紐付けます。


ツールget-timeを開き、Resource IdGet Timeを選択します。

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


以上で、データベースにMCPアプリbasic-server-vanillajsを登録できました。

MCP Inspectorを使って動作確認します。

npx -y @modelcontextprotocol/inspector

Transport TypeStreamable HTTPを選択し、URLにリモートMCPサーバーのエンドポイントとなるURLを指定します。

http://localhost:8181/ords/ORDS別名/ext-apps/mcp

ConnectボタンをクリックしてMCPサーバーに接続します。接続ができたら、Appsタブを開き、MCP Appsget-timeを選択します。


この後は、本記事の先頭のGIF動画のように動作します。

以上でMCPアプリのサンプルbasic-server-vanillajsをデータベースでホストすることができました。