MCP Remote 서버, SSE 그냥 쓰면 막히는 지점 있습니다

Published on

in

MCP Remote 서버, SSE 그냥 쓰면 막히는 지점 있습니다

2026.04.02 기준 / MCP 스펙 2025-06-18 기준
IT / AI

MCP Remote 서버, SSE 그냥 쓰면
막히는 지점 있습니다

MCP 스펙 2025-03-26부터 HTTP+SSE는 공식 deprecated입니다. Claude Connectors Directory는 이미 Streamable HTTP만 받습니다. 그런데도 대부분의 한국어 블로그는 아직 SSE 코드를 그대로 올리고 있습니다. 무엇이 어떻게 바뀌었는지, 그리고 마이그레이션할 때 가장 많이 틀리는 지점이 어디인지, 공식 스펙과 실제 코드 기준으로 정리했습니다.

2025.03.26
SSE 공식 deprecated 날짜
엔드포인트 2→1
SSE → Streamable HTTP 구조 변화
Serverless OK
Cloudflare Workers·Vercel Edge 배포 가능

SSE가 deprecated된 진짜 이유 — 보안 구조부터 달랐습니다

솔직히 말하면, “SSE가 deprecated됐다”는 사실은 이미 퍼진 편입니다. 그런데 왜 deprecated됐는지를 정확히 설명한 한국어 글은 찾기 어려웠습니다. 단순히 “Streamable HTTP가 더 좋아서”라고 넘기는 게 대부분이었는데, 공식 자료를 보면 이유가 꽤 구체적입니다.

SSE 구조에서 토큰이 URL에 노출됐습니다

HTTP+SSE 방식에서는 클라이언트가 두 개의 연결을 동시에 유지해야 했습니다. GET /sse로 서버 메시지를 수신하는 영구 연결을 열고, 클라이언트가 명령을 보낼 때는 별도의 POST /message를 써야 했습니다. 문제는 SSE 연결을 열 때 표준 브라우저 API로는 Authorization 헤더를 붙이기 어렵다는 점입니다. 결과적으로 많은 구현에서 인증 토큰을 URL 쿼리 파라미터(?token=xyz)에 담았고, 이는 서버 로그와 브라우저 히스토리에 그대로 남았습니다. (출처: Auth0 공식 블로그, 2025.12.19)

영구 연결은 서버리스 플랫폼에서 실행 자체가 안 됩니다

Cloudflare Workers나 Vercel Edge Functions는 장기 연결을 강제 종료합니다. SSE 방식으로 구축하면 이 플랫폼들에선 서버가 아예 동작하지 않습니다. 또한 로드 밸런서 앞에 서버를 두려면 sticky session을 설정해야 했는데, 이는 수평 확장을 사실상 막는 구조였습니다. (출처: Sunpeak.ai 공식 블로그, 2026.03.27)

💡 공식 스펙 변경 이력(modelcontextprotocol.io)과 Auth0 보안 분석을 같이 놓고 보면, SSE deprecated는 기술 성숙 문제가 아니라 구조적 보안 결함 수정이었습니다.

MCP 공식 스펙(modelcontextprotocol.io/specification/2025-03-26)에 HTTP+SSE는 deprecated로 명시됩니다. 새 구현에서 SSE 전용 서버를 올리는 건 이미 공식 지원 종료 방향으로 가고 있는 방식입니다.

▲ 목차로 돌아가기

stdio vs Remote — 뭘 써야 하는지 헷갈리는 분 많습니다

MCP를 처음 접하면 “왜 stdio를 쓰다가 Remote로 가야 하는지”가 애매합니다. 막상 정리해 보면 선택 기준이 꽤 명확합니다.

💡 공식 스펙을 직접 보면 이런 문장이 있습니다: “Clients SHOULD support stdio whenever possible.” — 로컬 환경에선 stdio가 여전히 권장 방식입니다. (출처: modelcontextprotocol.io/docs/concepts/transports)

“Remote가 더 최신이니 Remote를 써야 한다”는 인식은 공식 스펙과 다릅니다. stdio는 클라이언트가 서버를 서브프로세스로 직접 실행하는 방식으로, 네트워크 레이턴시 없이 빠르고, 별도 인증 설정이 필요 없습니다. Claude Desktop이나 Claude Code처럼 동일 머신에서 쓸 때는 stdio가 훨씬 단순합니다.

Remote가 필요한 상황은 다음 세 가지입니다

첫째, 여러 클라이언트(팀원 전체, 또는 여러 AI 에이전트)가 하나의 서버를 공유해야 할 때입니다. stdio는 1:1 구조라 공유가 안 됩니다. 둘째, Cloudflare Workers·Vercel·Fly.io 같은 서버리스나 엣지 플랫폼에 배포해야 할 때입니다. 셋째, Claude Connectors Directory에 제출해서 다른 사람이 쓸 수 있게 배포하려 할 때입니다. Connectors Directory는 Streamable HTTP만 허용합니다. (출처: Sunpeak.ai 공식 블로그, 2026.03.27)

구분 stdio Remote (Streamable HTTP)
실행 위치 동일 머신 별도 서버 (클라우드 포함)
동시 클라이언트 1개 다수
서버리스 배포 불가 가능 (stateless 모드)
Connectors Directory 제출 불가 가능
공식 권장 우선순위 SHOULD (로컬 우선) 원격 배포 필요 시

출처: modelcontextprotocol.io/docs/concepts/transports (2026.04.02 확인)

▲ 목차로 돌아가기

Streamable HTTP 구조 — 엔드포인트 하나로 다 됩니다

핵심은 단순합니다. SSE 방식은 /sse/message 두 개의 엔드포인트가 필요했는데, Streamable HTTP는 /mcp 하나에 POST, GET, DELETE 세 메서드를 모두 붙입니다.

TypeScript SDK 기준 전환 코드 — 바뀐 부분만 보면 됩니다

Before (SSE — deprecated):

// @modelcontextprotocol/sdk v1.10.0 미만 기준
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
app.get("/sse", async (req, res) => {
transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
await transport.handlePostMessage(req, res);
});

After (Streamable HTTP — 현행 스펙):

// @modelcontextprotocol/sdk v1.10.0 이상 필요
import { StreamableHTTPServerTransport }
from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless 모드
});
await server.connect(transport);
// POST, GET, DELETE 전부 /mcp 하나에
app.post("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
app.get("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
app.delete("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});

출처: Sunpeak.ai 공식 블로그 / modelcontextprotocol.io SDK 문서 (2026.03.27)

SDK 헬퍼 한 줄이면 DNS rebinding 보호까지 자동입니다

SDK가 제공하는 createMcpExpressApp()을 쓰면 Origin 헤더 검증, POST/GET/DELETE 라우팅, 세션 관리까지 자동으로 처리됩니다. 신규 구현에서 라우팅을 직접 짤 필요가 없습니다. 다만 커스텀 미들웨어나 인증을 얹어야 하는 상황이라면 수동 설정이 더 유연합니다.

Python SDK에서는 StreamableHTTPServerTransport 클래스를 사용하며, Starlette 앱에 Route("/mcp", ...)로 연결합니다. stateless_http=Truejson_response=True 옵션을 서버리스 배포 시 권장합니다. (출처: Sunpeak.ai 공식 블로그, 2026.03.27)

▲ 목차로 돌아가기

stateless vs stateful — 대부분 여기서 잘못 선택합니다

Streamable HTTP가 SSE와 결정적으로 다른 점은 stateless 모드 지원입니다. 그런데 실제 구현에서 이 선택을 잘못 하는 경우가 많습니다.

stateless 모드는 세션 ID 자체가 없습니다

TypeScript SDK에서 sessionIdGenerator: undefined로 설정하면, 서버는 요청 간 상태를 전혀 저장하지 않습니다. 각 POST 요청이 독립적으로 처리됩니다. Cloudflare Workers처럼 인스턴스가 매 요청마다 달라지는 환경에서는 stateless가 유일한 선택지입니다. 대부분의 Claude Connector는 stateless로 충분합니다. (출처: Sunpeak.ai 공식 블로그, 2026.03.27)

stateful이 필요한 경우는 딱 하나입니다

여러 요청에 걸쳐 서버 측에 상태를 유지해야 할 때만 stateful을 씁니다. 예를 들어 페이지 토큰을 서버가 들고 있다가 다음 요청에 이어줘야 하는 페이지네이션, 또는 다단계 wizard 흐름처럼 요청 순서가 의미 있는 경우입니다. 이 경우 sessionIdGenerator: () => randomUUID()를 설정하고, 서버가 세션 만료 시 HTTP 404로 응답해야 합니다. 클라이언트는 404를 받으면 InitializeRequest를 새로 보냅니다. (출처: modelcontextprotocol.io 공식 스펙, 2025-06-18)

💡 공식 스펙 설계 흐름을 따라가 보면 — stateful에서 세션이 만료됐을 때 500이 아닌 반드시 404를 내려야 합니다. 많은 구현이 에러 핸들러에서 이걸 삼켜버려 클라이언트가 재초기화 트리거를 못 받습니다.

정리하면 — 서버리스 배포라면 stateless, 고정 서버에 복잡한 상태가 필요하면 stateful. 고민이 된다면 stateless 먼저 시도해 보는 게 맞습니다.

▲ 목차로 돌아가기

세션 ID를 UUID로 쓰면 안 되는 이유가 있습니다

stateful 모드를 쓰기로 했다면 세션 ID 생성 방식이 중요합니다. 스펙에는 “globally unique and cryptographically secure”라고만 나와 있어서, 대부분 그냥 randomUUID()를 씁니다. 그런데 이게 충분하지 않은 이유가 있습니다.

💡 공식 발표문이 아닌 Auth0의 보안 분석(2025.12.19)과 스펙을 교차해서 보면 — UUID는 세션 위조 방어에 취약합니다. UUID를 아무나 생성할 수 있어서, 탈취된 UUID로 다른 사람의 세션에 붙을 수 있는 구조입니다.

JWT를 세션 ID로 쓰면 사용자 바인딩이 검증 가능해집니다

Auth0는 Mcp-Session-Id 값으로 JWT를 쓸 것을 권장합니다. JWT 안에 사용자 식별자를 담으면, 서버가 매 요청마다 Access Token의 User ID와 Session ID 안의 User ID를 대조할 수 있습니다. 세션 ID를 탈취해도 다른 사용자의 Access Token이 없으면 요청이 거부됩니다. (출처: Auth0 공식 블로그 “Why MCP’s Move Away from SSE Simplifies Security”, 2025.12.19)

스펙이 강제하는 세션 ID 형식 규칙도 있습니다

MCP 공식 스펙(2025-06-18)에는 세션 ID 형식 제약이 명시돼 있습니다: visible ASCII characters only, 즉 0x21~0x7E 범위 문자만 허용됩니다. JWT는 Base64url 인코딩이라 이 범위에 해당하지만, 일부 라이브러리가 생성하는 UUID에 포함된 특수 문자가 여기에 걸릴 수 있습니다. 스펙 원문을 직접 확인하고 쓰는 게 맞습니다. (출처: modelcontextprotocol.io/specification/2025-06-18)

결론은 — stateful 모드에서 세션 ID는 JWT로 만들고, 사용자 식별자를 페이로드에 포함시키는 것이 현재 권장 방식입니다.

▲ 목차로 돌아가기

마이그레이션할 때 자주 걸리는 함정 4가지

Sunpeak.ai가 정리한 마이그레이션 가이드와 공식 스펙을 교차 분석하면 실제로 자주 막히는 지점이 네 곳으로 좁혀집니다. 하나씩 짚겠습니다.

함정 1

DNS Rebinding 보호를 직접 구현 안 했을 때

공식 스펙은 모든 인커밍 연결에 Origin 헤더 검증을 MUST로 요구합니다. 로컬 서버는 반드시 127.0.0.1에만 바인딩해야 합니다. SDK 헬퍼 createMcpExpressApp()을 쓰면 자동 처리되지만, 라우팅을 수동으로 짜면 직접 넣어야 합니다. 이걸 빠트리면 외부 사이트가 로컬 MCP 서버에 접근할 수 있는 DNS Rebinding 공격에 노출됩니다. (출처: modelcontextprotocol.io/docs/concepts/transports)

함정 2

CORS 설정을 SSE 기준으로 그대로 뒀을 때

기존 SSE 서버에선 GET /ssePOST /message에 CORS를 설정했습니다. 새 구조에서는 단일 /mcp 엔드포인트에 POST, GET, DELETE 세 메서드 모두 허용해야 합니다. nginx 같은 리버스 프록시를 쓴다면 allowed methods 업데이트를 빠트리기 쉽습니다. (출처: Sunpeak.ai 공식 블로그)

함정 3

세션 만료 시 500을 내려보낼 때

stateful 모드에서 세션이 만료됐거나 서버가 재시작됐을 때, 클라이언트가 기존 Mcp-Session-Id를 들고 오면 서버는 반드시 HTTP 404를 내려야 합니다. 클라이언트는 404를 보고 새로 InitializeRequest를 보냅니다. 에러 핸들러에서 이걸 잡아 500으로 바꾸면 클라이언트가 재초기화를 못 하고 무한 오류 상태가 됩니다. (출처: modelcontextprotocol.io/specification/2025-06-18)

함정 4

엔드포인트 경로가 /mcp가 아닐 때

Claude Settings > Connectors에서 서버 URL을 입력할 때, Claude는 /mcp 경로를 기준으로 연결을 시도합니다. 기존 SSE 서버의 /sse/message를 그대로 두면 연결이 안 됩니다. Connectors Directory 제출 시 이 경로 규칙을 지키지 않으면 심사에서 거절됩니다. (출처: Sunpeak.ai 공식 블로그, 2026.03.27)

▲ 목차로 돌아가기

Q&A

Q1. SSE로 만든 기존 서버는 지금 당장 바꿔야 하나요?
Claude는 현재 SSE 연결을 아직 수락하지만, Anthropic은 이 지원이 곧 종료될 것이라고 공식적으로 밝혔습니다. Claude Connectors Directory에 제출하려면 지금 바로 Streamable HTTP가 필요합니다. 신규 서버라면 SSE를 시작점으로 쓸 이유가 없습니다. 기존 서버는 TypeScript SDK 기준 v1.10.0 이상으로 업데이트 후 트랜스포트 클래스만 교체하면 됩니다. (출처: Sunpeak.ai, 2026.03.27)
Q2. Streamable HTTP도 내부적으로 SSE를 씁니까?
씁니다. 단, 방식이 다릅니다. 기존 HTTP+SSE는 클라이언트가 미리 SSE 연결을 열어두고 거기서 서버 메시지를 받는 구조였습니다. Streamable HTTP에서 SSE는 서버가 클라이언트의 POST 요청에 대해 여러 메시지를 스트리밍해야 할 때 응답 포맷으로 선택적으로 씁니다. 영구 연결이 아니라, POST 요청의 응답 스트림입니다. (출처: modelcontextprotocol.io 공식 스펙)
Q3. Claude Desktop에서 Remote MCP를 쓰려면 mcp-remote 프록시가 필요한가요?
Claude Desktop은 현재 stdio 클라이언트로 동작합니다. HTTP 기반 Remote 서버에 직접 연결하려면 mcp-remote 같은 프록시를 거쳐야 합니다. 반면 Claude.ai 웹 인터페이스의 Connectors는 Streamable HTTP를 직접 지원합니다. 어떤 클라이언트를 쓰느냐에 따라 구축 방식이 달라집니다.
Q4. Remote MCP 서버를 무료로 배포할 수 있는 플랫폼이 있나요?
Cloudflare Workers 무료 플랜, Vercel 무료 플랜, Fly.io 무료 티어 모두 Streamable HTTP stateless 서버를 배포할 수 있습니다. 단 Cloudflare Workers는 실행 시간(CPU time) 제한이 있어서 무거운 연산이 있는 툴은 유료 플랜이 필요합니다. 서버리스 cold start 지연이 툴 호출 타임아웃을 유발할 수 있어서, 시작 코드를 최대한 가볍게 유지하는 것이 중요합니다.
Q5. 현행 MCP 프로토콜 버전은 무엇인가요?
2026년 4월 기준 최신 공개 버전은 2025-06-18입니다. Streamable HTTP가 표준 트랜스포트로 확정된 버전은 2025-03-26이며, 이 버전부터 HTTP+SSE가 deprecated됩니다. HTTP 요청에 MCP-Protocol-Version: 2025-06-18 헤더를 포함해야 하고, 헤더가 없으면 서버는 2025-03-26로 간주합니다. (출처: modelcontextprotocol.io/specification/2025-06-18)

▲ 목차로 돌아가기

마치며

써보니까, MCP Remote 서버 관련 글에서 가장 많이 보이는 패턴이 “Streamable HTTP로 바꿔야 한다”는 결론만 있고 왜, 어디서 막히는지를 빠트린 것들이었습니다. SSE deprecated의 배경에 보안 구조 문제가 있고, stateless/stateful 선택 기준이 실전에서 가장 많이 틀리는 지점이고, UUID 세션 ID가 왜 부족한지 — 이 세 가지를 공식 스펙과 Auth0 보안 분석 자료를 직접 교차해서 확인했습니다.

기대했던 것과 달랐던 부분은, Streamable HTTP가 SSE를 완전히 없앤 게 아니라는 점이었습니다. SSE는 응답 스트리밍 포맷으로 선택적으로 남아 있습니다. 구조가 바뀐 것이지, SSE 자체가 사라진 게 아닙니다. 이 부분을 오해하면 마이그레이션 방향을 잘못 잡기 쉽습니다.

MCP는 2024년 11월 공개 이후 스펙이 빠르게 바뀌고 있습니다. 지금 시점의 공식 스펙 원문을 직접 확인하는 것이, 어떤 블로그 글보다 정확합니다.

▲ 목차로 돌아가기

본 포스팅 참고 자료

  1. MCP 공식 Transport 스펙 —
    modelcontextprotocol.io/docs/concepts/transports
  2. MCP 공식 프로토콜 스펙 2025-06-18 —
    modelcontextprotocol.io/specification/2025-06-18
  3. Sunpeak.ai — Claude Connector SSE → Streamable HTTP 마이그레이션 가이드 (2026.03.27) —
    sunpeak.ai/blogs/claude-connector-sse-to-streamable-http
  4. Auth0 공식 블로그 — Why MCP’s Move Away from SSE Simplifies Security (2025.12.19) —
    auth0.com/blog/mcp-streamable-http
  5. Bright Data 공식 블로그 — SSE vs Streamable HTTP —
    brightdata.com/blog/ai/sse-vs-streamable-http

본 포스팅 작성 이후 서비스 정책·UI·기능이 변경될 수 있습니다. MCP 스펙은 현재 빠르게 업데이트되고 있으며, 최신 정보는
modelcontextprotocol.io 공식 사이트에서 확인하세요.
본 포스팅은 2026년 4월 2일 기준 MCP 스펙 2025-06-18을 바탕으로 작성되었습니다.

댓글 남기기


최신 글

  • 세금포인트 조회 사용 2026, 할인 혜택 전 확인
    세금포인트 조회 사용 2026 기준으로 포인트 잔액, 사용처와 조건, 납세담보 등 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 현금영수증 미발급 신고 2026, 포상금 전 증빙
    현금영수증 미발급 신고 2026 기준으로 결제 증빙, 상호·연락처, 요청 기록 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 보육료 전환 신청 2026, 양육수당 중복 체크
    보육료 전환 신청 2026 기준으로 입소일과 신청일, 양육수당·부모급여, 보육료 자격 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 청년월세지원 신청 2026, 임대차 서류 체크
    청년월세지원 신청 2026 기준으로 나이·거주 요건, 계약서와 이체 내역, 본인·원가구 소득 확인 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 국민취업지원제도 신청 2026, 구직촉진수당 체크
    국민취업지원제도 신청 2026 기준으로 유형과 자격, 월 소득과 재산, 구직활동 계획 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 국민연금 반환일시금 청구 2026, 수급 조건 확인
    국민연금 반환일시금 청구 2026 기준으로 10년 기준, 연령·국외이주 등, 신분·계좌·증빙 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 건강보험 환급금 조회 2026, 본인부담금 확인
    건강보험 환급금 조회 2026 기준으로 공식 화면 여부, 발생 사유, 본인 명의 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 주택청약 당첨 포기 2026, 재당첨 제한 체크
    주택청약 당첨 포기 2026 기준으로 주택 유형과 지역, 일정과 통장 영향, 사유와 소명 기한 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 청약통장 납입회차 확인 2026, 인정금액 체크
    청약통장 납입회차 확인 2026 기준으로 가입일과 회차, 인정 회차, 납입 인정금액 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.
  • 토지이용계획확인원 열람 2026, 매수 전 제한 확인
    토지이용계획확인원 열람 2026 기준으로 정확한 필지, 건축 가능성, 개발제한·보전 항목을 제출 전 확인 순서로 정리했습니다. 반려, 지연, 재처리를 줄이기 위한 체크리스트와 공식 출처를 함께 담았습니다.


아이테크 어른경제에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기