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が作成されます。また同名のリソースが作成され、ツールに紐づけられます。
登録後に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アプリをホストできることが確認できました。
今回の記事は以上になります。
完





