webweaver-mcp-server について
webweaver-mcp-server は、WebWeaver Nexus の問い合わせフォーム、製品概要、連絡先を MCP 対応ホストに提供します。フォームはインタラクティブな MCP App として表示され、MCP ブリッジ経由で送信されます。
主な機能
- 01インタラクティブな問い合わせフォーム
- 02問い合わせ API へのフォーム送信
- 03製品概要の取得
- 04連絡先の取得
活用シーン
- 01MCP ホスト内でフォームを入力・送信
- 02WebWeaver Nexus の製品情報を確認
- 03WebWeaver Nexus の連絡方法を確認
MCP 対応
問い合わせフォーム、製品概要、連絡先のツールを提供する MCP サーバーです。フォームは MCP App として表示され、送信は MCP ブリッジ経由でサーバーツールを呼び出します。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://webweaver-nexus-mcp.vercel.app/mcpclaude mcp add --transport http webweaver-nexus-webweaver https://webweaver-nexus-mcp.vercel.app/mcp{"mcpServers":{"webweaver-nexus-webweaver":{"type":"http","url":"https://webweaver-nexus-mcp.vercel.app/mcp"}}}Cursor で開く{"servers":{"webweaver-nexus-webweaver":{"type":"http","url":"https://webweaver-nexus-mcp.vercel.app/mcp"}}}VS Code で開く{"mcpServers":{"webweaver-nexus-webweaver":{"type":"http","url":"https://webweaver-nexus-mcp.vercel.app/mcp"}}}{"mcpServers":{"webweaver-nexus-webweaver":{"serverUrl":"https://webweaver-nexus-mcp.vercel.app/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
4このツールにパラメーターはありません。
詳細
WebWeaver Nexus MCP サーバー
WebWeaver Nexus のサービス(MCP アプリとして表示されるお問い合わせフォーム、製品概要、お問い合わせ先情報)を、MCP 対応ホスト(Claude Desktop、claude.ai、ChatGPT、Cursor、MCP Inspector、basic-host)に公開する MCP サーバーです。
本番 URL: https://webweaver-nexus-mcp.vercel.app/mcp — Streamable HTTP、公開、認証なし。
ホスト名のみの場合は、静的な案内ページ(public/index.html)が表示されます。関数へのルーティングは /mcp のみなので、ブラウザーでエンドポイント自体を開くと 406 Not Acceptable が返されます。これは正しいコンテンツネゴシエーションの動作であり、障害ではありません。
公式 MCP レジストリに io.github.webweaver-nexus/webweaver-mcp-server として公開されています。詳しくは「MCP レジストリへの公開」をご覧ください。
ツール
| ツール | 種類 | 説明 |
|---|---|---|
get_contact_form | MCP アプリ(UI) | WebWeaver Nexus のお問い合わせフォームをホスト内にネイティブ表示します |
get_product_overview | 通常のツール | WebWeaver Nexus のサービス内容を説明します |
get_contact_info | 通常のツール | お問い合わせ方法とリンクを返します |
submit_contact_form | アプリ専用 | フォームを送信します。_meta.ui.visibility: ["app"] としてマークされているため、準拠したホストではモデルに表示されません |
フォームはアプリ独自のドキュメント内に表示され、外部へのリクエストは一切行いません。MCP ブリッジ(app.callServerTool)を介して submit_contact_form に送信し、ランディングページの /api/contact にベアラーシークレット付きでサーバー間 POST リクエストを送ります。この方式が、以前の Tally iframe では動作しなかった環境でも機能する理由です。詳しくは「既知の制限事項」をご覧ください。
前提条件
- Node.js 24.x(
package.jsonのenginesを参照) - npm
enginesでは、意図的に 24.x を指定しています。Vercel はこれを基に関数のランタイムを選択します。ローカルでより新しいメジャーバージョンの Node.js を使っても問題ありませんが、npm がEBADENGINE警告を表示します。
インストール
npm install
ビルド
npm run build
これにより、Vite と vite-plugin-singlefile で MCP アプリの UI をバンドルし、クライアントとサーバーの型をチェックして、コンパイル済みのサーバーを生成します。
Vite の処理は最初に実行されます。これは重要です。ビルドプラグインがバンドル済み HTML を generated/mcp-app-html.ts に書き込み、server.ts がそれをインポートします。この処理を実行しないと、サーバーの型チェックは失敗します。generated/ はビルド成果物であり、Git の対象外です。
npm run devの実行中は、ウォッチビルドによってクライアントを変更するたびにこのモジュールが再生成されます。一方、npm run serveは起動時に一度だけ読み込むため、UI の変更を反映するにはサーバーを再起動してください。
実行
# HTTP transport (default, port 3001)
npm run serve
# Custom port
PORT=4000 npm run serve
# stdio transport (for Claude Desktop local config)
npm run serve:stdio
# Dev mode (watch + serve)
npm run dev
デフォルトでは、サーバーは http://localhost:3001/mcp でリッスンします。
設定
環境変数
お問い合わせ API 用の環境変数が 2 つあります。どちらも server.ts でのみ読み込まれます。ローカル開発では .env.example を .env にコピーしてください。
| 変数 | 用途 |
|---|---|
CONTACT_FORM_SHARED_SECRET | ランディングページの /api/contact 用ベアラートークンです。対象プロジェクトの値と一致させる必要があります。 |
CONTACT_API_BASE_URL | フォームの送信先です。デフォルトは https://webweaver-nexus.vercel.app です。末尾のスラッシュは削除されます。 |
シークレットをクライアントバンドルに含めてはいけません。 src/mcp-app.ts は、すべてのユーザーに配信される約 246 KB の単一 HTML 文字列にコンパイルされます。ビルド後は、必ず次のコマンドを実行してください。
grep -c 'CONTACT_FORM_SHARED_SECRET' generated/mcp-app-html.ts # must print 0
API をアプリから直接フェッチするのではなく callServerTool を経由することで、認証情報をサーバー側に保持できます。
開発中は、CONTACT_API_BASE_URL をローカルのランディングページに設定してください。 本番環境に送信すると、テスト送信のたびに本番 Supabase テーブルに実際の行が追加され、実際のメールが 2 通送信されます。このサーバーの Vercel プレビュー デプロイも本番環境を参照するため、使用は必要最小限にとどめ、送信後は行を削除してください(source='mcp' が設定されています)。
npm run serve は作業ディレクトリから .env を読み込みます。stdio エントリポイントは cwd=/ で実行されるため .env を見つけられません。stdio ホストの設定には独自の env ブロックが必要です。詳しくは「Claude Desktop」をご覧ください。
フォームの契約
ランディングページがフォームの契約を所有し、GET /api/contact/schema で公開しています。このリポジトリには、生成されてコミットされたコピーが src/contact-contract.ts にあります。
npm run sync:contract # regenerate from the endpoint
npm run check:contract # fail if the copy has drifted
このファイルを手動で編集しないでください。8 個のゴールの選択肢文字列は、ランディングページのウェルカムメールにある GOAL_PARAGRAPHS のキーでもあるため、1 バイトでもずれると、どこにもエラーが出ないまま、パーソナライズされた段落が表示されなくなります。オフライン環境や新規クローン直後でも npm run build が動作するよう、コピーはビルド時に取得せずコミットされています。check:contract はビルド手順ではなく、リリース手順です。
MCP Inspector でのテスト(デフォルトのハーネス)
MCP Inspector は公式の MCP デバッガーであり、ここでのほとんどすべてのチェックに適したツールです。v2.5.0 で検証済みです。
npx @modelcontextprotocol/inspector
ウェブ UI はポート 6274 で開きます。次のように設定します。
- トランスポートタイプ:
Streamable HTTP - 接続タイプ:
Via ProxyまたはDirect— どちらも動作します。Proxy は JSON-RPC を Inspector のローカルプロキシ(ポート6277)経由でルーティングし、Direct はブラウザーからサーバーへ直接接続します。 - URL:
http://localhost:3001/mcp(ローカル)またはhttps://webweaver-nexus-mcp.vercel.app/mcp(本番環境)
Connect をクリックし、タブを切り替えて確認します。
- Tools —
get_contact_form、get_product_overview、get_contact_info、submit_contact_formがすべて表示されます。最後のツールはアプリ専用としてマークされています。Inspector に表示されるのは、visibilityによるフィルタリングはサーバーではなくホストの役割だからです。通常の 2 つのツールは、呼び出すとテキストを返します。 - Resources —
ui://get-contact-form/mcp-app.htmlが一覧表示されます。読み込むと、約 246 KB のバンドル済み HTML が返され、_meta.ui.prefersBorderが含まれますが、cspキーはまったくありません。 - Apps —
get_contact_formを選択します。フォームは操作可能なコントロールとともにレンダリングされ、ネットワークパネルは空のままです。Inspector はallow-same-originを取り除くため、ここでコントロールが動作することは、以前の Tally の不具合が構造的なサンドボックスの制約ではなく、Tally 独自のストレージアクセスに起因していたことの証明にもなります。
ブラウザーを使わずにデプロイ後のチェックをスクリプト化できる CLI もあります。
npx @modelcontextprotocol/inspector --cli --transport http \
--server-url https://webweaver-nexus-mcp.vercel.app/mcp --method tools/list
# App metadata for a UI tool
npx @modelcontextprotocol/inspector --cli --transport http \
--server-url https://webweaver-nexus-mcp.vercel.app/mcp \
--method tools/call --tool-name get_contact_form --app-info
Inspector でできないことは 1 つだけです。 ui/update-model-context を受け取れません。v2.5.0 の時点では onupdatemodelcontext ハンドラーを登録せず、この機能を宣言もしないため、app.updateModelContext() の通知先がありません。このチェックには basic-host を使ってください。
basic-host でのテスト(モデルコンテキストの更新)
MCP Apps の basic-host ハーネスのベンダーコピーが tools/basic-host/ にあります。updateModelContext 機能を宣言し、📋 モデルコンテキストパネルをレンダリングするローカルホストはこれだけです。このパネルで、フォーム送信後にフォームが実際にホストモデルへ通知することを確認できます。
# once
cd tools/basic-host && npm install && npm run build
# Terminal 1 — the server under test
npm run build && npm run serve
# Terminal 2 — the harness
cd tools/basic-host && SERVERS='["http://localhost:3001/mcp"]' npm run serve
# Open http://localhost:8080
本番環境に対して実行する場合:
cd tools/basic-host && SERVERS='["https://webweaver-nexus-mcp.vercel.app/mcp"]' npm run serve
SERVERS は JSON 配列で、ポート 8080/8081 は実質的に固定されています。由来と注意事項の全リストについては、tools/basic-host/README.md を参照してください。
モデルコンテキストパネルは、最初の更新が届くまで表示されません。
get_contact_formの場合、これは実際に送信することを意味します。送信すると行が書き込まれ、通知メールが送信されます。CONTACT_API_BASE_URLをローカルのランディングページに設定し、使い捨ての情報を入力してください。
使い分け
| 必要なこと | 使用するもの |
|---|---|
| 「サーバーに接続できるか? ツールは一覧表示されるか?」 | Inspector(スクリプトの場合は --cli) |
| 「通常のツールは正しいテキストを返すか?」 | Inspector |
| 「フォームがレンダリングされ、コントロールを操作できるか?」 | Inspector |
| 「ユーザーは実際にフォームへ入力して送信できるか?」 | basic-host、または Claude Desktop — どちらも serverTools を宣言しています |
「app.updateModelContext() はホストに届くか?」 | basic-host — Inspector では確認できません |
| 「顧客が実際に使うホストで動作するか?」 | ローカルの stdio 設定を使った Claude Desktop |
| デプロイ後に最も手早く健全性を確認する | Inspector --cli |
検証済みのホスト対応状況
リリースごとに再確認してください。以下は確認時点の状況であり、保証ではありません。
| ホスト | フォームの表示 | コントロールの動作 | serverTools(送信) | visibility: ["app"] の尊重 |
|---|---|---|---|---|
| Claude Desktop | ✅ | ✅ | ✅ | ✅ |
tools/basic-host | ✅ | ✅ | ✅ | n/a(すべてのツールを表示) |
| MCP Inspector | ✅ | ✅ | ✅ | ❌ 表示される |
| claude.ai コネクター | ✅ | ✅ | ✅ | ✅ |
Claude DesktopとInspectorは2026年10月5日に検証し、10月6日にv2.1.0で再検証しました。claude.aiは10月6日にcloudflaredトンネル経由で検証しました。また、Inspectorはui/update-model-contextを受信できません。
claude.ai向けにローカル開発環境を公開する(Cloudflareトンネル)
claude.aiのカスタムコネクターUI(公開HTTPS URLのみを受け付け、localhostは受け付けません)に対してサーバーをローカルで反復開発するには、Cloudflareトンネル経由で開発サーバーを公開します。
# Terminal 1 — run the server
npm run build && npm run serve
# Terminal 2 — start the tunnel
npx cloudflared tunnel --url http://localhost:3001
トンネルの出力からhttps://*.trycloudflare.comのURLをコピーします。Claudeの設定で、https://<tunnel-url>/mcpを指すカスタムMCPコネクターを追加します。
*.trycloudflare.comはapi/mcp.tsのHostヘッダーチェックで許可リストに登録されています。cloudflaredはlocalhostではなく公開ホスト名を転送するため、この登録がないとトンネル経由のリクエストはすべて403 Invalid Hostで拒否されます。
注:
get_contact_formはCSPを一切宣言しないため、以前のTally埋め込みをブロックしたframeDomainsのバグは該当しなくなりました。代わりに確認すべき点は、ホストがserverTools機能を宣言しているかどうかです。宣言していない場合、Appには送信ボタンではなく「ウェブで続行」のフォールバックが表示されます。既知の制限事項を参照してください。
デプロイ(Vercel)
MCPサーバーは、Express + Streamable HTTPパターンを使用したVercelサーバーレス関数としてデプロイされています。
仕組み:
vercel.jsonがトラフィックをapi/mcp.tsにルーティングします。api/mcp.tsはExpressアプリをdefaultとしてエクスポートします。- ViteでバンドルされたMCP AppのUIは文字列定数(
generated/mcp-app-html.ts)として関数にコンパイルされるため、実行時にディスクからファイルを読み込む必要はありません。 - 関数は
maxDuration: 60でVercelのNode.js 24ランタイム上で実行されます(必要時間を十分に上回っています。ツール呼び出しは数ミリ秒で返ります)。 - 環境変数は不要です。すべての設定はソースにハードコードされています。
再デプロイするには: mainブランチにプッシュします。Vercelはプッシュ時に自動デプロイします。
デプロイを確認するには:
# Sanity check — should return a JSON-RPC tools list (~880 bytes)
curl -X POST https://webweaver-nexus-mcp.vercel.app/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Resource read — should return ~246 KB of bundled HTML.
# A response under a few KB means the unbundled source HTML was inlined
# instead of the bundle (the build guards against this — see git history).
curl -X POST https://webweaver-nexus-mcp.vercel.app/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"resources/read","params":{"uri":"ui://get-contact-form/mcp-app.html"},"id":1}' \
| wc -c
対話的に確認する場合は、デプロイ済みのURLに対してMCP Inspectorを実行します(上記の「MCP Inspectorでのテスト」を参照)。basic-hostを起動する前に行う、デプロイ後の最初の確認として推奨します。
MCPレジストリへの公開
レジストリエントリーはio.github.webweaver-nexus/webweaver-mcp-serverで、server.jsonに定義されています。公開は手動ディスパッチのみです。ActionsタブからPublish to MCP Registryワークフローを実行してください。
# validate locally before dispatching
mcp-publisher validate
ノートPCからではなくCIで公開する理由。 mcp-publisher login githubは、GitHubがGET /user/memberships/orgs?state=active経由で、ユーザーを組織のOwnerとして報告した場合にのみ組織の名前空間を付与します(レジストリがrole == "admin"を要求するためです。上流のinternal/api/handlers/v0/auth/github_at.goを参照)。このアカウントは公開メンバーシップを持つOwnerであり、組織のサードパーティポリシーも原因ではありませんが、この組織についてその照会は何も返しません。そのため、個人アカウントでログインするとio.github.<user>/*しか付与されず、公開は403で失敗します。
GitHub ActionsのOIDCを使えばこの問題を回避できます。レジストリはrepository_ownerクレームから名前空間を導出します。このリポジトリではwebweaver-nexusであるため、io.github.webweaver-nexus/*が直接付与されます。ワークフローにはid-token: writeが必要です。
ディスパッチする前に、server.jsonのversionを更新してください。レジストリは既存バージョンの再公開を拒否します。
MCP クライアントへのインストール
すべてのクライアントは同じエンドポイントに接続します。
https://webweaver-nexus-mcp.vercel.app/mcp
トランスポートは Streamable HTTP です。認証は不要です。submit_contact_form を含む、登録済みのすべてのツールを誰でも呼び出せます。詳しくは、visibility is a hint, not an access control をご覧ください。
Streamable HTTP にネイティブ対応するホスト(claude.ai、ChatGPT、MCP Inspector、basic-host)では、URLをそのまま指定します。stdio のみ対応するホスト(現時点での Claude Desktop、Cursor)では、mcp-remote npm シムを使用します。これはローカルの stdio プロセスとして起動し、JSON-RPC をリモート URL に中継する小さなパッケージです。
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または %APPDATA%\Claude\claude_desktop_config.json(Windows)を編集します。
{
"mcpServers": {
"webweaver-nexus": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://webweaver-nexus-mcp.vercel.app/mcp"]
}
}
}
Claude Desktop を再起動すると、モデル向けの3つのツールがツールメニューに表示されます。visibility: ["app"] が適用されるため、submit_contact_form は表示されません。
未リリースの変更をテストする場合など、このサーバーをローカルの stdio プロセスとして実行するには、ビルド済みのエントリーポイントを設定で指定し、環境変数も引き継いでください。stdio ホストはプロセスを cwd=/ で起動するため、.env が見つからないからです。
{
"mcpServers": {
"webweaver-nexus-local": {
"command": "node",
"args": ["/absolute/path/to/webweaver-mcp-server/dist/main.js", "--stdio"],
"env": {
"CONTACT_API_BASE_URL": "http://localhost:3000",
"CONTACT_FORM_SHARED_SECRET": "local-dev-secret"
}
}
}
}
上記の
mcp-remote設定は、デプロイ済みサーバーに HTTP で中継するため、v1.0.1 で修正された stdio のcwdバグは発生しません。このバグは、この例のようなローカル stdio 設定にのみ該当します。
claude.ai(カスタムコネクター)
claude.ai は Streamable HTTP にネイティブ対応しているため、シムは不要です。
- 設定 → コネクター(または、段階的な提供状況に応じて 機能プレビュー → MCP)を開きます。
- カスタムコネクターを追加を選択し、
https://webweaver-nexus-mcp.vercel.app/mcpを貼り付けます。 - 保存します。
モデル向けの3つのツールはすべて動作します。get_contact_form で送信できるかどうかは、claude.ai が serverTools 機能を宣言しているかによります。詳しくは既知の制限をご覧ください。
Cursor
~/.cursor/mcp.json を編集するか、Cursor の UI で 設定 → MCP サーバーを開きます。
{
"mcpServers": {
"webweaver-nexus": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://webweaver-nexus-mcp.vercel.app/mcp"]
}
}
}
Cursor のウィンドウを再読み込みします(Cmd+Shift+P → Reload Window)。
ChatGPT
MCP/カスタムコネクターに対応するプランで利用できます。
- 設定 → コネクター(または アプリ)→ カスタム MCP サーバーを追加を選択します。
https://webweaver-nexus-mcp.vercel.app/mcpを貼り付けます。- 会話で有効にします。
接続の確認(すべてのクライアント)
ホストに、次のように尋ねます。
WebWeaver Nexus コネクターを使って、製品の概要を取得してください。
ツールが実行されてテキストが返ってくれば、接続は正常です。get_contact_form については、MCP Apps のレンダリングに対応したホストでは、フォームが会話内に直接表示されます。クライアントごとの注意事項は上記をご覧ください。
既知の制限
Claude Desktop の stdio 連携 — v1.0.1 で修正済み
以前の問題: join_waitlist(現在の get_contact_form)は、Claude Desktop で ENOENT: no such file or directory, open '/dist/mcp-app.html' のエラーにより失敗していました。Claude Desktop は macOS で MCP サーバープロセスを、作業ディレクトリを / に設定して起動します。また、server.ts は path.join(process.cwd(), "dist", "mcp-app.html") を使ってバンドル済み HTML を特定していました。回避策として claude_desktop_config.json の cwd フィールドを試しましたが、検証したバージョンでは無視され、何も通知されませんでした。
修正: Vite のビルドで、バンドル済み HTML を generated/mcp-app-html.ts に出力するようになりました。server.ts はこのファイルを文字列定数としてインポートします。実行時にファイルシステムのパスを解決しないため、App リソースは stdio、ローカル HTTP、Vercel のいずれでも同じように動作します。サーバーを / から実行し、stdio 経由でリソースを読み取って検証済みです。
参考のため、以前の2つの試みも履歴に残しています。パスの調査(e73484f で追加、32dc1aa で取り消し)では Vercel 上でバンドルされていないソースファイルが解決され、Vite 8 から 7 へのダウングレード(2e33c8e、c8ad36d で取り消し)では原因を誤って追っていました。現在のビルドでは、バンドルされていないソース HTML が誤ってインライン化された場合、明確にエラーとなります。
MCP Inspector は allow-same-origin を取り除きます — フォームは壊れなくなりました
Inspector のサンドボックスプロキシのソースにも記載されています。App の iframe は allow-same-origin なしでサンドボックス化されます。この属性は「サーバーから提供された値から常に取り除かれます」。そのため、App は不透明な(null)オリジンで実行されます。
以前はこれによってフォームが動作しなくなっていました。以前の Tally iframe は不透明なオリジンを継承し(サンドボックスフラグはネストされたフレームにも継承されます)、その JavaScript は同一オリジンのストレージにアクセスできず、ドロップダウンとチェックボックスは機能しなくなりましたが、通常のテキスト入力は引き続き機能していました。当時、このフラグだけを変更した A/B テストで確認済みです。
ネイティブフォームには影響しません。ネイティブコントロールはストレージを必要としないためです。ただし、制約がひとつ残っており、遵守する必要があります。不透明なオリジンでは localStorage、sessionStorage、document.cookie はすべて SecurityError をスローします。そのため、src/mcp-app.ts の RENDERED_AT はモジュールスコープ定数になっており、この状態を維持する必要があります。
_meta.ui.domain を使ってサンドボックスを「修正」しないでください。 Inspector はこのフィールドを宣言した App に allow-same-origin を付与しますが、仕様ではその形式はホストに依存すると明記されています({hash}.claudemcpcontent.com、www-example-com.oaiusercontent.com)。値は各ホストによって割り当てられるため、ひとつの値を推測して指定すると Inspector では機能しても、ほかのホストを壊すおそれがあります。ここでは必要ありません。
frameDomains を無視するホスト — 依存を削除し、v2.0.0 で解決済み
frameDomains を無視するホスト影響を受けていたのは claude.ai のカスタムコネクターと Claude Desktop です。 どちらも App のシェルは正しく表示しましたが、埋め込まれた Tally フォームは空白のままでした。ホストが _meta.ui.csp で宣言された frameDomains を無視するため、ネストされた tally.so iframe がブロックされていました。上流の課題 anthropics/claude-ai-mcp#40 は、現在も未解決です。
現在は該当しない理由。 フォームは App 自身のドキュメント内にネイティブに表示されるようになり、csp も宣言しなくなりました。依存していないポリシーは無視されようがありません。今後の作業に関わる制約として、ここに記載を残します。MCP App にサードパーティーの iframe を決して配置しないでください。独自フォームを iframe にしても同じ問題が発生します。問題は Tally 固有のものではなく、ネストされたフレームという構造にあるためです。
Claude Desktop も同じスタックを使用しており、その initialize は clientInfo.name: "claude-ai (via mcp-remote …)" を送信するため、同じ問題が発生します。MCP Apps のサポート(extensions["io.modelcontextprotocol/ui"] に text/html;profile=mcp-app)を通知しており、MCP ログでは resources/read と tools/call の両方が正常に応答していることが確認できます。このため、プロトコルの問題ではなく、レンダリングの問題として現れます。
Claude Desktop の DevTools コンソールで確認済み:
Framing 'https://tally.so/' violates the following Content Security Policy
directive: "frame-src 'self' blob: data:". The request has been blocked.
取り除かれているのは frameDomains だけで、ほかの 2 つの宣言は維持されています。App フレームは、それらをクエリ文字列に含めて読み込まれます。
mcp_apps?connect-src=https%3A%2F%2Ftally.so&resource-src=https%3A%2F%2Ftally.so+https%3A%2F%2Fassets.claude.ai
frame-src パラメーターがなかったため、ポリシーは frame-src 'self' blob: data: にフォールバックしました。Tally のローダーは実行され、App は正しい埋め込み URL をリクエストしていました。フレームをブロックしたのはホストであり、その上流の処理ではありません。レンダラー側のブロックとして予想されるとおり、HTTP 上の mcp-remote とローカルの stdio 設定の両方で同じでした。
2026 年 10 月 5 日、Claude Desktop で修正を確認済みです。 同じ App フレーム URL は、現在次のようになっています。
mcp_apps?stable-origin=true&resource-src=https%3A%2F%2Fassets.claude.ai&dev=true
tally.so は resource-src からなくなり、App は connect-src を要求しないため、そのパラメーターもありません。DevTools コンソールではセッション全体を通じて CSP 違反はありませんでした。ポリシーに関する行は、Claude Desktop 自身の Unrecognized Content-Security-Policy directive 'webrtc' だけでした。フォームは操作可能なコントロールとともに表示され、送信内容は source='mcp' として Supabase に到達しました。
新しい stable-origin=true パラメーターに注意してください。Claude Desktop は App フレームに安定したオリジンを割り当てるようになりました。これは _meta.ui.domain の仕組みの背後にあるものです。これに allow-same-origin が伴う場合、このホスト上の Apps では、将来的にブラウザーのストレージを使用できる可能性があります。ただし、これには依存しないでください。MCP Inspector は引き続きこのフラグを取り除くため、以下の不透明なオリジンのルールは依然として適用されます。
/mcp への GET と DELETE が 405 を返す
v2.1.0 以降、このエンドポイントは createMcpHandler を介して、2025 プロトコル時代とリビジョン
2026-07-28 の両方に 1 つの URL で対応します。レガシートラフィックはステートレスに処理されます。リクエストごとに新しいサーバーインスタンスが作成されるため、2025 年のセッション操作の対象となるセッションが存在せず、GET と DELETE には 405 Method not allowed が返されます。以前は 200 が返されていました。
これは障害ではなく、SDK で文書化されているステートレスの慣用的な使い方です。実際の v1.30.0 SDK クライアント、tools/basic-host、Claude Desktop
または claude.ai には影響しないことを確認済みです。いずれもリクエスト/レスポンス処理にそのストリームを必要としません。両方の Claude ホストでフォームが表示され、送信されて Supabase に到達しました。どちらのコンソールにも 405 は表示されませんでした。POST に変更はありません。
クライアントはプローブを行わずに 2025 のハンドシェイクをデフォルトで使用するため、クライアント側で変更する必要はありません。versionNegotiation: { mode: 'auto' } を指定してオプトインするクライアントは、代わりに 2026-07-28 をネゴシエートします。どちらの場合も、同じ URL から同じツールを取得できます。
serverTools 機能に対応していないホスト
app.callServerTool() を使うには、ホストが serverTools を宣言している必要があります。宣言がないと、App から送信できません。Claude Desktop はこれを宣言しています。2026 年 10 月 5 日に検証し、送信がエンドツーエンドで完了しました。tools/basic-host も同様です。claude.ai やその他のホストではまだ未検証であり、ホストがこの機能を取り下げる可能性もあるため、ガードは維持します。
src/mcp-app.ts は送信時ではなく読み込み時に機能を確認します。機能がない場合はフォームを、「ウェブで続行」するためのリンクに置き換えます。このリンクは app.openLink() を介して /#contact を開きます(app.openLink() 自体も openLinks が必要です。こちらも使えない場合は、選択可能なテキストとして URL を表示します)。この順序には意図があります。送信ボタンを押しても何も起きないフォームは、正常に動作したように見えてしまうため、フォームがないよりも悪いからです。
検証済みのホストではこの機能が利用できるため、フォールバックは、実際のホストではまだ使われていない予備策です。 機能をローカルでスタブ化して確認しただけです。この処理に手を加える場合は、再確認してください。
visibility: ["app"] はヒントであり、アクセス制御ではない
submit_contact_form は _meta.ui.visibility: ["app"] を宣言しており、Claude Desktop はこれを尊重します。ツールの一覧表示を依頼したところ、モデルは App 内から送信ツールを呼び出した直後にもかかわらず、get_contact_form、get_contact_info、get_product_overview だけを挙げました(2026 年 10 月 5 日に検証)。
これはホスト側の配慮であり、強制力のある制御ではありません。tools/list はすべてのクライアントにこのツールを返します。サーバー側ではフィルタリングを行わず、仕様ではホスト側の役割とされています。このフラグを無視するホストでは、そのモデルにツールが公開されます。これは危険ではなく、許容できる範囲です。consentPrivacy は文字列リテラルの true である必要があり、ランディングページはこの経路での送信をすべて source='mcp' として記録するため、合成された内容を識別できます。このフラグをセキュリティ境界として扱わないでください。
プロジェクト構成
├── api/ │ └── mcp.ts # Vercel サーバーレスエントリーポイント — Express アプリをエクスポートします ├── public/ │ └── index.html # / で提供される静的ランディングページ(MCP サーバーの一部ではありません) ├── main.ts # ローカル開発用エントリーポイント — HTTP および stdio トランスポート ├── server.ts # ツールとリソースの登録(共通) ├── mcp-app.html # アプリの UI テンプレート(Vite エントリー、ソース) ├── src/ │ ├── mcp-app.ts # クライアント側のアプリのライフサイクルとネイティブの問い合わせフォーム │ ├── contact-contract.ts # 生成済み — ランディングページから取り込んだフォーム契約 │ ├── submit-outcome.ts # server.ts とアプリで共有する結果の型 │ ├── mcp-app.css # アプリ固有のスタイル │ └── global.css # ホスト変数のフォールバックとリセット ├── scripts/ │ └── contract.ts # 取り込んだ契約の同期/チェック ├── tools/ │ └── basic-host/ # 取り込み済みの MCP ホストハーネス(モデルコンテキストの更新) ├── generated/ # ビルド成果物 — TS 定数としてバンドルされた HTML(Git 管理対象外) ├── dist/ # ビルド出力 — 関数コードとバンドルされた mcp-app.html ├── vite.config.ts # Vite と singlefile プラグインの設定 ├── tsconfig.json # クライアントと共通の型チェック ├── tsconfig.server.json # サーバーの宣言ファイル出力 ├── tsconfig.scripts.json # scripts/ の型チェック(noEmit) ├── .env.example # CONTACT_API_BASE_URL、CONTACT_FORM_SHARED_SECRET ├── vercel.json # Vercel デプロイ設定 └── package.json
バージョン履歴
- v2.1.0 — モノリシックな
@modelcontextprotocol/sdkから、v2 の分割パッケージ(server/node/express/client/core。すべてバージョンを厳密に固定)およびext-apps1.7.5 → 2.0.3 に移行しました。エンドポイントはcreateMcpHandlerを介して、1 つの URL でプロトコルリビジョン 2026-07-28 と 2025 era の両方を提供するようになりました。クライアントはプローブなしで 2025 のハンドシェイクをデフォルトで使用するため、クライアント側で変更する必要はありません。/mcpへのGETとDELETEは405を返すようになりました。詳細は既知の制限をご覧ください。バンドルされたアプリは、より軽量な ext-apps のみを理由として 447 KB から 246 KB に縮小しました。tools/basic-hostは、クロスバージョン互換性テストとして意図的に SDK v1 のままにしており、テストに合格しています。ツール、スキーマ、動作に変更はありません。 - v2.0.0 — 破壊的変更:
join_waitlistはget_contact_formに改名され、リソース URI はui://get-contact-form/mcp-app.htmlになりました。エイリアスはないため、キャッシュされたクライアントは再度一覧を取得するまでエラーになります。Tally iframe はアプリ内でネイティブに描画されるフォームに置き換えられました。外部リクエストを一切行わないため、_meta.ui.cspは宣言されません。これはframeDomainsを無視するホストに対する構造的な修正です。送信には、新しいアプリ専用のsubmit_contact_formツール(_meta.ui.visibility: ["app"])を使用します。このツールはサーバー間でランディングページの/api/contactに POST するため、共有シークレットがクライアントバンドルに含まれることはありません。リポジトリ初の環境変数を 2 つ追加し、npm run check:contractで検証するベンダー提供のフォーム契約も追加しました。また、オリジンチェックがなく、Tally の送信を主張する任意のフレームを信頼していたpostMessageリスナーを削除しました。 - v1.0.2 — すべてのホストで
join_waitlistが空のパネルを表示する問題を修正しました。アプリは Tally のembed.jsを読み込んでいましたが、Tally.loadEmbeds()を呼び出していなかったため、フォーム iframe にsrcが設定されていませんでした。また、ホストの機能に応じてupdateModelContextを呼び出すようにし、basic-host ハーネスをtools/basic-host/にベンダーとして含めることで、ext-appsのクローンが不要になりました。 - v1.0.1 —
join_waitlistが stdio 経由(Claude Desktop)で動作するようになりました。アプリの HTML はディスクから読み込むのではなくサーバーにコンパイルされ、process.cwd()への依存がなくなりました。レート制限をクライアントごとに適用するようになりました。trust proxyが未設定だったため、Vercel のプロキシの背後ではすべての呼び出し元が 60 回/分の単一のバケットを共有していました。ホストの検証で Vercel のプレビュー環境と*.trycloudflare.comが受け入れられるようになりました。これらは、ドキュメントに記載されたトンネルのワークフローで必要です。すべてのnpm auditの警告を解消するため、依存関係を更新しました(MCP SDK 1.29 → 1.30)。 - v1.0.0 — 公式 MCP レジストリに
io.github.webweaver-nexus/webweaver-mcp-serverとして公開しました。CORS の許可リストとレート制限により、エンドポイントを強化しました。 - v0.1.0 — 初の本番環境へのデプロイが稼働しました。3 つのツールを公開し、
join_waitlistは Streamable HTTP ホストで動作します。Claude Desktop の stdio 統合は延期されました。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。