mcpField
S
synapse

synapse

github.com/nimblebraininc開発340 回インストール2 週間前に更新
リポジトリ

synapse について

Synapseは、既存のMCPサーバー向けにReact UIを構築し、MCP Appsリソースとして提供するためのスキルです。SDKやホスト間の互換性、サーバー統合の手順を扱います。

主な機能

  1. 01React UIを単一HTMLにバンドル
  2. 02Synapseのフックとコンポーネントライブラリ
  3. 03ホスト機能に応じた動作
  4. 04ツールブリッジとリソースの統合
  5. 05ビルドと検証の手順

活用シーン

  1. 01MCPサーバーへのインタラクティブUI追加
  2. 02Claude、ChatGPT、NimbleBrainへの対応
  3. 03サーバー書き込み後のUIデータ更新

MCP 対応

バンドルしたUIをMCPリソースとして提供し、MCP Appsホストブリッジ経由で既存のサーバーツールを呼び出す方法を説明します。ホストへの登録と機能判定も扱います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nimblebraininc/skills --skill synapse

インストール後、Claude Code で/synapseを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
gotchas.md
host-contract.md
portability.md

詳細

Synapse — MCP サーバー向けの UI を構築する

MCP サーバーにインタラクティブな UI を追加します。この UI は、@nimblebrain/synapse と Vite を使って 1 つのインライン HTML ファイルにビルドされる React アプリです。サーバーから MCP リソース ui://<name>/main として配信され、postMessage ブリッジに接続されたサンドボックス化 iframe 内で、MCP Apps ホスト(Claude、ChatGPT、NimbleBrain)によってマウントされます。UI はこのブリッジを通じてサーバーの既存ツールを呼び出します。データレイヤーに依存せず、特別なサーバーフレームワークも必要ありません。すべてのホストは同じブリッジを使用します。宣言する機能はホストによって異なりますが、機能がない場合も SDK は予測可能な動作をします。コンポーネントを作成する前に、以下の移植性についてお読みください。

@nimblebrain/synapse@^0.20.0(npm で公開済み)を対象にしてください。依存するピアパッケージは @modelcontextprotocol/ext-apps@^1.7.5 および @modelcontextprotocol/sdk@^1.29.0 です。0.x ではキャレット範囲がマイナーバージョンをまたがないため、誰かが意図的に更新するまで、アプリは固定したバージョンを使い続けます。NimbleBrain で 0.20.0 を使うには、message、updateModelContext、および ai.nimblebrain/* 拡張機能を宣言するホストのリリースが必要です(nimblebrain#1239)。それ以前のホストでは、チャット、モデルコンテキスト、ホストアクション、キー転送は機能せず、ファイルピッカーは拒否されます。このパッケージ自体がドキュメントです。コードを書く前に、エクスポートされている型をお読みください。ガイドは synapse.nimblebrain.ai にあります。

事前確認 — SDK の型を読む(実際のドキュメント)

インストール済みパッケージ(node_modules/@nimblebrain/synapse/dist/*.d.ts)または公開リポジトリ(github.com/NimbleBrainInc/synapse)から、以下を確認してください。

  • react エントリー — AppProvider とそのフック(useApp、useCallTool、useCallToolAsTask、useDataSync、useTheme、useHostContext、useModelContext、useSendMessage、useAction、useFileUpload、useToolResult、useToolInput、useResize)。
  • ui エントリー — コンポーネントライブラリ(AppFrame、ListDetailLayout、ListRow、Table、Badge、Prose、ConfirmDialog、tokens、…)。スタイルを独自に実装しないでください。
  • 型のエクスポート — ToolCallResult、Theme、App インターフェース(hostCapabilities を含む)、HostCapabilityError、hostSupports、NIMBLEBRAIN_EXTENSIONS。

続いて references/gotchas.md(デバッグの手間を減らす、見落としやすい API の仕様)と references/host-contract.md(NimbleBrain のマニフェスト宣言とブリッジ)をお読みください。どちらも短いので、まず目を通してください。動作するか、それとも「なぜすべてのツール呼び出しが unauthenticated を返すのか」となるかの違いを知る手がかりになります。

移植性 — これは MCP アプリであり、NimbleBrain アプリではありません

Claude、ChatGPT、NimbleBrain はすべて MCP Apps を使用します。それぞれが ui/initialize の応答(hostCapabilities)で提供する機能を宣言し、Synapse のゲート付き呼び出しはすべて、送信前にその宣言を確認します。また、機能がない場合には、文書化された動作を 1 つだけ行います(resize はゲートされず、useDataSync はリッスンするだけです)。

  • 応答を待つリクエストは、送信せずに HostCapabilityError で拒否されます — useCallTool (serverTools)、readServerResource(serverResources)、downloadFile、useCallToolAsTask (タスク)、ファイルピッカー。
  • 応答を待たない呼び出しは何も送信しません — useSendMessage(message)、useModelContext (updateModelContext)、useAction、forwardKeys。openLink は URL 自体を開きます。
  • ホストを待つフックは初期値のままです — ホストが notifications/resources/list_changed を中継しない場合(またはサーバーがステップ 5 で 通知を宣言していない場合)、useToolResult/useToolInput は null のままで、 useDataSync は一度も呼び出されません。

移植可能なサブセットは、ハンドシェイク、テーマとホストコンテキスト、ツールの入力/結果、callTool、 sendMessage、updateModelContext、resize です。これを基に構築したアプリは、どこでも同じように動作します。 NimbleBrain 拡張機能は、synapse/action、synapse/request-file、synapse/keydown の 3 つだけで、 それぞれホストが hostCapabilities.experimental に ai.nimblebrain/<name> を宣言している場合にのみ使用します。

何もしない動作もユーザーには見えないため、宣言を読み取り、ホストが対応していない機能は非表示にしてください。 hostSupports(app, "requestFile")、app.hostCapabilities.message、app.supportsTasks を使います。 <AppProvider> はハンドシェイクが完了するまで何もレンダリングしないため、コンポーネントがこれらを読み取る前にすべて確定します。ハンドシェイクが返ってこない場合(ビルド済み HTML を直接開いた場合)は、プロバイダー配下は何もレンダリングされません。 フックごとの表、拡張機能の一覧、Claude と ChatGPT に同じアプリを提供する方法、2 つの接続エントリーポイントについては、references/portability.md を参照してください。

プロセス

  1. サーバーを分析する — 言語(Python/FastMCP または TS)、トランスポート(stdio 対 HTTP ネイティブ/エッジ経由)、ツール一覧と戻り値の形式、デプロイ形態(.mcpb バンドルかコンテナーイメージか。コンテナーの場合はステップ 7 で Node のビルドステージが必要です)。

  2. ui/ をスキャフォールドする — package.json(react/react-dom ^19、@nimblebrain/synapse@^0.20.0、@modelcontextprotocol/ext-apps@^1.7.5、@modelcontextprotocol/sdk@^1.29.0、vite、vite-plugin-singlefile、typescript。Markdown をレンダリングする場合に限り、marked と dompurify を追加)、vite.config.ts(react()、viteSingleFile()、synapseVite()、build.assetsInlineLimit: Infinity)、厳格な tsconfig.json、index.html、.gitignore(node_modules/、dist/、.vite/)を用意します。ビルドで npm ci を実行できるように、package-lock.json をコミットしてください。

  3. App.tsx を構築する — <AppProvider name="<server>" version="<version>"> を使用します。副作用を伴うインポートは 1 つだけ Vite のエントリーポイント(main.tsx)に記述します:import "@nimblebrain/synapse/ui/base"(AppFrame が埋めるルートの高さの連鎖。最初の描画前に適用されます。注意点 M)。フォントはインポートしないでください — SDK にはフォントが含まれておらず、他のテーマ値と同様にホストからタイポグラフィが渡されます(注意点 N)。パッケージの AppFrame シェルを使い(AppFrame.Body bleed 内に ListDetailLayout/SidebarLayout を配置)、独自に height:100vh を実装しないでください(注意点 D)。データの取得には useCallTool(name)、または useApp().callTool(name, args) をラップした薄い useCall<T>() を使います。サーバーが書き込みを通知したときに呼び出される useDataSync(() => …) で再取得します(ステップ 5)。コールバックではツール名を指定しないため、常に再取得して結果を返します。テーマには tokens/useTheme を、エージェントコンテキストの送信には useModelContext を使います。マスターリストには Table ではなく ListRow を使ってください(注意点 D — Table は固定幅のレールからはみ出し、詳細ペインに重なって表示されます)。

  4. レンダリングする HTML は必ずサニタイズしてください(保存型 XSS)。 サーバーまたはエージェントが作成した Markdown(メモ、説明、調査結果)を Prose/dangerouslySetInnerHTML 経由でレンダリングする場合は、必ず DOMPurify を先に実行してください:DOMPurify.sanitize(marked.parse(md, { async: false }) as string)。iframe は allow-scripts と script-src 'unsafe-inline' の CSP で実行されるため、注入された <script>/onerror がツールブリッジの完全な権限で実行されるのを防ぐのは、CSP ではなくサニタイズだけです(ツールがアクセスできるすべてのものの読み取り/外部送信/変更)。プレーンな <Text>{value}</Text> は安全です(React がエスケープします)。一連の流れの詳細:gotcha K。

  5. UI をリソースとして提供し、書き込みを通知してください。 ビルド済みの ui/dist/index.html を返す @mcp.resource("ui://<name>/main") を使用し、仕様で定められた MIME タイプ text/html;profile=mcp-app で提供します(fastmcp 3.4+ では ui:// URI のデフォルトです。それ以外では mime_type を明示的に渡してください)。パスは環境変数(<APP>_UI_DIR)で解決し、__file__ 相対パスをフォールバックとして使用します(gotcha E — インストール済みパッケージは site-packages に配置されるため、__file__ 相対の ui/dist 検索では見つかりません)。ファイルをそのまま読み取るだけにしてください:DB/認証セッションは不要で、HTML に ID 情報を含めず、テナントデータはすべて実行時にブリッジ経由で取得します。その後、書き込みを行うすべてのツールで、書き込みのコミット後に notifications/resources/list_changed を送信してください。これが useDataSync を起動する唯一の通知であり、すべての書き込み元(エージェント、別のビュー、Webhook)をカバーします:

    from fastmcp import Context
    from mcp.types import ResourceListChangedNotification
    
    @mcp.tool()
    async def save_item(item: Item, ctx: Context) -> dict:
        stored = store.save(item)
        await ctx.session.send_notification(ResourceListChangedNotification(), related_request_id=ctx.request_id)
        return stored
    

    related_request_id を指定すると、HTTP 経由で呼び出し自体のレスポンス内に通知を含められます。書き込みごとに一度通知し、行ごとには通知しないでください。ガイド全文:synapse.nimblebrain.ai/docs/guides/keep-ui-in-sync/。

  6. アプリをホストに登録してください。 手順 2~5 で作成したバンドルはどちらも同じで、登録方法だけが異なります。NimbleBrain ホストを対象にする場合 — manifest.json に _meta["ai.nimblebrain/host"] を追加します:placements[] エントリーを 1 つ指定します(slot: "sidebar.apps"、resourceUri: "ui://<name>/main"、route、label、icon)。登録仕様とオプションの詳細:references/host-contract.md。Claude または ChatGPT を対象にする場合 — ホストがツールで指定されたリソースをレンダリングします。ツールに _meta.ui.resourceUri を設定してそのリソースにバインドし、text/html;profile=mcp-app でそのリソースを 1 つだけ提供します(別の MIME タイプで 2 つ目のコピーを提供しないでください)。ui.visibility と ui.csp を宣言し、サーバーでサインインが必要な場合は securitySchemes と 401 チャレンジも設定します。Python サーバーでは、nimblebrain-synapse の SynapseUI がこれらすべてを設定します。その後、npx @nimblebrain/synapse check --target claude,chatgpt <server-url> を実行します。詳細:references/portability.md。

  7. コンテナにデプロイするサーバー — マルチステージビルド。 .mcpb バンドルではリリース CI から ui/dist を取得しますが、コンテナイメージではビルドが必要です:node:22 のビルダーステージで npm ci && npm run build を実行し、その後ランタイムで COPY --from=builder …/ui/dist を行います(ランタイムに Node を含めないため)。さらに UI ディレクトリの環境変数を設定します。ランタイムレイヤーから ui/ のソースを除外し、dist だけを含めてください。

  8. ローカルプレビュー — cd ui && npm run dev → /__preview を開きます。エッジ経由で公開されるサーバー(HTTP ヘッダーから ID を取得し、DB を使用)の場合、stdio プレビューでは実サーバーを操作できないため、すべての呼び出しが unauthenticated を返します。環境変数のフラグで有効にする シード済みの stdio モック を synapseVite({ serverCmd }) に指定してください(gotcha F)。

  9. 検証 — npx tsc --noEmit と npm run build を実行します(Vite/esbuild はそれだけでは型チェックを行いません)。その後、UI の型チェックだけで済ませず、サーバープロジェクト独自の lint/format/test ゲート(例:make verify、ruff format --check)も実行してください。型が通っていても、フォーマットのみの差分で CI が失敗することがあります。プレビューをライトモードとダークモードに切り替え、すべての画面を目視で確認してください — テーマを考慮していない色指定は tsc/build では検出されず、片方のモードでのみ表示が崩れます。すべての tokens.* は両方のモードで定義されていますが、独自の変数はそうではありません(gotcha L)。サーバーが ui://<name>/main を text/html;profile=mcp-app として提供すること、プレビュー経由の書き込みでビューが更新されること(プレビューはサーバーの list_changed を中継します。gotcha F)を確認してください。

対象外

  • ホスト側のレンダリング — ホストには配置 → iframe → ブリッジがすでに実装されています。必要なのはバンドルの構築と配置の宣言だけで、それ以上は不要です。
  • 宣言型のホスト「コマンド」/ツールバー — そのような機能はありません。アプリ内のボタンは iframe 内に配置し、useCallTool を使ってツールを呼び出します。

コメント