/sse の罠:壊れて見えるが壊れていない 387 台
MCP には HTTP のトランスポートが二種類あります。間違ったほうを使うと、サーバーは 200 を返しながら何も教えてくれません。私たちは 387 台に対してまさにそれをしていました。
MCP は二通りの方法で HTTP を話し、両者は入れ替えられません。Streamable HTTP は JSON-RPC を単一のエンドポイントへ直接送ります。より古い HTTP+SSE トランスポートはまったく別で、まずストリームを開き、どこへ送るかをサーバーが教えてくれます。
失敗したときの見え方
HTTP+SSE のエンドポイントに initialize リクエストを送ると、エラーは返りません。返ってくるのはこれです。
HTTP 200
content-type: text/event-stream
event: endpoint
data: https://example.com/my-mcp/api/process200 OK。エラーコードも苦情もありません。あるのは「本当のエンドポイントはあちらです」という丁寧な案内だけ。それを予期していないクライアントは、空のレスポンスとして読んでしまいます。
罠はここにあります。成功を返す失敗は、500 を返す失敗よりはるかに気づきにくいのです。
私たち自身が落ちました
読み取れないサーバーは「unknown」に分類しています。なぜこの区分がこれほど大きいのか調べたところ、387 台が誤った方法でリクエストしていた HTTP+SSE エンドポイントだと判明しました。URL が /sse で終わるサーバーのうち 84% が unknown 扱いで、カタログ全体では 10% です。八倍の差は偶然ではなく、指紋です。
それらのサーバーはずっと正常でした。間違ったプロトコルを話していたのは私たちのほうです。
避け方
- JSON-RPC の本文が空、または解析できない 200 応答は、死んだサーバーではなくトランスポートの不一致として扱ってください。
- コンテンツタイプが text/event-stream で、最初のイベント名が endpoint なら、それをたどってそちらへ送ってください。
- URL だけを信用しないでください。/sse で終わるのは手がかりであって保証ではなく、/sse で終わらない HTTP+SSE エンドポイントも多数あります。
サーバーを公開しているなら、最も安上がりな対策はレジストリ登録でどのトランスポートを話すか明示することです。ほとんどのクライアントはそれを信じます。
プロトコルの挙動でサーバーを探す