

MCP는 JSON-RPC 2.0을 통신 포맷으로 사용하며, 이 메시지를 효율적으로 주고받기 위한 전송 계층(transport mechanism)이 필요합니다. 단순한 HTTP 요청-응답 모델은 지연(latency)과 연결 비용이 크기 때문에, 지속적이고 실시간 성격의 AI 응답 처리에는 적합하지 않습니다.
그래서 MCP는 AI 시스템 간 통신을 위한 프로토콜로, 본래 HTTP+SSE(Server Sent Events) 기반으로 서버에서 클라이언트로의 스트리밍 통신을 구현했습니다. 그러나 2025년 3월 26일 버전부터는 HTTP+SSE를 폐지하고 Streamable HTTP로 전환하였습니다.
이 글에서는, 전송 계층(transport mechanism)에 대해 알아보기 전에 JSON-RPC 2.0 에 대해서 알아보도록 하겠습니다
JSON-RPC 2.0은 JSON 기반의 원격 프로시저 호출(Remote Procedure Call, RPC) 프로토콜(규칙과 약속)로, 클라이언트와 서버가 JSON 형식의 메시지를 주고받아 메서드(함수)를 호출하는 방식입니다. 쉽게 말해, 한 시스템이 네트워크를 통해 다른 시스템의 함수를 마치 로컬 함수처럼 호출할 수 있게 해주는 규약입니다.
2009년에 표준화된 이 프로토콜은 필요 최소한의 규칙으로도 원하는 기능을 구현할 수 있고, stateless(상태를 유지하지 않음)·구조 설계에서 오는 부담을 최소화 해서 가볍고, HTTP나 WebSocket 같은 다양한 전송 계층 위에서 동작할 수 있습니다. JSON-RPC의 핵심 철학은 단순함입니다. REST API처럼 리소스 중심이 아닌, 액션(메서드) 중심으로 설계되어 있어 "이 작업을 수행해줘"라는 요청을 직관적으로 표현할 수 있습니다.
JSON-RPC 2.0은 응답에서 성공은 result 필드로, 실패는 error 필드로만 표현합니다.
메시지 구조가 최소화되어 있어 복잡한 규칙 없이 구현이 가능합니다.
REST처럼 "리소스 중심"이 아니라, 메서드 중심(액션 중심) 설계이므로, 호출 목적이 명확합니다.
// 성공 응답
{"jsonrpc":"2.0","result":42,"id":1}
// 실패 응답
{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found"},"id":1}
REST에서는 /users/1 GET, POST, PUT 등 HTTP 메서드로 행동을 구분하지만,
JSON-RPC에서는 "method": "user.create"처럼 호출 동작 자체를 명시합니다.
JSON-RPC 2.0은 데이터 구조만 규정하고, 전송 프로토콜에는 종속되지 않습니다. 즉, HTTP, WebSocket, TCP, IPC(프로세스 간 통신) 등 JSON 문자열을 전달할 수 있는 채널이면 모두 가능합니다.
동일한 메시지 포맷을 여러 통신 방식에 재사용할 수 있습니다.
HTTP POST
POST /rpc HTTP/1.1
Host: example.com
Content-Type: application/json
{"jsonrpc":"2.0","method":"math.add","params":[5,7],"id":1}
// 연결 후 바로 전송
{"jsonrpc":"2.0","method":"math.add","params":[5,7],"id":1}
두 경우 모두 동일한 JSON 구조를 사용하므로, 서버 로직은 전송 계층에 의존하지 않습니다.
각 호출은 독립적이며, 서버는 이전 요청의 상태를 기억하지 않습니다.
클라이언트가 매 요청마다 필요한 모든 정보를 전달해야 합니다.
이 방식은 수평 확장, 장애 격리, 캐시 프락시 활용에 유리합니다.
수평 확장에 유리 : 서버 간 상태 동기화가 필요 없으므로, 부하 분산(Load Balancing) 을 쉽게 적용 가능. 어떤 서버가 요청을 받아도 동일한 결과를 반환할 수 있음.
장애 격리 : 특정 서버 인스턴스가 장애가 나더라도, 다른 인스턴스가 요청을 동일하게 처리할 수 있음. 세션 데이터를 메모리에 저장하는 구조(Session Sticky)보다 안정적.
캐시 활용 가능성 높음 : 동일한 요청은 동일한 응답을 반환하므로, HTTP 캐시나 Reverse Proxy(Cache Layer) 를 쉽게 적용 가능.
사용 예시
{"jsonrpc":"2.0","method":"order.get","params":{"orderId":"ORD-1001"},"id":1}
서버는 ORD-1001에 대한 정보만 조회해서 반환하고, 이전 호출의 컨텍스트나 세션 상태를 참조하지 않습니다. 만약 추가 정보가 필요하다면 다음 요청에 다시 포함해야 합니다.
여러 요청을 배열로 묶어서 한 번에 전송하면 네트워크 왕복 횟수를 줄일 수 있습니다.
응답은 배열 형태로 반환되며, 요청 순서와 상관없이 id로 매칭합니다.
[
{"jsonrpc":"2.0","method":"math.add","params":[1,2],"id":"a"},
{"jsonrpc":"2.0","method":"math.multiply","params":[3,4],"id":"b"}
]
[
{"jsonrpc":"2.0","result":3,"id":"a"},
{"jsonrpc":"2.0","result":12,"id":"b"}
]
id 필드를 생략하면 서버는 응답을 보내지 않습니다.
WebSocket, TCP 같은 전송 방식에서는 Notification에 대해 아무 메시지도 보내지 않는 것이 가능. 즉, 연결만 유지하고, 해당 호출에 대한 응답 패킷을 보내지 않음.
HTTP의 경우 “아예 생략”이란 게 불가능하니, 대신 204 같은 빈 응답을 보냄.
결과가 필요 없는 작업(로그 적재, 이벤트 기록 등)에 적합합니다.
{"jsonrpc":"2.0","method":"logEvent","params":{"event":"user_login"}}
JSON-RPC 2.0에서 Notification은 id 필드를 생략해 "응답을 보내지 않는다"는 의미를 가집니다. 하지만 HTTP 위에서 동작할 때는, 서버가 아무 응답도 안 보내고 연결을 끊을 수는 없습니다. HTTP는 요청을 받으면 반드시 응답 상태줄과 헤더를 보내야 요청-응답 사이클이 완성됩니다. HTTP에서는 보통 204 No Content로 처리하거나 응답을 아예 생략합니다.
{
"jsonrpc": "2.0",
"method": "math.add",
"params": [1, 2],
"id": 1
}
- jsonrpc: 항상 "2.0".
- method: 문자열, 호출할 메서드 이름.
- params: 선택적, 배열 또는 객체.
- id: 응답을 매칭하기 위한 식별자. 없으면 Notification.
{
"jsonrpc": "2.0",
"result": 3,
"id": 1
}
{
"jsonrpc": "2.0",
"error": { "code": -32601, "message": "Method not found" },
"id": 1
}
의미: 클라이언트가 보낸 JSON 문자열이 유효하지 않아 파싱에 실패했을 때 발생합니다.
상황 예시:
{"jsonrpc": "2.0", "method": "sum", "params": [1,2], "id": 1
(중괄호 닫힘 누락 등 구문 오류)
의미: 요청 자체의 형식이 JSON-RPC 2.0 스펙에 맞지 않을 때 발생합니다.
주요 원인:
예시:
{"method": 123, "id": "abc"} // method는 반드시 문자열이어야 함
의미: 전달된 파라미터(params)가 잘못되었을 때 발생합니다.
주요 원인:
예시:
{"jsonrpc": "2.0", "method": "sum", "params": {"a":1}, "id":1}
(서버가 a와 b 둘 다 필요로 하는 경우)
의미: JSON-RPC에서 표준화하지 않은, 서버 애플리케이션 특화 에러를 정의할 수 있는 범위입니다.
활용 예시:
-32000: 데이터베이스 연결 오류
-32001: 인증 실패
-32002: 권한 부족
-32010: 특정 비즈니스 로직 오류 (예: 배송 불가 지역)
| 코드 | 의미 | 설명 |
|---|---|---|
| -32700 | Parse error | JSON 파싱 실패 |
| -32600 | Invalid Request | 형식이 올바르지 않음 |
| -32601 | Method not found | 메서드 미등록 |
| -32602 | Invalid params | 파라미터 오류 |
| -32603 | Internal error | 서버 내부 오류 |
| -32000~-32099 | Server error | 커스텀 서버 에러 범위 |