/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 엔드포인트도 많습니다.
서버를 공개한다면 가장 값싼 해결책은 레지스트리 항목에 어떤 전송을 사용하는지 밝히는 것입니다. 대부분의 클라이언트는 그 말을 믿습니다.
프로토콜 동작으로 서버 둘러보기