
WebアプリをAIから操作できるようにした話(第4回)— Claude Codeから繋げるときにハマった2つの落とし穴
このシリーズ: 全4回
やりたかったこと
第2・3回で実装した MCP サーバーを Claude Code から使える状態 にする。
具体的には:
- Claude Code のセッション起動時に
team-task-schedulerのツール43個が自動ロードされる /task-flow 今週のタスクを教えてのようなコマンドで自然言語操作できる
こんな人向け
- MCPサーバーの実装は完成したのに Claude Code でツールが認識されない
~/.claude/settings.jsonにmcpServersを書いたが動かない- Claude Code の
/mcpで接続失敗になっている
❌ 落とし穴①:GETエンドポイント未実装で接続自体が失敗する
何が起きたか
MCPサーバーの curl テストは全て成功していました:
# initialize → 200 OK
curl -X POST https://your-app.cloudfront.net/api/mcp \
-H "Authorization: Bearer <token>" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}'
# → {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",...}}
# tools/list → 43ツールが返る
curl -X POST https://your-app.cloudfront.net/api/mcp \
-H "Authorization: Bearer <token>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# → {"result":{"tools":[...43件...]}}しかし Claude Code を再起動しても /mcp でツールが認識されず、ToolSearch でも team-task-scheduler のツールが見つかりませんでした。
なぜこうなるのか
Claude Code の MCP HTTP クライアントは セッション開始時に GET /api/mcp リクエストを送って SSE チャネルを確立しようとします。
当初の実装では POST と OPTIONS のみを実装していたため、GET に対して 405 Method Not Allowed が返っていました。
curl -X GET https://your-app.cloudfront.net/api/mcp \
-H "Authorization: Bearer <token>" \
-H "Accept: text/event-stream"
# → HTTP 405 Method Not Allowed ← これが原因Claude Code は GET が失敗した時点でそのMCPサーバーを「接続不可」と判断し、POST による tool 呼び出しも試みません。
✅ 解決した実装
GET ハンドラーを追加し、空の SSE ストリームを返すようにしました:
// src/app/api/mcp/route.ts に追加
export async function GET(request: NextRequest): Promise<NextResponse> {
const auth = await authenticateMcpRequest(request.headers.get('authorization'));
if (!auth) return unauthorized();
// Lambda はステートレスなので長時間接続できない
// 即座に閉じることで「SSEは使えないが接続は確立できた」と伝える
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(': connected\n\n'));
controller.close();
},
});
return new NextResponse(stream, {
status: 200,
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
},
});
}OPTIONS も GET を含めるよう更新:
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', // GET を追加
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}なぜこれで解決するのか
Claude Code は GET で 200 が返ると「このサーバーはSSEをサポートしている(が即座に閉じた)」と判断します。ストリームが即座に閉じられても、以降の POST リクエストは問題なく動作します。
❌ 落とし穴②:settings.json の mcpServers は Claude Code が無視する
何が起きたか
落とし穴①を修正してデプロイし、Claude Code を再起動しました。しかし 依然としてツールが認識されません。
確認のため ToolSearch を実行すると:
No matching deferred tools found接続確認のために claude mcp list を実行すると:
plugin:context7:context7: npx -y @upstash/context7-mcp - ✓ Connected
playwright: npx @playwright/mcp@latest - ✓ Connected
# ← team-task-scheduler がリストにない!team-task-scheduler が存在すら認識されていませんでした。
なぜこうなるのか
設定を書いたファイルは ~/.claude/settings.json でした:
// ~/.claude/settings.json
{
"mcpServers": {
"team-task-scheduler": {
"type": "http",
"url": "https://your-app.cloudfront.net/api/mcp",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}しかし Claude Code が MCP サーバー設定を読み込むのは ~/.claude.json(ホームディレクトリ直下の別ファイル) です。
~/.claude/settings.json は Claude Code の動作設定(言語、権限、モデル設定など)を書くファイルで、MCP サーバーの登録には使われません。
この2つのファイルを混同するのが「あるある」のミスです:
| ファイル | 用途 |
|---|---|
~/.claude/settings.json |
Claude Code の動作設定(言語、権限、環境変数など) |
~/.claude.json |
MCPサーバーの登録、プロジェクトごとの設定 |
✅ 解決した方法
claude mcp add コマンドで正しいファイルに登録します:
# ユーザースコープで登録(全プロジェクトで使えるようになる)
claude mcp add \
--transport http \
--scope user \
team-task-scheduler "https://your-app.cloudfront.net/api/mcp" \
--header "Authorization: Bearer <your-token>"実行後の確認:
claude mcp list
# → team-task-scheduler: https://... (HTTP) - ✓ Connectedコマンドの引数順序に注意が必要です。--header は URL の後に指定します:
# ❌ エラーになる(--header が name より前)
claude mcp add --transport http --scope user --header "..." team-task-scheduler "https://..."
# ✅ 正しい
claude mcp add --transport http --scope user team-task-scheduler "https://..." --header "..."スコープの選択
| スコープ | コマンド | 保存先 | 用途 |
|---|---|---|---|
| user(全プロジェクト共通) | --scope user |
~/.claude.json |
個人の共通ツール(今回はこれ) |
| local(プロジェクト固有) | (デフォルト) | ~/.claude.json(プロジェクトパス配下) |
プロジェクト専用ツール |
| project(チーム共有) | --scope project |
.mcp.json |
チームで共有するツール |
比較まとめ
| ❌ 問題 | ✅ 解決後 | |
|---|---|---|
| GETエンドポイント | 実装なし → 405 → 接続失敗 | 空SSEストリームを返す → 200 |
| 設定ファイル | ~/.claude/settings.json の mcpServers(無視される) |
claude mcp add --scope user → ~/.claude.json に書き込まれる |
| 接続状態 | /mcp でツールが認識されない |
43ツールがロードされる |
接続できたら:/task-flow カスタムコマンドを作る
ツールが使えるようになったら、自然言語で操作するカスタムコマンドを作るとさらに便利です。
~/.claude/commands/task-flow.md を作成:
Team Task Scheduler の MCP ツールを使って、以下のリクエストを処理してください:
$ARGUMENTS
## 使えるMCPツール
- **チーム・ユーザー**: `team_list`, `team_get`, `user_list`
- **プロジェクト**: `project_list`, `project_get`, `project_create`, `project_update`, `project_delete`
- **タスク**: `task_list`, `task_get`, `task_create`, `task_update`, `task_delete`
(省略)
## ガイドライン
- チームIDが不明な場合は team_list → project_list の順で確認する
- 複数の操作が必要な場合は順番に実行し、各ステップを簡潔に報告するこれで Claude Code から以下のように使えます:
/task-flow 私が担当しているアクティブなプロジェクトを教えて
/task-flow トレサビリティシステムの未完了タスクを一覧で表示して
/task-flow プロジェクトAの進捗を70%に更新してClaude Code は $ARGUMENTS の内容に応じて、適切な MCP ツールを自動的に選択して実行してくれます。
まとめ
落とし穴①: GET エンドポイント未実装
- Claude Code はセッション開始時に GET でSSE接続を試みる
- 405 が返ると「接続不可」と判断しツールが一切ロードされない
- Lambda 環境では「即座に閉じる空SSEストリーム」で対処
落とし穴②: 設定ファイルの間違い
~/.claude/settings.jsonのmcpServersは Claude Code が読み込まないclaude mcp add --scope userを使うと正しく~/.claude.jsonに登録される
この2点を押さえておけば、他のアプリを MCP サーバーとして公開する際にも同じ問題を踏まずに済みます。
バイブコーディングで実装する
作成済みの MCP HTTP サーバーを Claude Code に接続する手順を実施してください。
【確認手順】
1. curl で GET エンドポイントをテスト:
curl -X GET <MCP_URL> -H "Authorization: Bearer <TOKEN>" -H "Accept: text/event-stream"
→ 200 が返らなければ GET ハンドラーを実装する
2. GET ハンドラーの実装(Lambda/ステートレス環境の場合):
Content-Type: text/event-stream で ': connected\n\n' を送信して即閉じる
OPTIONS の Allow-Methods に GET を追加する
3. Claude Code への登録:
claude mcp add --transport http --scope user <name> <url> --header "Authorization: Bearer <token>"
※ --header は URL の後に指定する
※ --scope user で全プロジェクトで使えるようになる
4. 接続確認:
claude mcp list
→ <name>: <url> (HTTP) - ✓ Connected が表示されれば成功AIに指示するときのポイント
- GET エンドポイントを実装するよう明示する: 指示しないと POST のみの実装になり接続できなくなる。「Claude Code は GET リクエストで SSE 接続を確立しようとする」と説明するとAIが理解しやすい
- 設定ファイルのパスを明示する:
~/.claude/settings.jsonのmcpServersは機能しないことをプロンプトに書く。「claude mcp addコマンドを使って~/.claude.jsonに登録する」と明示する - スコープを指定する:
--scope userを省略するとプロジェクトローカルになり、他のプロジェクトで使えなくなる
これで「WebアプリをMCPサーバーにしてAIから操作できるようにした話」シリーズは完結です。第1回から順に読むことで、設計から実装・接続まで一貫した理解が得られます。