同一のツール定義(EventTool[])を stdio(ローカル)と HTTP(URL 経由リモート)の両方で公開する。 URL・公開ツール・外部クライアントは admin 設定画面から編集する。秘密トークンは env に置く。
> 注: starter テンプレートは MCP を配線済みで出荷している(route / lib/mcp / 設定画面 / > registry / content/mcp-settings.json / scripts/mcp.mjs)。create-event-app で生成した app では > 実際に必要な作業は「event.config.ts で mcp: true」と「env に MCP_BEARER_TOKEN(HTTP を使うとき)」の2つだけ。 > 下の 2〜5 は「既に存在するか確認」の節。ゼロから配線する場合のリファレンスとして完全形を載せる。
1. 依存と機能
package.json に "@event/mcp": "*"。next.config.ts の transpilePackages に追加。 event.config.ts で mcp: true(descriptor は starter に完成形あり。nav /admin/settings/mcp + env MCP_BEARER_TOKEN)。
2. 設定を CMS registry に載せる(設定画面)
src/lib/cms.ts の registry に MCP 設定 schema を足す(GenericForm がそのまま設定画面になる):
import { mcpSettingsResourceSchema } from "@event/mcp/ui";
// createResourceRegistry([...既存, { ...mcpSettingsResourceSchema(), hidden: true }])
content/mcp-settings.json を置く:
{ "serverEnabled": true, "httpEnabled": false, "publicUrl": "", "actorName": "claude",
"tools": { "db": false, "content": true, "fourS": true }, "stdioScript": "scripts/mcp.mjs", "clients": [] }
3. ツール合成
src/lib/mcp.ts:
import "server-only";
import { createFoursConfig } from "@event/fours-sdk";
import { contentToolGroup, foursToolGroup, resolveMcpSettings, generateConnectionConfig, type EventTool, type McpSettings } from "@event/mcp";
import { eventConfig } from "@/event.config";
import { reader, registry, store } from "@/lib/cms";
export async function loadMcpSettings(): Promise<McpSettings> {
return resolveMcpSettings(await reader.readContent("mcp-settings"));
}
export async function buildMcpTools(s: McpSettings): Promise<EventTool[]> {
const tools: EventTool[] = [];
if (s.tools.content) tools.push(...contentToolGroup({ registry, reader, store }));
if (s.tools.fourS) tools.push(...foursToolGroup({ config: createFoursConfig() }));
return tools; // db ツールは recipe add-database で dbToolGroup を足す
}
export const connectionConfigFor = (s: McpSettings) => generateConnectionConfig(eventConfig.name, s);
4. HTTP エンドポイント(URL 経由)
src/app/api/mcp/route.ts:
import { createMcpHttpHandler } from "@event/mcp";
import { features } from "@/event.config";
import { buildMcpTools, loadMcpSettings } from "@/lib/mcp";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export async function POST(req: Request) {
if (!features.isEnabled("mcp")) return Response.json({ error: "disabled" }, { status: 404 });
const settings = await loadMcpSettings();
if (!settings.serverEnabled || !settings.httpEnabled) return Response.json({ error: "MCP HTTP disabled" }, { status: 503 });
return createMcpHttpHandler({ name: "<app>", tools: await buildMcpTools(settings) })(req);
}
env に MCP_BEARER_TOKEN=<強いトークン>。認証は Authorization: Bearer。
5. 設定画面 + 接続パネル
src/app/admin/settings/mcp/page.tsx(server)— auth/feature gate → <GenericForm schema={mcpSettingsResourceSchema()}>(save は saveSingletonAction("mcp-settings"))+ <McpConnectionPanel config={connectionConfigFor(settings)} />。 starter に完成形があるので apps/starter/src/app/admin/settings/mcp/** をコピーが最短。
6. 接続と検証
管理画面 /admin/settings/mcp で「HTTP を有効化」+ 公開 URL を入れると、下の接続パネルに .mcp.json が出る。 headless(CI・スクリプト)で確認するなら content/mcp-settings.json を直接 "httpEnabled": true にし、 env に MCP_BEARER_TOKEN=<token> を入れてから叩く。
認証の戻り値: MCP_BEARER_TOKEN 未設定なら 503(公開しない)、設定済みで Bearer 不一致/欠落なら 401。 つまり下の「401」テストは token を env に入れてある前提。
# 401(token 設定済み・認証なしで叩く)
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:<port>/api/mcp \
-H "content-type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# tools/list(認証あり)→ content_list_resources / content_get / content_put
curl -s -X POST http://localhost:<port>/api/mcp \
-H "content-type: application/json" -H "authorization: Bearer <token>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Claude 側は接続パネルの .mcp.json を貼るだけ(stdio ならローカル、HTTP ならリモートコネクタ)。
ツール群
- content: content_list_resources / content_get / content_put(CMS 読み書き・locked ガード)
- fourS: fours_health / fours_sessions / fours_speakers(read-only)
- db: db_schema_describe / db_schema_addField(カラム追加)/ db_entity_* / db_audit_query / db_migration_propose(recipe add-database で有効化)