제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
서론
MCP 서버 개발이나 공부를 하게되면 stdio, SSE, Streamable HTTP 등 여러 방식과 용어가 나온다.
해당 포스팅에서는 각 방식의 차이점들과 특징들을 설명하도록 하려고 한다.
해당 포스팅 이후에 하위 질문들을 대답할 수 있을 정도로 공부를 진행해보자.
- HTTP는 어떻게 동작하는지?
- Streaming HTTP가 무엇인지
- SSE는 무엇이고, 어떻게 메시지를 전달하는지
- 기존 SSE의 한계는 무엇이였는지
- Streamable HTTP는 이를 어떻게 변경했는지
- Streamable HTTP에서도 여전히 왜 SSE가 등장하는지
HTTP의 기본구조
일반적인 HTTP 통신을 예로 들어보자.

Client에서 Request를 Server로 요청하면 Server는 그 API에 해당하는 적절한 비즈니스 로직을 진행 후 Response를 보낸다.
여기서 중요한 특징이 있다.
Client가 Request를 보내야 Server가 Response를 보낸다.
Server는 Request없이 Response가 가능한가? Server가 아무 이유 없이 Client에게 새로운 HTTP Response를 보내는 것은 일반적인 HTTP request-response 모델에서는 불가능하다.
이러한 작업이 불가능 하기에 전통적으로는 Client Polling을 사용했다.

예를 들어 1초마다 서버에 GET /status를 보내, 상태 변화가 없더라도 계속해서 Request랑 Response를 통신한다.
하지만 이러한 부분은 반복적인 동작을 계속해서 진행하고, 상태변화가 없을 시에 무의미하며 매번 Request와 Response를 생성하는 것에 리소스가 소모된다.
그래서 등장한 방식 중 하나가 Server-Send Events(SSE)이다.
SSE(Server-Sent Events)란?
SSE는 HTTP 연결을 이용하여 Server가 Client에게 지속적으로 Event를 전송할 수 있도록 하는 방식이다.
여기서 핵심은 HTTP Response를 즉시 끝내지 않는 것이다.
예를 들어 일반적인 HTTP Response같은 경우에는
HTTP/1.1 200 OK
Content-Length: 42
{...42바이트...}
과 같은 header를 받고, 이후 연결을 FIN 패킷을 이용하여 닫거나, keep-alive로 다음 요청에 재사용하는 반면에
SSE는 다음과 같이 동작한다.
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Transfer-Encoding: chunked ← 길이를 미리 정하지 않음
// 이미 열려있는 TCP 소켓에 write()만을 호출
data: {"price": 100}\n\n ← 10:00:01
data: {"price": 101}\n\n ← 10:00:05
data: {"price": 99}\n\n ← 10:00:12
Content-Length가 없고, 마지막 chunk도 보내지 않아, 클라이언트는 HTTP Respone가 끝나지 않았다고 판단. 계속해서 응답을 기다리는 구조이다. TCP는 메시지 경계가 없기에, 이벤트 경계를 SSE 포맷(\n \n)으로 지정하여 구분되며,
SSE는 UTF-8 기반의 text stream이며 event, data, id, retry 등의 필드를 사용할 수 있다.
WebSocket과의 차이점
여기서 드는 의문은 이러한 동작을 WebSocket으로 처리하면 되지 않나? 라는 의문이 들 수 있는데,
SSE와 WebSocket과는 큰 차이점이 존재한다.
바로 통신 방향이다.
SSE는 기본적으로 Server -> Client로의 단방향 통신인 반면
WebSocket은 Server <-> Client의 전이중(Full-Duplex) 통신이 가능하다.
따라서 SSE 자체만으로는 Client가 Server에 메시지를 보낼 수 없다.
또한 WebSocket을 통해 MCP를 구현하면 되지 않을까란 고민이 존재하지만,
MCP의 대부분은 기본적으로 RPC 형태이다.
즉 항상 양방향 실시간 socket이 필요한 것은 아니다.
단순한 Tool Server라면
POST tools/call
↓
Response
만으로 충분할 수 있다.
따라서 MCP처럼 단순 Tool Call부터 장시간 실행, Progress Notification, Server→Client Request까지 다양한 패턴을 지원해야 하는 프로토콜에는 현재의 MCP(추후 설명할 Streamable HTTP)의 유연성이 유용하다.
SSE를 활용한 초기 MCP의 통신 방법(Two-Channel)
SSE의 통신 방향이 MCP Server에서 Client로의 단방향이라면 데이터를 주고받기에 불가능할 것이다.
MCP Client는 어떠한 Tool을 실행을 요청할 통로가 없기 때문에 MCP Server는 그 해당 하는 응답 값을 보낼 수 없을 것이다.
그래서 기존 MCP 통신은 두개의 통신 Channel을 사용했다.
예를 들어 Client가 Tool을 호출한다고 생각해보자.
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "Seoul"
}
}
}
이러면 MCP 연결은 다음과 같다.

즉 통신 방향별로 역할을 나눴다.
Client → Server
HTTP POST
Server → Client
SSE
여기서 중요한 점은 Client가 보낸 POST의 JSON-RPC Response가 반드시 그 POST의 HTTP Response Body로 돌아오는 구조가 아니라는 것이다.
JSON-RPC Response가 별도의 SSE Channel을 통해 전달될 수 있다.
통신 흐름을 다시한번 정리하면
- Client는 GET /sse를 호출하여 Server -> Client의 장기 SSE Connection을 만든다.
- 그리고 Client는 서버로 별도의 POST Endpoint로 값을 보낸다(/message)
- Server가 처리한 JSON-RPC Response는 기존 SSE Connection으로 전달한다.
한계
읽다보면 알겠지만, 한계가 명확하다.
항상 장기 연결이 필요하다.
Client가 Server에 연결하려면 SSE Connection을 열고 유지해야 한다.
Client A ─════════════▶ Server
Client B ─════════════▶ Server
Client C ─════════════▶ Server
Client D ─════════════▶ Server
...
Client 수가 증가하면 Server는 많은 장기 연결을 관리해야 한다.
또한 하나의 논리적 Session이
GET /sse
+
POST /message
두 HTTP 흐름으로 분리되어 있기 때문에 서버는
"이 POST가 어느 SSE Connection에 해당하는가?"
를 알아야 한다.
즉 Session 관리가 중요해진다.
분산환경에서 살펴보면 더욱 명확하다.

Client가 만약 서버 한대와 Session을 맺었을 때 Client는 Load Balancer를 통해서 MCP Server를 선택하기 때문에
도구 호출은 다른 서버가 진행이 될 수도 있다.
따라서 서버 간 Session 공유나 Sticky Session 같은 추가적인 인프라 고려가 필요해진다.
즉 HTTP가 본래 제공하는 Stateless Request/Response의 장점을 충분히 활용하기 어려워진다.
또한 Backpressure 문제도 있다.
Backpressure란 쉽게 말하면
소비자가 처리할 수 있는 속도보다 생산자가 더 빠르게 데이터를 밀어 넣을 때 이를 어떻게 제어할 것인가?
이러한 구조 속에서 Client가 매우 빠르게 요청을 보내면 Server 내부에는 처리할 작업이 계속 쌓일 수 있다.
HTTP Request 자체가 자연스럽게 처리 속도를 제한해주지 않기에 비즈니스 로직은 나중에 처리 될 수 있고 응답을 늦게 받을 수 있다.
Streamable HTTP의 등장
https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
Transports - Model Context Protocol
Responses are generated using AI and may contain mistakes.
modelcontextprotocol.io
에서는 기존 HTTP+SSE Transport를 대체하기 위해 Streamable HTTP가 도입되었다.
특징 1. endpoint의 통합
기존에는
GET /sse
POST /message
처럼 역할이 분리되어 있었던 통신 구조를 하나의 MCP Endpoint를 중심으로 통신한다.
예를 들면
/mcp
통해 JSON-RPC 메시지는 HTTP POST로 보낸다.
POST /mcp
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "Seoul"
}
}
}
특징 2. Response를 선택할 수 있다.
Streamable HTTP에서 Server는 상황에 따라 일반 JSON Response 또는 Streaming Response를 사용할 수 있다.
간단한 요청이라면
Client Server
│ POST /mcp │
│ JSON-RPC Request │
│─────────────────────────────▶│
│ │
│ application/json │
│ JSON-RPC Response │
│◀─────────────────────────────│
같은 방식으로 처리할 수 있다.
즉 단순한 요청을 처리하기 위해 항상 SSE Connection을 유지할 필요가 없다.
하지만 실행 중 여러 메시지를 보내야 하는 경우가 있다.
예를 들어 Tool 실행이 오래 걸리면서 progress notification을 보내야 한다고 생각해보자.
Tool 실행 시작
20%
40%
60%
80%
완료
이 경우 Server는 POST Response를 즉시 종료하지 않고 SSE Stream으로 사용할 수 있다.
Client Server
POST /mcp
tools/call
──────────────────────────────▶
◀══════════════════════════════
progress 20%
◀══════════════════════════════
progress 40%
◀══════════════════════════════
progress 60%
◀══════════════════════════════
final JSON-RPC response
Response 종료
또한 SSE Stream을 닫아놓지 않기 때문에 다음과 같은 작업이 가능하다.
- MCP Server 내의 Resource가 변경
- Tool 목록이 변경
- Server 내의 비동기 이벤트가 발생.
만약 SSE Stream 이 존재하지 않는다면, Server가 Client 쪽으로 사용할 Response Channel이 없기 때문에,
Streamable HTTP의 stateful 형태에서는 Client가 GET을 이용해 별도의 SSE Stream을 열어둘 수 있다.
Client
GET /mcp
Accept: text/event-stream
│
▼
Server
│
│ SSE
▼
Client
이를 통해 Server가 unsolicited notification 등을 보낼 수 있다.
따라서 Streamable HTTP의 구조를 정확히 이해하면 다음과 같다.
Streamable HTTP
/mcp
│
┌──────────────┴──────────────┐
│ │
POST GET
│ │
Client → Server Server → Client
JSON-RPC Message Optional SSE Stream
│
▼
Response
│
├── application/json
│
└── text/event-stream
즉 하나의 endpoint에서 HTTP semantics를 활용하면서 필요한 경우 streaming을 결합한다.
정리.
SSE는 HTTP Response를 종료하지 않고 text/event-stream 형식으로 Server가 Client에게 지속적으로 데이터를 전송할 수 있게 하는 웹 표준 기술이다.
하지만 SSE는 기본적으로 단방향이다.
Server → Client
따라서 초기 MCP HTTP+SSE Transport는
Client → Server = HTTP POST
Server → Client = SSE
두 Channel을 조합하여 MCP 통신을 구현했다.
이 구조는 동작하지만 장기 SSE Connection, Session 관리, Request/Response Channel 분리, Backpressure, Load Balancing 및 수평 확장 측면에서 복잡성이 발생한다.
이를 개선하기 위해 MCP에서는 Streamable HTTP가 도입되었다.
Streamable HTTP에서는 Client가 JSON-RPC 메시지를 HTTP POST로 보내고 Server는 상황에 따라
application/json
또는
text/event-stream
으로 응답할 수 있다.
따라서 단순한 RPC는 일반적인 HTTP Request/Response처럼 처리하고, Progress나 중간 메시지 등 Streaming이 필요한 경우에만 SSE를 사용할 수 있다.
'AI > MCP' 카테고리의 다른 글
| [MCP] MCP에서 JSON-RPC를 채택한 이유 (0) | 2026.10.06 |
|---|---|
| [MCP] MCP를 이해하기 위한 JSON과 JSON-RPC 2.0 차이 (0) | 2026.10.06 |