버전 관리와 호환성
버전 관리와 호환성 (Versioning and Compatibility)
클라이언트와 서버가 "어떤 프로토콜로 대화하고 있는지" 합의하는 방식을 정의하는 페이지예요. 요청마다 선언되는 프로토콜 버전, 기능을 통해 협상되는 선택적 확장, 그리고 이전 세대의 핸드셰이크 기반 프로토콜 개정과의 상호운용성을 설명해요.
출처: 문서
본문
이 페이지는 클라이언트와 서버가 서로 "무엇으로 말하고 있는지" 합의하는 방법을 정의해요: 모든 요청에 선언되는 프로토콜 버전, 기능(capabilities)을 통해 협상되는 선택적 확장, 그리고 이전의 핸드셰이크 기반 프로토콜 개정과의 상호운용성까지요.
협상 핸드셰이크는 없어요. 모든 요청이 자기 프로토콜 버전을 담고, 서버는 각 요청을 독립적으로 수락하거나 거부해요:
sequenceDiagram
participant Client
participant Server
Client->>Server: request (with `_meta`)
alt server supports requested version
Server-->>Client: result
else version unsupported
Server-->>Client: UnsupportedProtocolVersionError
Note over Client,Server: Client retries with a mutually supported version
end
용어 (Terminology)
이 페이지는 프로토콜 개정들 사이의 상호운용성을 위해 다음 용어를 사용해요:
- Modern: 버전·정체성·기능을 요청별 메타데이터로 전달하는 프로토콜 버전 (revision
2026-07-28이후) - Legacy:
initialize핸드셰이크로 세션을 수립하는 프로토콜 버전 (2025-11-25이전) - Dual-era: modern과 legacy 버전을 모두 지원하는 구현
프로토콜 버전 협상 (Protocol Version Negotiation)
모든 요청은 _meta 필드에 사용 중인 프로토콜 버전을 선언해요. HTTP에서는 이를 MCP-Protocol-Version 헤더로도 전달해요.
서버가 요청된 버전을 구현하지 않았다면(버전을 모르거나, 알지만 지원하지 않기로 했거나), MUST 반응으로 UnsupportedProtocolVersionError를 보내 자신이 지원하는 버전들을 나열해야 해요:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": {
"supported": ["2026-07-28", "2025-11-25"],
"requested": "1900-01-01"
}
}
}
클라이언트는 SHOULD supported 목록에서 상호 지원되는 버전을 골라 요청을 재시도하거나, 호환 버전이 없으면 사용자에게 오류를 표시해야 해요.
서버는 MUST server/discover를 구현해야 해요. 클라이언트는 MAY 다른 어떤 요청보다 먼저 호출해 서버의 지원 버전을 미리 알아낼 수 있지만, 필수는 아니에요. 클라이언트는 아무 RPC나 인라인으로 호출하고 선호 버전이 지원되지 않으면 UnsupportedProtocolVersionError를 처리하면 되거든요.
확장 협상 (Extension Negotiation)
클라이언트와 서버는 핵심 프로토콜을 넘어선 선택적 확장 지원을 협상할 수 있어요. 확장은 capabilities의 extensions 필드에 광고되는데, 이는 확장 식별자에서 확장별 설정 객체(per-extension settings object)로 가는 맵이에요. 확장 식별자는 _meta 키 명명 규칙을 따라 필수 접두사를 MUST 가져야 해요.
다음은 io.modelcontextprotocol/ui로 식별되는 MCP Apps 확장을 광고하는 클라이언트의 예시예요:
{
"capabilities": {
"roots": {},
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
io.modelcontextprotocol/tasks로 식별되는 Tasks 확장의 예시예요:
{
"capabilities": {
"tools": {},
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}
각 확장은 설정 객체의 스키마를 지정해요. 빈 객체는 추가 설정 없이 지원함을 나타내요.
한쪽이 확장을 지원하는데 다른 쪽은 지원하지 않는다면, 지원하는 쪽은 MUST 핵심 프로토콜 동작으로 되돌아가거나 적절한 오류로 요청을 거부해야 해요. 확장은 예상되는 폴백 동작을 문서화해야 SHOULD 해요.
초기화 기반 버전과의 하위 호환성 (Backward Compatibility with Initialization-Based Versions)
legacy 클라이언트(initialize 핸드셰이크를 기대)와 modern 클라이언트(요청별 메타데이터 사용)를 모두 지원하려는 서버는 MAY 두 동작을 모두 구현할 수 있어요.
두 종류의 서버와 상호운용해야 하는 클라이언트는 바인딩 페이지에 명시된 트랜스포트별 메커니즘으로 서버의 시대(era)를 감지해요:
- stdio:
server/discover로 조사하고, 인식된 modern 오류가 아닌 모든 오류에서 폴백한다. - Streamable HTTP: modern 요청을 시도하고, 폴백하기 전에
400 Bad Request본문을 검사한다.
두 경우 모두, 인식된 modern JSON-RPC 오류(예: UnsupportedProtocolVersionError)는 modern 서버를 식별해요. 클라이언트는 폴백 대신 지원되는 버전으로 재시도해요. 그 외의 모든 것은 legacy 서버를 식별해요.
시대 결정은 개별 요청의 속성이 아니라 서버의 속성이에요. 클라이언트는 SHOULD 결과를 서버 프로세스(stdio) 또는 origin(HTTP) 수명 동안 캐시하고, MAY 같은 서버 구성의 재시작에 걸쳐 이를 유지하며, 캐시된 가정이 나중에 실패하면 다시 조사해요.
modern 버전만 지원하는 서버는 SHOULD 어떤 트랜스포트에서든 initialize 요청에 반환하는 어떤 오류에서도 지원하는 프로토콜 버전을 명명해야 해요. legacy 클라이언트는 fall-forward 메커니즘이 없고, 이 메시지가 사용자에게 표시할 수 있는 유일한 진단일 수 있거든요.
호환성 매트릭스 (Compatibility Matrix)
다음 매트릭스는 클라이언트·서버 시대의 모든 조합에서 예상되는 결과를 요약해요:
| Client | Server | Outcome |
|---|---|---|
| Modern | Modern | Works. server/discover is optional; version mismatches surface as UnsupportedProtocolVersionError and the client retries with a mutually supported version. |
| Modern | Legacy | Fails. The server may reject the request with an implementation-defined error, stay silent, or even process an era-ambiguous method under legacy semantics. On stdio, clients SHOULD send server/discover first to fail deterministically; the client then surfaces an actionable error to the user. |
| Dual-era | Modern | Works. The stdio probe returns a DiscoverResult (or UnsupportedProtocolVersionError); on HTTP, the first modern request succeeds or returns a modern error. The client stays modern. |
| Dual-era | Legacy | Works. stdio: the probe returns a non-modern error or times out, and the client falls back to initialize. HTTP: the modern request returns a 4xx without a recognized modern error body, and the client falls back to initialize (and possibly further to the deprecated HTTP+SSE transport). |
| Legacy | Modern | Fails. stdio: the server rejects initialize with a JSON-RPC error; the exact code is implementation-defined (initialize is an unknown method and the request also lacks the required _meta fields). HTTP: the request is missing the required headers and is rejected per server validation with 400 Bad Request (a client on the deprecated HTTP+SSE transport fails at its opening GET instead). Legacy clients have no fall-forward mechanism. |
| Legacy | Dual-era | Works. The server answers initialize and serves the client according to the negotiated legacy revision. |
| Legacy | Legacy | Works according to the legacy revision; out of scope for this document. |
dual-era 서버는 클라이언트가 어떻게 연결을 여는지에 따라 동작을 선택해요:
- modern 요청별
_meta를 담은 요청은 이 개정에 따라 무상태로 서비스된다. initialize요청은 협상된 legacy 프로토콜 버전이 지정한 대로 stdio 프로세스(stdio) 또는 세션(HTTP)에 한정된 legacy 의미론을 선택한다.
dual-era 서버는 MAY 같은 엔드포인트나 프로세스에서 두 시대를 동시에 서비스할 수 있어요.