09. 무엇을 언제 쓰나 + 구(舊) SSE 호환

🎯 이 장의 목표
  • 상황에 맞는 전송(stdio vs Streamable HTTP)을 고를 수 있다
  • 구 HTTP+SSE 전송이 왜 deprecated인지, 호환을 어떻게 다루는지 안다
  • 전송이 바뀌어도 도구 로직은 그대로라는 "전송 무관성"을 이해한다

한 장으로 정리하는 선택 가이드

flowchart TD
    Q{"서버를 어디서 쓰나?"}
    Q -->|"내 PC에서, 로컬 도구로"| STDIO["stdio\n자식 프로세스"]
    Q -->|"네트워크 너머, 여러 사용자"| HTTP["Streamable HTTP\n원격 서비스"]
    STDIO --> S1["빠르고 단순\n포트·인증 불필요"]
    HTTP --> H1{"규모는?"}
    H1 -->|"단일·소규모"| HSF["stateful 가능"]
    H1 -->|"대규모·로드밸런싱"| HSL["stateless + json_response"]
    classDef q fill:#bfdbfe,stroke:#1d4ed8,color:#000;
    classDef stdio fill:#5eead4,stroke:#0f766e,color:#000;
    classDef http fill:#fde68a,stroke:#b45309,color:#000;
    class Q,H1 q;
    class STDIO,S1 stdio;
    class HTTP,H1,HSF,HSL http;

현재 스펙(2025-11-25)은 stdioStreamable HTTP 두 가지를 표준 전송으로 정의하며, 구 SSE는 하위 호환을 위해서만 남아 있습니다. 실무 권장은 명확합니다:

  • 이제 막 시작한다면 → stdio. 도구를 로컬에서 동작시키고, 통합을 검증하고, 도구 설명이 명확한지·응답이 유용한지부터 확인하세요. 전송은 서버에서 가장 덜 흥미로운 부분이니 처음부터 고민할 필요가 없습니다.
  • 원격으로 가야 한다면 → Streamable HTTP. MCP의 전송 무관 설계 덕분에 마이그레이션 경로가 단순합니다 — 도구 로직은 바뀌지 않고 전송 배선만 바뀝니다.
  • 기존 SSE 서버를 유지 중이라면 → Streamable HTTP를 곁에 추가. 스펙이 깔끔한 하위 호환 경로를 제공합니다(아래 참고).
기준stdioStreamable HTTP
위치로컬 (같은 머신)원격 (네트워크)
클라이언트 수1 (프로세스-당)다수 동시
네트워크/포트없음있음 (URL)
인증전송 계층 없음OAuth 등
성능매우 빠름 (파이프)게이트웨이·네트워크 비용
배포실행 명령만 등록호스팅·TLS·스케일링
대표 사용처데스크톱 앱, CLI 도구, 개발사내·SaaS·다중 사용자 서비스
💡 팁
클라이언트 지원: 주요 MCP 클라이언트(데스크톱 앱·코딩 에이전트 등)는 stdio를 폭넓게 지원합니다. "가장 낮은 공통분모"라는 점에서 로컬 배포의 안전한 선택입니다. 원격이 꼭 필요한 게 아니라면 stdio로 시작하세요.

구 HTTP+SSE 전송 — 왜 사라졌나

MCP가 처음 원격 전송이 필요했을 때(2024-11-05) 채택한 것이 HTTP+SSE였습니다. 이 방식은 엔드포인트가 둘이었습니다 — 클라이언트가 스트림을 받기 위해 /sse에 지속적 GET 연결을 열고, 요청은 별도 /messages에 POST하는 구조였죠.

문제가 여럿이었습니다:

  • 엔드포인트 2개: 서버가 SSE 연결 ID와 POST 요청을 짝지어 응답을 라우팅해야 해서, 상태 복잡성이 커지고 수평 확장이 어색했습니다.
  • 단방향 스트리밍: SSE는 서버→클라이언트 한 방향만 스트리밍하고, 클라이언트→서버는 별도 POST가 필요해 비대칭적이었습니다.
  • 인프라 궁합: 많은 로드 밸런서·게이트웨이·CDN이 오래 유지되는 SSE 연결을 잘 다루지 못해, 타임아웃·버퍼링·연결 관리가 운영 골칫거리였습니다.

그래서 2025-03-26에서 Streamable HTTP(단일 엔드포인트)가 이 모든 한계를 해결하며 도입됐고, HTTP+SSE는 deprecated 됐습니다.

⚠️ 흔한 실수
흔한 실수: 2024~2025 초의 튜토리얼을 보고 새 프로젝트를 SSE 2개 엔드포인트로 시작하는 것. 새 구현은 SSE를 건너뛰고 Streamable HTTP로 가세요. SSE는 아직 Streamable HTTP로 업그레이드하지 않은 구형 클라이언트와의 호환을 위해서만 남아 있습니다.

하위 호환 — 한 서버가 둘을 동시에

기존 SSE 서버가 있다면 당황할 필요 없습니다. 스펙은 명확한 하위 호환 경로를 제공합니다 — 서버는 새 전송과 기존 SSE 엔드포인트를 동시에 호스팅할 수 있고, 클라이언트는 먼저 MCP 엔드포인트에 POST를 시도해 어느 쪽을 지원하는지 자동 감지할 수 있습니다.

클라이언트 측 자동 감지 흐름은 이렇습니다:

flowchart TD
    START["사용자가 준 서버 URL"] --> POST["InitializeRequest를 POST 시도\n(Accept 헤더 포함)"]
    POST -->|"성공"| NEW["새 Streamable HTTP 서버로 간주\n→ 이 전송 사용"]
    POST -->|"400/404/405 실패"| GET["URL에 GET 요청\n(SSE 스트림 기대)"]
    GET -->|"endpoint 이벤트 도착"| OLD["구 HTTP+SSE 서버로 간주\n→ SSE 전송 사용"]
    classDef start fill:#bfdbfe,stroke:#1d4ed8,color:#000;
    classDef new fill:#86efac,stroke:#15803d,color:#000;
    classDef old fill:#fca5a5,stroke:#b91c1c,color:#000;
    class START,POST,GET start;
    class NEW new;
    class OLD old;

클라이언트는 사용자가 준 URL이 구 전송인지 새 전송인지 모를 수 있으므로, 먼저 InitializeRequest를 POST해 봅니다. 성공하면 새 Streamable HTTP로 간주하고, 400 Bad Request·404 Not Found·405 Method Not Allowed로 실패하면 GET 요청을 보내 SSE 스트림과 첫 endpoint 이벤트가 오는지 확인해 구 전송으로 처리합니다.

💡 팁
실무: 직접 이 감지 로직을 짜는 일은 드뭅니다. 대부분 SDK가 처리해 줍니다. 알아둘 점은 "한 서버가 두 전송을 병행 호스팅할 수 있고, 클라이언트가 알아서 고른다"는 사실입니다. 그래서 마이그레이션을 점진적으로 할 수 있습니다.

전송 무관성 — 도구 로직은 안 바뀐다

MCP의 중요한 설계 원칙은 전송 무관성(transport-agnostic) 입니다. 통신 세부를 추상화함으로써, MCP는 어떤 전송에서도 동일한 JSON-RPC 2.0 메시지 형식을 씁니다. 프로토콜은 양방향 메시지 교환을 지원하는 어떤 통신 채널 위에서도 구현될 수 있고, 클라이언트·서버는 필요하면 커스텀 전송을 끼워 넣을 수도 있습니다.

실용적 의미는 큽니다. 07장08장의 예제를 비교해 보면, 도구 정의(@mcp.tool()로 감싼 함수)는 완전히 동일하고 mcp.run(...)transport 인자만 다릅니다.

PYTHON
# 같은 서버, 전송만 교체
if __name__ == "__main__":
    # 로컬: mcp.run(transport="stdio")
    # 원격: mcp.run(transport="streamable-http")
    mcp.run(transport="stdio")
📌 핵심
핵심: 비즈니스 로직(도구·리소스·프롬프트)을 전송과 분리해서 짜면, 로컬 프로토타입을 원격 서비스로 옮길 때 로직은 그대로 두고 전송 배선만 바꾸면 됩니다. 5부에서 서버를 만들 때 이 분리를 처음부터 지키겠습니다.

미래 방향 (참고, 단정 금지)

전송 작업 그룹은 대규모 원격 배포를 위해 Streamable HTTP를 넘어서는 방향(예: 초기화 핸드셰이크 부담을 줄이고, 연결 전에 서버 능력을 알 수 있는 /.well-known/mcp.json 형태의 "Server Card" 등)을 탐색 중입니다. 다만 MCP는 최소 호환 기준을 위해 앞으로도 STDIO(로컬)와 Streamable HTTP(원격) 두 공식 전송만 지원할 방침입니다.

⚠️ 흔한 실수
위 미래 방향은 작성 시점에 탐색·초안 단계입니다. 확정된 기능으로 단정하지 말고, 실제 구현 전 최신 스펙·SDK를 확인하세요.

이 장에서 배운 것

  • 로컬·시작 단계는 stdio, 원격·다중 사용자는 Streamable HTTP. 대규모면 stateless를 고려.
  • HTTP+SSE(2개 엔드포인트)는 deprecated — 엔드포인트 2개·단방향·인프라 궁합 문제 때문. 새 구현은 쓰지 않는다.
  • 서버는 두 전송을 병행 호스팅할 수 있고, 클라이언트는 POST 시도 → 실패 시 GET으로 자동 감지한다.
  • 전송 무관성 덕분에 도구 로직은 그대로 두고 전송만 바꿀 수 있다.

✍️ 확인 문제

  1. "사내 직원 누구나 브라우저 기반 도우미에서 접근하는 서버"를 만든다. 어떤 전송이 적절하고, 규모가 커지면 어떤 설정을 추가로 고려할까?
  2. 구 HTTP+SSE 전송의 단점 두 가지를 들어 보자.
  3. 로컬 stdio로 잘 돌던 서버를 원격으로 옮기려 한다. 도구 함수 코드는 얼마나 바꿔야 할까? 왜?
3부 끝. 다음은 4부 · 서버의 세 가지 핵심 능력(10장 Tools)으로 이어집니다.