MCP 개념

Model Context Protocol(MCP)은 AI 어시스턴트가 외부 데이터 소스 및 도구와 통신할 수 있도록 하는 개방형 프로토콜입니다. MCP를 통해 다양한 LLM과 AI 에이전트가 파일 시스템, 데이터베이스, API 등 다양한 컨텍스트 소스에 표준화된 방식으로 접근할 수 있습니다.

업데이트 안내: 모델/요금/버전/정책 등 시점에 민감한 정보는 변동될 수 있습니다. 최신 내용은 공식 문서를 확인하세요.
빠른 시작
  • MCP는 Anthropic이 공개한 오픈 프로토콜
  • JSON-RPC 2.0 기반의 클라이언트-서버 아키텍처
  • stdio, HTTP SSE 등 다양한 전송 계층 지원
  • Tools, Resources, Prompts 세 가지 핵심 개념

MCP란 무엇인가?

정의

Model Context Protocol(MCP)은 AI 모델이 외부 데이터 소스 및 도구와 상호작용할 수 있도록 설계된 개방형 표준 프로토콜입니다. 2024년 Anthropic이 공개한 MCP는 AI 어시스턴트의 컨텍스트 제공 방식을 표준화하여, 다양한 데이터 소스를 통합하고 확장 가능한 생태계를 구축하는 것을 목표로 합니다.

개념
┌─────────────────────────────────────────────────────────┐
│  MCP의 핵심 가치                                          │
├─────────────────────────────────────────────────────────┤
│  1. 표준화: 모든 데이터 소스에 일관된 인터페이스 제공      │
│  2. 확장성: 새로운 도구와 리소스를 쉽게 추가              │
│  3. 재사용성: 한 번 작성한 서버를 여러 클라이언트에서 사용 │
│  4. 분리: 데이터 제공자와 AI 모델의 독립적 개발           │
└─────────────────────────────────────────────────────────┘

개발 배경

전통적으로 AI 어시스턴트는 다음과 같은 문제에 직면했습니다:

  • 컨텍스트 제한: 모델의 지식 컷오프 이후 정보나 실시간 데이터에 접근 불가
  • 통합 복잡도: 각 데이터 소스마다 커스텀 통합 코드 필요
  • 유지보수 부담: API 변경 시 모든 통합 코드 수정 필요
  • 재사용 불가: 한 어시스턴트용 통합을 다른 어시스턴트에서 재사용 어려움

MCP는 이러한 문제를 해결하기 위해 설계되었습니다.

기존 방식 vs MCP
측면 기존 방식 MCP
통합 방식 각 데이터 소스마다 커스텀 코드 표준 프로토콜
재사용성 낮음 높음
유지보수 복잡 단순
확장성 제한적 우수

주요 사용 사례

예제
// 1. 파일 시스템 접근
"프로젝트 src/ 디렉토리의 모든 TypeScript 파일을 분석해줘"
→ MCP 파일시스템 서버가 파일 목록과 내용 제공

// 2. 데이터베이스 쿼리
"지난 30일간 매출 상위 10개 제품을 보여줘"
→ MCP PostgreSQL 서버가 데이터베이스 쿼리 실행

// 3. API 통합
"GitHub에서 open 상태인 이슈 중 'bug' 레이블이 있는 것들을 찾아줘"
→ MCP GitHub 서버가 GitHub API 호출

// 4. 도구 실행
"이 Python 코드를 실행하고 결과를 보여줘"
→ MCP 실행 환경 서버가 코드 실행

MCP 아키텍처

전체 구조

AI 클라이언트 데스크톱 AI 앱 IDE / CLI 에이전트 Custom Client MCP 프로토콜 JSON-RPC 2.0 Transport Layer (stdio / HTTP SSE) MCP 서버 Filesystem Database API Integrations 데이터 소스 파일 시스템 로컬 파일 디렉토리 데이터베이스 PostgreSQL SQLite 웹 API GitHub Slack 기타 검색 엔진 실행 환경 메시지 흐름 1. 클라이언트: "list tools" 요청 2. 서버: 사용 가능한 도구 목록 응답 3. 클라이언트: "call tool" 요청 (매개변수 포함) 4. 서버: 도구 실행 및 결과 반환 5. 클라이언트: 결과를 AI 모델에 전달 6. AI: 결과를 기반으로 응답 생성

MCP 전체 구조: 클라이언트, 프로토콜, 서버, 데이터 소스 간 상호작용

핵심 컴포넌트

1. MCP 클라이언트

AI 어시스턴트 애플리케이션이 MCP 서버와 통신하기 위해 구현하는 컴포넌트입니다.

TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// 클라이언트 생성
const client = new Client({
  name: "my-ai-client",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// 서버에 연결 (stdio 전송)
const transport = new StdioClientTransport({
  command: "python",
  args: ["server.py"]
});

await client.connect(transport);

// 사용 가능한 도구 목록 조회
const tools = await client.listTools();
console.log(tools);

2. MCP 서버

실제 데이터 소스나 도구에 접근하여 AI 클라이언트에게 기능을 제공하는 서비스입니다.

Python
from mcp.server import Server
from mcp.server.stdio import stdio_server

# 서버 생성
server = Server("my-mcp-server")

# 도구 등록
@server.tool()
async def read_file(path: str) -> str:
    """파일 내용을 읽습니다."""
    with open(path, 'r') as f:
        return f.read()

# 서버 실행
async def main():
    async with stdio_server() as streams:
        await server.run(
            streams[0],
            streams[1],
            server.create_initialization_options()
        )

3. 전송 계층 (Transport Layer)

클라이언트와 서버 간 메시지 전송을 담당합니다.

전송 방식 특징 사용 사례
stdio 표준 입출력 사용, 프로세스 간 통신 로컬 서버, CLI 도구
HTTP SSE Server-Sent Events, 단방향 스트리밍 원격 서버, 웹 애플리케이션
WebSocket (계획 중) 양방향 실시간 통신 대화형 애플리케이션

프로토콜 계층

애플리케이션 계층 Tools, Resources, Prompts 프로토콜 계층 JSON-RPC 2.0 메시지 전송 계층 stdio / HTTP SSE / WebSocket 네트워크 계층 TCP/IP, Local IPC

MCP 계층 구조: 애플리케이션, 프로토콜, 전송, 네트워크

종합 아키텍처 다이어그램

다음 다이어그램은 MCP의 전체 아키텍처를 상세하게 보여줍니다. 호스트 애플리케이션 내부의 클라이언트가 전송 계층을 통해 여러 MCP 서버에 동시에 연결되고, 각 서버는 다양한 데이터 소스에 접근하여 Tools, Resources, Prompts 기능을 제공하는 구조입니다.

MCP 종합 아키텍처 Host Application (데스크톱 앱, IDE, CLI 등) LLM / Agent MCP Client MCP Client 2 Transport stdio SSE Streamable HTTP JSON-RPC 2.0 MCP Server A (Filesystem) Tools Resources Prompts MCP Server B (Database) Tools Resources Local Files PostgreSQL GitHub API Slack API MCP 메시지 흐름 시퀀스 Client Server 1 initialize (protocolVersion, capabilities) 2 response (serverInfo, capabilities) 3 notifications/initialized 4 tools/list 5 response (tools: [...]) 6 tools/call (name, arguments) 7 response (content: [...]) 8 resources/subscribe (uri) 9 notifications/resources/updated (비동기 알림)

MCP 종합 아키텍처: 호스트, 클라이언트, 전송 계층, 서버, 데이터 소스 간 연결 관계 및 메시지 흐름

MCP vs 기존 방식

Function Calling과의 비교

측면 Function Calling MCP
정의 위치 API 요청 시 함수 정의 전달 서버에서 도구 정의 관리
실행 주체 클라이언트 애플리케이션 MCP 서버
재사용성 낮음 (매번 정의 필요) 높음 (서버 재사용)
상태 관리 클라이언트 서버 (가능)
복잡도 간단한 도구에 적합 복잡한 통합에 적합
비교 예제
// Function Calling 방식
const response = await anthropic.messages.create({
  model: "claude-4-5",
  messages: [{ role: "user", content: "파일 읽어줘" }],
  tools: [{
    name: "read_file",
    description: "파일을 읽습니다",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string" }
      }
    }
  }]
});

// 도구 실행은 클라이언트가 직접 처리
if (response.stop_reason === "tool_use") {
  const result = await readFile(toolUse.input.path);
  // 결과를 다시 모델에게 전달...
}

// ─────────────────────────────────────

// MCP 방식
const client = new MCPClient();
await client.connect(fileSystemServer);

// 도구 목록은 서버에서 자동 제공
const tools = await client.listTools();

// 도구 실행도 서버가 처리
const result = await client.callTool("read_file", {
  path: "/path/to/file"
});

Tool Use와의 관계

MCP는 Tool Use를 보완하고 확장하는 프로토콜입니다:

  • Tool Use: AI 모델이 도구를 호출하도록 요청하는 기능 (모델 레벨)
  • MCP: 도구를 정의하고 실행하는 표준 프로토콜 (인프라 레벨)
통합 예제

LLM의 Tool Use 기능과 MCP를 함께 사용하면:

  1. MCP 서버가 사용 가능한 도구 목록 제공
  2. 클라이언트가 이를 Tool Use 형식으로 변환하여 모델에게 전달
  3. 모델이 도구 사용 결정
  4. 클라이언트가 MCP 서버를 통해 도구 실행
  5. 결과를 모델에게 반환

직접 API 통합과의 비교

비교
// 직접 API 통합 (각 클라이언트에서 반복)
class GitHubIntegration {
  constructor(token) {
    this.token = token;
  }

  async listIssues(repo) {
    const response = await fetch(`https://api.github.com/repos/${repo}/issues`, {
      headers: { Authorization: `token ${this.token}` }
    });
    return response.json();
  }

  // 수십 개의 메서드...
}

// ─────────────────────────────────────

// MCP 방식 (한 번만 작성, 모든 클라이언트에서 재사용)
const githubServer = await client.connect("github-mcp-server");

// 모든 GitHub 기능을 표준 인터페이스로 사용
const issues = await client.callTool("github_list_issues", {
  repo: "anthropics/anthropic-sdk-python"
});

프로토콜 개요

JSON-RPC 2.0

MCP는 JSON-RPC 2.0 표준을 기반으로 합니다.

JSON
// 요청 메시지
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/home/user/example.txt"
    }
  }
}

// 응답 메시지
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Hello, MCP!"
      }
    ]
  }
}

// 에러 응답
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "details": "File not found"
    }
  }
}

메시지 유형

유형 설명 예제
Request 클라이언트 → 서버 요청 tools/list, tools/call
Response 서버 → 클라이언트 응답 성공 시 result, 실패 시 error
Notification 응답 불필요한 알림 notifications/progress

핵심 메서드

1. 초기화

JSON
// initialize 요청
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": {
      "name": "Claude Desktop",
      "version": "1.0.0"
    },
    "capabilities": {
      "tools": {}
    }
  }
}

// initialize 응답
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {
      "name": "filesystem-server",
      "version": "1.0.0"
    },
    "capabilities": {
      "tools": {},
      "resources": {}
    }
  }
}

2. 도구 관련

JSON
// tools/list - 도구 목록 조회
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}

// 응답
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "read_file",
        "description": "파일 내용을 읽습니다",
        "inputSchema": {
          "type": "object",
          "properties": {
            "path": {
              "type": "string",
              "description": "파일 경로"
            }
          },
          "required": ["path"]
        }
      }
    ]
  }
}

// tools/call - 도구 실행
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/etc/hosts"
    }
  }
}

3. 리소스 관련

JSON
// resources/list - 리소스 목록
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "resources/list"
}

// resources/read - 리소스 읽기
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/read",
  "params": {
    "uri": "file:///path/to/document.txt"
  }
}

4. 프롬프트 관련

JSON
// prompts/list - 프롬프트 목록
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "prompts/list"
}

// prompts/get - 프롬프트 가져오기
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "prompts/get",
  "params": {
    "name": "code-review",
    "arguments": {
      "language": "python"
    }
  }
}

MCP 프로토콜 상세 스펙

MCP 프로토콜의 핵심은 JSON-RPC 2.0 표준 위에 구축된 메시지 교환 체계입니다. 이 섹션에서는 프로토콜의 세부 사양을 심층적으로 다루며, 실제 구현 시 참조할 수 있는 상세한 메시지 형식, 초기화 절차, 전체 메서드 목록, 에러 코드를 제공합니다.

JSON-RPC 2.0 기반 메시지 형식

MCP의 모든 통신은 JSON-RPC 2.0 사양을 준수합니다. 메시지는 크게 Request(요청), Response(응답), Notification(알림) 세 가지 유형으로 구분됩니다. 각 유형은 명확한 JSON 스키마를 가지며, 프로토콜의 신뢰성과 상호 운용성을 보장합니다.

Request (요청) 메시지

Request는 클라이언트 또는 서버가 상대방에게 특정 작업을 요청하는 메시지입니다. 반드시 id 필드를 포함하며, 상대방은 동일한 id로 응답해야 합니다.

JSON
// Request 메시지 스키마
{
  "jsonrpc": "2.0",        // 필수: 항상 "2.0"
  "id": 1,                   // 필수: 정수 또는 문자열 (고유 식별자)
  "method": "tools/list",   // 필수: 호출할 메서드 이름
  "params": {}               // 선택: 메서드에 전달할 매개변수
}

// 도구 목록 조회 요청 예제
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

// 도구 실행 요청 예제 (매개변수 포함)
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/home/user/document.txt"
    }
  }
}

Response (응답) 메시지

Response는 Request에 대한 응답 메시지입니다. 성공 시 result 필드를, 실패 시 error 필드를 포함합니다. resulterror는 상호 배타적이며, 하나만 존재해야 합니다.

JSON
// 성공 응답 스키마
{
  "jsonrpc": "2.0",        // 필수: 항상 "2.0"
  "id": 1,                   // 필수: 요청과 동일한 id
  "result": {               // 필수(성공): 결과 데이터
    "tools": [
      {
        "name": "read_file",
        "description": "파일 내용을 읽습니다",
        "inputSchema": {
          "type": "object",
          "properties": {
            "path": { "type": "string", "description": "파일 경로" }
          },
          "required": ["path"]
        }
      }
    ]
  }
}

// 에러 응답 스키마
{
  "jsonrpc": "2.0",        // 필수: 항상 "2.0"
  "id": 1,                   // 필수: 요청과 동일한 id
  "error": {                // 필수(실패): 에러 정보
    "code": -32601,          // 필수: 에러 코드 (정수)
    "message": "Method not found",  // 필수: 에러 메시지
    "data": {                // 선택: 추가 에러 정보
      "method": "unknown/method"
    }
  }
}

Notification (알림) 메시지

Notification은 응답을 기대하지 않는 단방향 메시지입니다. Request와 달리 id 필드가 없으며, 상대방은 이 메시지에 대해 응답하지 않습니다. 주로 상태 변경 알림, 진행 상황 보고 등에 사용됩니다.

JSON
// Notification 스키마 (id 필드 없음)
{
  "jsonrpc": "2.0",        // 필수: 항상 "2.0"
  "method": "notifications/initialized",  // 필수: 알림 메서드
  "params": {}               // 선택: 알림 데이터
}

// 초기화 완료 알림
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

// 진행 상황 알림
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "task-123",
    "progress": 75,
    "total": 100
  }
}

// 리소스 변경 알림
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "uri": "file:///config.json"
  }
}
메시지 유형 판별 방법
조건 메시지 유형 설명
id 존재 + method 존재 Request 응답 필요, method 실행 요청
id 존재 + result 또는 error Response 이전 Request에 대한 응답
id 없음 + method 존재 Notification 응답 불필요, 단방향 알림

초기화 핸드셰이크

MCP 연결의 첫 번째 단계는 초기화 핸드셰이크입니다. 이 과정은 정확히 3단계로 구성되며, 프로토콜 버전 합의, capability 교환, 연결 확인을 수행합니다. 초기화가 완료되기 전까지는 다른 어떤 메서드도 호출할 수 없습니다.

MCP 초기화 핸드셰이크 (3단계) Client Server 1 initialize (Request) params: protocolVersion: "2024-11-05" clientInfo: { name, version } capabilities: { tools, sampling, ... } 2 initialize (Response) result: protocolVersion: "2024-11-05" serverInfo: { name, version } capabilities: { tools, resources, ... } 3 notifications/initialized (Notification) id 없음, 응답 불필요 (단방향 알림) 연결 준비 완료 - 일반 메서드 호출 가능

단계 1: 클라이언트 → 서버 initialize 요청

클라이언트가 서버에 연결한 후 가장 먼저 보내는 메시지입니다. 프로토콜 버전, 클라이언트 정보, 지원하는 capabilities를 포함합니다.

JSON
// 클라이언트 → 서버: initialize 요청
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": {
      "name": "Claude Desktop",
      "version": "1.5.0"
    },
    "capabilities": {
      "sampling": {},
      "roots": {
        "listChanged": true
      }
    }
  }
}

단계 2: 서버 → 클라이언트 initialize 응답

서버는 프로토콜 버전 합의, 서버 정보, 서버 측 capabilities를 응답합니다.

JSON
// 서버 → 클라이언트: initialize 응답
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {
      "name": "filesystem-server",
      "version": "2.1.0"
    },
    "capabilities": {
      "tools": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "prompts": {
        "listChanged": true
      },
      "logging": {}
    }
  }
}

단계 3: 클라이언트 → 서버 initialized 알림

클라이언트가 서버의 응답을 처리한 후, 초기화 완료를 알리는 notification을 전송합니다. 이 단계가 완료되면 양측은 일반 메서드를 호출할 수 있습니다.

JSON
// 클라이언트 → 서버: initialized 알림
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}
// id 필드 없음 - Notification이므로 응답 불필요
초기화 관련 주의사항
  • initialized 알림 전송 전까지는 tools/list, resources/read 등 일반 메서드를 호출할 수 없습니다.
  • 프로토콜 버전이 호환되지 않으면 클라이언트는 연결을 거부해야 합니다.
  • 초기화 요청의 id는 일반적으로 0을 사용하지만, 유일한 식별자이면 어떤 값이든 가능합니다.

MCP 메서드 전체 목록

다음은 MCP 프로토콜에서 정의하는 모든 메서드의 종합 목록입니다. 각 메서드의 방향, 유형, 필요한 capability, 설명을 포함합니다.

초기화 관련 메서드

메서드 방향 유형 Capability 설명
initialize Client → Server Request - 프로토콜 버전 협상, capability 교환, 연결 초기화
notifications/initialized Client → Server Notification - 초기화 완료 확인, 일반 메서드 사용 가능 상태로 전환
ping 양방향 Request - 연결 상태 확인 (keepalive), 빈 result 응답

도구 관련 메서드

메서드 방향 유형 Capability 설명
tools/list Client → Server Request tools 서버가 제공하는 도구 목록 조회. 커서 기반 페이지네이션 지원
tools/call Client → Server Request tools 특정 도구 실행. 도구 이름과 인수를 전달하고 실행 결과 수신
notifications/tools/list_changed Server → Client Notification tools.listChanged 도구 목록 변경 알림. 클라이언트는 tools/list 재호출 필요

리소스 관련 메서드

메서드 방향 유형 Capability 설명
resources/list Client → Server Request resources 서버가 제공하는 리소스 URI 목록 조회
resources/read Client → Server Request resources 특정 URI의 리소스 내용 읽기 (텍스트 또는 바이너리)
resources/templates/list Client → Server Request resources 리소스 URI 템플릿 목록 조회 (동적 리소스)
resources/subscribe Client → Server Request resources.subscribe 특정 리소스의 변경 알림 구독
resources/unsubscribe Client → Server Request resources.subscribe 리소스 변경 알림 구독 해제
notifications/resources/updated Server → Client Notification resources.subscribe 구독 중인 리소스의 내용이 변경되었음을 알림
notifications/resources/list_changed Server → Client Notification resources.listChanged 리소스 목록 자체가 변경되었음을 알림

프롬프트 관련 메서드

메서드 방향 유형 Capability 설명
prompts/list Client → Server Request prompts 서버가 제공하는 프롬프트 템플릿 목록 조회
prompts/get Client → Server Request prompts 특정 프롬프트를 인수로 렌더링하여 가져오기
notifications/prompts/list_changed Server → Client Notification prompts.listChanged 프롬프트 목록이 변경되었음을 알림

로깅 관련 메서드

메서드 방향 유형 Capability 설명
logging/setLevel Client → Server Request logging 서버의 로그 레벨 설정 (debug, info, warning, error 등)
notifications/message Server → Client Notification logging 서버에서 클라이언트로 로그 메시지 전송

기타 메서드

메서드 방향 유형 Capability 설명
completion/complete Client → Server Request - 자동 완성 제안 요청 (프롬프트 인수나 리소스 URI)
sampling/createMessage Server → Client Request sampling 서버가 클라이언트의 LLM을 통해 텍스트 생성 요청
roots/list Server → Client Request roots 클라이언트의 파일 시스템 루트 정보 요청
notifications/roots/list_changed Client → Server Notification roots.listChanged 클라이언트의 루트 목록이 변경되었음을 알림
notifications/progress 양방향 Notification - 장시간 작업의 진행 상황을 보고
notifications/cancelled 양방향 Notification - 이전에 보낸 요청의 취소를 알림
메서드 네이밍 규칙
  • 일반 메서드: 카테고리/동작 형식 (예: tools/list, resources/read)
  • 알림 메서드: notifications/카테고리/이벤트 형식 (예: notifications/tools/list_changed)
  • 양방향 메서드: ping, notifications/progress 등은 클라이언트와 서버 모두 발신 가능

에러 코드 전체 참조

MCP에서 사용되는 에러 코드는 JSON-RPC 2.0 표준 에러 코드와 MCP 전용 에러 코드로 나뉩니다. 에러 응답의 code 필드에 사용되며, 클라이언트는 이 코드를 기반으로 적절한 에러 처리를 수행해야 합니다.

JSON-RPC 2.0 표준 에러 코드

코드 이름 설명 대처 방법
-32700 Parse error 서버가 수신한 JSON을 파싱할 수 없음. 유효하지 않은 JSON 형식 전송 데이터의 JSON 형식을 검증하세요. 인코딩 문제를 확인하세요.
-32600 Invalid Request 보낸 JSON은 유효하지만, JSON-RPC 요청 규격에 맞지 않음 jsonrpc, method, id 필드가 올바른지 확인하세요.
-32601 Method not found 요청된 메서드가 존재하지 않거나, 해당 capability가 비활성 상태 서버의 capabilities를 확인하고, 메서드 이름의 오타를 점검하세요.
-32602 Invalid params 메서드에 전달된 매개변수가 유효하지 않음 (타입 오류, 필수 필드 누락 등) 메서드의 입력 스키마를 확인하고, 필수 매개변수를 올바르게 전달하세요.
-32603 Internal error 서버 내부 오류. 예상치 못한 예외 발생 서버 로그를 확인하세요. 재시도하거나 서버 관리자에게 문의하세요.

MCP 전용 에러 코드

코드 이름 설명 대처 방법
-32002 Resource not found 요청된 리소스 URI에 해당하는 리소스가 존재하지 않음 리소스 URI를 확인하세요. resources/list로 사용 가능한 리소스를 조회하세요.
-32001 Tool execution error 도구 실행 중 오류 발생. 도구의 비즈니스 로직에서 발생한 에러 도구의 입력 매개변수를 확인하세요. 도구의 전제 조건(파일 존재 여부 등)을 점검하세요.
JSON
// 에러 응답 예제: Parse error
{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32700,
    "message": "Parse error",
    "data": {
      "details": "Unexpected token at position 42"
    }
  }
}

// 에러 응답 예제: Method not found
{
  "jsonrpc": "2.0",
  "id": 5,
  "error": {
    "code": -32601,
    "message": "Method not found",
    "data": {
      "method": "resources/list",
      "reason": "Server does not support resources capability"
    }
  }
}

// 에러 응답 예제: Tool execution error
{
  "jsonrpc": "2.0",
  "id": 12,
  "error": {
    "code": -32001,
    "message": "Tool execution error",
    "data": {
      "tool": "read_file",
      "reason": "Permission denied: /etc/shadow"
    }
  }
}
에러 처리 모범 사례
  • 클라이언트는 모든 에러 코드에 대한 기본 처리 로직을 구현하세요.
  • 사용자에게 에러를 표시할 때는 기술적 코드보다 이해하기 쉬운 한국어 메시지를 제공하세요.
  • -32603 Internal error는 일시적 장애일 수 있으므로, 적절한 지수 백오프(exponential backoff)로 재시도를 고려하세요.
  • 에러 응답의 data 필드를 활용하여 디버깅에 필요한 상세 정보를 로깅하세요.

전송 계층

stdio 전송

가장 일반적인 전송 방식으로, 서버를 자식 프로세스로 실행하고 표준 입출력으로 통신합니다.

TypeScript
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// Python 서버 실행
const transport = new StdioClientTransport({
  command: "python",
  args: ["-m", "mcp_server_filesystem"],
  env: {
    ...process.env,
    "PYTHONPATH": "/path/to/server"
  }
});

await client.connect(transport);

장점:

  • 간단한 설정
  • 로컬 프로세스로 보안 우수
  • 플랫폼 독립적

단점:

  • 원격 서버 지원 불가
  • 프로세스 생명주기 관리 필요

HTTP SSE 전송

Server-Sent Events를 사용한 HTTP 기반 전송입니다.

TypeScript
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

// 원격 서버 연결
const transport = new SSEClientTransport(
  new URL("https://mcp-server.example.com/sse")
);

await client.connect(transport);

장점:

  • 원격 서버 지원
  • HTTP 표준 기술 활용
  • 방화벽 친화적

단점:

  • 단방향 스트리밍 (서버 → 클라이언트)
  • 네트워크 오버헤드

전송 방식 비교

특성 stdio HTTP SSE
위치 로컬 로컬/원격
설정 복잡도 낮음 중간
지연시간 매우 낮음 낮음~중간
보안 프로세스 격리 TLS, 인증 필요
확장성 제한적 우수

stdio 전송 상세

stdio(Standard I/O) 전송은 MCP에서 가장 많이 사용되는 로컬 전송 방식입니다. 클라이언트가 MCP 서버를 자식 프로세스로 실행하고, 표준 입력(stdin)과 표준 출력(stdout)을 통해 JSON-RPC 메시지를 교환합니다. 각 메시지는 개행 문자(\n)로 구분되는 NDJSON(Newline-Delimited JSON) 형식을 사용합니다.

stdio 전송 메시지 흐름 Client Process (부모 프로세스) Server Process (자식 프로세스) stdin (JSON-RPC 요청) {"jsonrpc":"2.0","id":1,"method":"..."}\n stdout (JSON-RPC 응답) {"jsonrpc":"2.0","id":1,"result":{...}}\n stderr (로그 전용, 비프로토콜) 디버깅 메시지, 에러 로그 등
stdio 전송의 핵심 규칙
  • NDJSON 형식: 각 JSON-RPC 메시지는 한 줄의 JSON 문자열이며, \n(개행 문자)으로 구분됩니다. 메시지 내부에는 개행이 포함되면 안 됩니다.
  • stderr는 비프로토콜: stderr로 출력되는 내용은 MCP 프로토콜의 일부가 아닙니다. 서버의 디버깅 로그나 에러 메시지 출력 용도로만 사용하세요.
  • 프로세스 생명주기: 클라이언트는 서버 프로세스의 시작과 종료를 직접 관리합니다. 서버 프로세스가 예기치 않게 종료되면 재시작 로직을 구현해야 합니다.
  • 환경 변수 전달: 클라이언트는 자식 프로세스 실행 시 필요한 환경 변수(API 키, 경로 등)를 설정할 수 있습니다.
Python
import sys
import json

# stdio 서버의 메시지 읽기/쓰기 기본 구조
def read_message():
    """stdin에서 한 줄(JSON-RPC 메시지)을 읽습니다."""
    line = sys.stdin.readline()
    if not line:
        return None
    return json.loads(line.strip())

def write_message(message: dict):
    """stdout으로 JSON-RPC 메시지를 한 줄로 출력합니다."""
    line = json.dumps(message, ensure_ascii=False)
    sys.stdout.write(line + "\n")
    sys.stdout.flush()  # 즉시 전송 보장

def log_debug(msg: str):
    """stderr로 디버그 메시지를 출력합니다 (비프로토콜)."""
    sys.stderr.write(f"[DEBUG] {msg}\n")
    sys.stderr.flush()

# 메인 루프
while True:
    message = read_message()
    if message is None:
        break  # stdin이 닫히면 종료

    log_debug(f"수신: {message['method']}")

    # 메시지 처리...
    response = handle_request(message)
    write_message(response)

SSE (Server-Sent Events) 전송 상세

SSE 전송은 HTTP 기반의 원격 통신 방식입니다. 클라이언트에서 서버로의 메시지는 HTTP POST 요청으로 전송하고, 서버에서 클라이언트로의 메시지는 SSE(Server-Sent Events) 스트림으로 전송합니다. 이 비대칭 구조는 HTTP 인프라(프록시, 로드 밸런서, 방화벽)와 잘 호환됩니다.

TypeScript
// SSE 전송 서버 구현 예제 (Express 기반)
import express from "express";

const app = express();
app.use(express.json());

// SSE 엔드포인트: 서버 → 클라이언트 스트림
app.get("/sse", (req, res) => {
  // SSE 헤더 설정
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  // 메시지 전송 엔드포인트 URL을 클라이언트에게 전달
  const messageEndpoint = "/messages";
  res.write(`event: endpoint\ndata: ${messageEndpoint}\n\n`);

  // JSON-RPC 메시지를 SSE 이벤트로 전송하는 함수 등록
  const sendMessage = (message) => {
    res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
  };

  // 클라이언트 연결 등록...
  registerClient(sendMessage);

  req.on("close", () => {
    unregisterClient(sendMessage);
  });
});

// POST 엔드포인트: 클라이언트 → 서버 메시지
app.post("/messages", (req, res) => {
  const jsonRpcMessage = req.body;
  // JSON-RPC 메시지 처리...
  const response = handleMessage(jsonRpcMessage);
  res.json(response);
});

app.listen(3000);

SSE 전송의 통신 흐름:

  1. 클라이언트가 GET /sse로 SSE 연결을 수립합니다.
  2. 서버가 endpoint 이벤트로 메시지 전송용 POST URL을 알려줍니다.
  3. 클라이언트는 JSON-RPC 요청을 해당 POST URL로 전송합니다.
  4. 서버는 응답과 알림을 SSE 스트림의 message 이벤트로 전송합니다.

Streamable HTTP 전송

Streamable HTTP는 MCP에서 가장 최신에 추가된 전송 방식으로, SSE 전송의 후속 사양입니다. 단일 HTTP 엔드포인트를 사용하여 양방향 통신을 처리하며, 필요에 따라 SSE 스트리밍으로 업그레이드할 수 있는 유연한 구조를 가지고 있습니다.

Streamable HTTP의 핵심 특징
  • 단일 엔드포인트: 하나의 HTTP 엔드포인트(/mcp)에서 모든 통신 처리
  • 기본 무상태: 기본적으로 stateless이며, 서버가 세션 관리를 선택적으로 활성화 가능
  • SSE 업그레이드: 서버가 스트리밍이 필요한 경우 응답을 SSE로 업그레이드 가능
  • 인프라 호환성: 표준 HTTP 인프라(CDN, 로드 밸런서, API 게이트웨이)와 완벽 호환
TypeScript
// Streamable HTTP 서버 구현 예제
import express from "express";

const app = express();
app.use(express.json());

// 단일 엔드포인트에서 모든 MCP 통신 처리
app.post("/mcp", async (req, res) => {
  const message = req.body;
  const accept = req.headers["accept"] || "";

  // 클라이언트가 SSE를 수용하는 경우 스트리밍 응답 가능
  if (accept.includes("text/event-stream")) {
    // SSE 스트리밍 응답
    res.setHeader("Content-Type", "text/event-stream");
    res.setHeader("Cache-Control", "no-cache");

    // 진행 상황을 스트리밍으로 전송
    for await (const progress of processWithProgress(message)) {
      res.write(`data: ${JSON.stringify(progress)}\n\n`);
    }

    // 최종 결과 전송
    const result = await getFinalResult(message);
    res.write(`data: ${JSON.stringify(result)}\n\n`);
    res.end();
  } else {
    // 일반 JSON 응답 (단순 요청/응답)
    const result = await handleMessage(message);
    res.json(result);
  }
});

// 선택적 세션 관리
app.get("/mcp", (req, res) => {
  // 서버→클라이언트 스트림 (알림 수신용)
  res.setHeader("Content-Type", "text/event-stream");
  // 세션 ID를 통한 상태 관리
  const sessionId = req.headers["mcp-session-id"];
  // 알림 스트림 설정...
});

app.listen(3000);

전송 방식 종합 비교

다음 표는 MCP에서 지원하는 세 가지 전송 방식의 상세 비교입니다. 사용 환경과 요구사항에 따라 적절한 전송 방식을 선택하세요.

특성 stdio SSE (Server-Sent Events) Streamable HTTP
통신 방식 stdin/stdout 프로세스 간 통신 GET(SSE) + POST(메시지) 단일 HTTP 엔드포인트
서버 위치 로컬만 가능 로컬 / 원격 로컬 / 원격
프로세스 관리 클라이언트가 관리 독립 실행 독립 실행
상태 관리 프로세스별 상태 유지 연결별 상태 유지 기본 무상태, 선택적 세션
지연시간 매우 낮음 (IPC) 낮음~중간 (네트워크) 낮음~중간 (네트워크)
확장성 프로세스당 1 연결 다중 연결 가능 대규모 확장 용이
인프라 호환 해당 없음 일부 프록시 제한 CDN, LB 완벽 호환
재연결 처리 프로세스 재시작 필요 SSE 자동 재연결 HTTP 요청 수준 재시도
보안 프로세스 격리 TLS + 인증 토큰 TLS + 인증 토큰
권장 사용처 Claude Desktop 로컬 서버 원격 서버, 웹 애플리케이션 프로덕션 환경, 클라우드 배포
사양 상태 안정(Stable) 사용 중단 예정(Deprecated) 최신 권장(Recommended)
SSE 전송 사용 중단 예정 안내

SSE 전송은 Streamable HTTP 전송으로 대체될 예정입니다. 신규 프로젝트에서는 Streamable HTTP를 사용하는 것을 권장하며, 기존 SSE 기반 서버는 점진적으로 마이그레이션하세요. 다만, 당분간 SSE 전송도 계속 지원됩니다.

핵심 개념

Tools (도구)

클라이언트가 호출할 수 있는 함수나 작업입니다.

Python
@server.tool()
async def calculate(expression: str) -> str:
    """수학 표현식을 계산합니다.

    Args:
        expression: 계산할 수학 표현식 (예: "2 + 2")

    Returns:
        계산 결과
    """
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"에러: {e}"

특징:

  • 실행 가능한 작업 (부작용 가능)
  • 입력 스키마 정의 필요
  • 동기/비동기 실행 지원

Resources (리소스)

서버가 제공하는 데이터나 컨텐츠입니다.

Python
@server.resource("file://{path}")
async def read_file_resource(path: str) -> str:
    """파일을 리소스로 제공합니다."""
    with open(path, 'r') as f:
        return f.read()

특징:

  • 읽기 전용 데이터
  • URI로 식별
  • 컨텍스트 제공 목적

Prompts (프롬프트)

재사용 가능한 프롬프트 템플릿입니다.

Python
@server.prompt()
async def code_review_prompt(language: str, code: str) -> str:
    """코드 리뷰 프롬프트를 생성합니다."""
    return f"""다음 {language} 코드를 리뷰해주세요:

```{language}
{code}
```

다음 관점에서 검토해주세요:
1. 코드 품질
2. 성능
3. 보안
4. 베스트 프랙티스
"""

특징:

  • 템플릿 기반
  • 매개변수화 가능
  • 재사용성 높음

개념 비교

개념 목적 읽기/쓰기 예제
Tools 작업 수행 읽기/쓰기 파일 쓰기, 이메일 전송
Resources 데이터 제공 읽기 전용 파일 내용, DB 레코드
Prompts 프롬프트 템플릿 읽기 전용 코드 리뷰, 번역

연결 생명주기

1. 연결 (Connection) • 전송 계층 초기화 (stdio/HTTP SSE) • 서버 프로세스 시작 또는 네트워크 연결 2. 초기화 (Initialization) • 클라이언트: initialize 요청 (프로토콜 버전, capabilities) • 서버: initialize 응답 (서버 정보, capabilities) • 클라이언트: initialized 알림 전송 3. 작업 (Operation) • tools/list, resources/list, prompts/list • tools/call, resources/read, prompts/get • 반복 호출 가능 4. 종료 (Shutdown) • 클라이언트: 연결 해제 • 서버: 리소스 정리, 프로세스 종료

MCP 생명주기: 연결, 초기화, 작업, 종료 단계

TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

async function main() {
  // 1. 클라이언트 생성
  const client = new Client({
    name: "example-client",
    version: "1.0.0"
  }, {
    capabilities: {
      tools: {}
    }
  });

  // 2. 전송 계층 설정
  const transport = new StdioClientTransport({
    command: "python",
    args: ["server.py"]
  });

  try {
    // 3. 연결 및 초기화
    await client.connect(transport);
    console.log("연결 성공!");

    // 4. 작업 수행
    const tools = await client.listTools();
    console.log("사용 가능한 도구:", tools);

    const result = await client.callTool("read_file", {
      path: "/etc/hosts"
    });
    console.log("결과:", result);

  } finally {
    // 5. 종료
    await client.close();
    console.log("연결 종료");
  }
}

main().catch(console.error);

Capabilities (기능 선언)

클라이언트와 서버는 초기화 시 지원하는 기능을 선언합니다.

클라이언트 Capabilities

JSON
{
  "capabilities": {
    "tools": {},                    // 도구 호출 지원
    "resources": {
      "subscribe": true          // 리소스 변경 구독
    },
    "prompts": {},                  // 프롬프트 사용 지원
    "sampling": {}                  // 샘플링 요청 수신 (서버→클라이언트)
  }
}

서버 Capabilities

JSON
{
  "capabilities": {
    "tools": {
      "listChanged": true        // 도구 목록 변경 알림
    },
    "resources": {
      "subscribe": true,         // 리소스 구독 지원
      "listChanged": true        // 리소스 목록 변경 알림
    },
    "prompts": {
      "listChanged": true        // 프롬프트 목록 변경 알림
    },
    "logging": {}                   // 로그 메시지 전송
  }
}
협상 메커니즘

클라이언트와 서버는 초기화 시 capabilities를 교환하여 공통으로 지원하는 기능만 사용합니다. 예를 들어, 서버가 resources.subscribe를 지원하지 않으면 클라이언트는 리소스 구독 기능을 사용할 수 없습니다.

에러 처리

표준 에러 코드

코드 의미 설명
-32700 Parse error JSON 파싱 실패
-32600 Invalid request 잘못된 요청 형식
-32601 Method not found 지원하지 않는 메서드
-32602 Invalid params 잘못된 매개변수
-32603 Internal error 내부 서버 오류

커스텀 에러

Python
from mcp.server import McpError

@server.tool()
async def read_file(path: str) -> str:
    if not os.path.exists(path):
        raise McpError(
            code=-32001,
            message="File not found",
            data={"path": path}
        )

    with open(path, 'r') as f:
        return f.read()

클라이언트 에러 처리

TypeScript
try {
  const result = await client.callTool("read_file", {
    path: "/nonexistent.txt"
  });
} catch (error) {
  if (error.code === -32001) {
    console.error("파일을 찾을 수 없습니다:", error.data.path);
  } else {
    console.error("예상치 못한 오류:", error.message);
  }
}

다음 단계

학습 경로
  1. MCP 서버 개발: MCP 서버 개발 가이드에서 Python과 TypeScript로 서버를 만드는 방법 학습
  2. MCP 클라이언트: MCP 클라이언트 가이드에서 Claude Desktop, Claude CLI 설정 방법 학습
  3. 실전 예제: MCP 실전 예제에서 파일 시스템, 데이터베이스, API 통합 예제 확인
  4. 생태계 탐색: MCP 생태계에서 공개 서버 및 리소스 확인
  5. 고급 주제: MCP 고급 주제에서 보안, 성능, 배포 전략 학습

Capability 협상 상세

MCP의 capability 협상은 클라이언트와 서버가 서로 지원하는 기능을 교환하고, 공통으로 사용 가능한 기능만 활성화하는 메커니즘입니다. 이를 통해 프로토콜의 유연성과 하위 호환성을 동시에 보장합니다.

협상 메커니즘

Capability 협상은 초기화 핸드셰이크 과정에서 자동으로 이루어집니다. 클라이언트가 initialize 요청을 보낼 때 자신이 지원하는 capabilities를 선언하고, 서버가 응답할 때 서버 측 capabilities를 선언합니다. 이후 양측은 상대방이 선언한 capabilities 범위 내에서만 기능을 사용합니다.

Capability 협상 흐름 Client Server initialize (client capabilities) capabilities: tools: {} resources: { subscribe: true } sampling: {} response (server capabilities) capabilities: tools: { listChanged: true } resources: { subscribe: true } prompts: { listChanged: true } 활성화된 기능 (교집합) tools (양측 지원) + resources.subscribe (양측 지원) sampling: 서버 미지원 -> 비활성 | prompts: 클라이언트 미선언 -> 제한적 양측이 모두 선언한 capability만 활성화됨

클라이언트 vs 서버 Capabilities 비교

다음 표는 클라이언트와 서버 각각이 선언할 수 있는 capabilities의 전체 목록입니다. 각 capability는 독립적으로 활성화되며, 상대방이 해당 기능을 지원하지 않으면 해당 기능과 관련된 메서드는 사용할 수 없습니다.

Capability 선언 주체 설명 관련 메서드
tools 서버 도구 기능 제공 tools/list, tools/call
tools.listChanged 서버 도구 목록 변경 시 알림 전송 notifications/tools/list_changed
resources 서버 리소스 기능 제공 resources/list, resources/read
resources.subscribe 서버 리소스 변경 구독 지원 resources/subscribe, resources/unsubscribe
resources.listChanged 서버 리소스 목록 변경 시 알림 notifications/resources/list_changed
prompts 서버 프롬프트 템플릿 제공 prompts/list, prompts/get
prompts.listChanged 서버 프롬프트 목록 변경 시 알림 notifications/prompts/list_changed
logging 서버 로그 메시지 전송 logging/setLevel, notifications/message
sampling 클라이언트 서버가 LLM 샘플링 요청 가능 sampling/createMessage
roots 클라이언트 파일 시스템 루트 정보 제공 roots/list
roots.listChanged 클라이언트 루트 목록 변경 시 알림 notifications/roots/list_changed

Capability 불일치 처리

클라이언트가 서버에서 지원하지 않는 기능을 요청하면, 서버는 -32601 Method not found 에러로 응답합니다. 잘 구현된 클라이언트는 이런 상황을 사전에 방지해야 합니다.

TypeScript
// Capability 확인 후 기능 사용 예제
const serverCaps = client.getServerCapabilities();

// 서버가 resources를 지원하는지 확인
if (serverCaps.resources) {
  const resources = await client.listResources();
  console.log("리소스 목록:", resources);

  // 구독 기능도 지원하는지 추가 확인
  if (serverCaps.resources.subscribe) {
    await client.subscribeResource("file:///config.json");
  } else {
    console.log("서버가 리소스 구독을 지원하지 않습니다.");
  }
} else {
  console.log("서버가 리소스 기능을 지원하지 않습니다.");
}

// 서버가 도구를 지원하는지 확인
if (serverCaps.tools) {
  const tools = await client.listTools();

  // 도구 목록 변경 알림 수신 설정
  if (serverCaps.tools.listChanged) {
    client.on("tools/list_changed", async () => {
      const updatedTools = await client.listTools();
      console.log("도구 목록이 업데이트되었습니다:", updatedTools);
    });
  }
}
Capability 협상 시 주의사항
  • 선언하지 않은 기능 사용 금지: 상대방이 선언하지 않은 capability와 관련된 메서드를 호출하면 에러가 발생합니다.
  • 기능 축소 불가: 초기화 완료 후에는 capabilities를 변경할 수 없습니다. 변경이 필요하면 연결을 끊고 재초기화해야 합니다.
  • 빈 객체의 의미: "tools": {}는 도구 기능을 지원하되, 하위 옵션(예: listChanged)은 비활성이라는 의미입니다.
  • 키 누락의 의미: capabilities 객체에 특정 키가 아예 없으면 해당 기능 자체를 지원하지 않는다는 뜻입니다.

실전 시나리오: Capability 협상 예제

JSON
// 시나리오: 클라이언트가 sampling을 지원하고, 서버가 tools만 제공하는 경우

// 1. 클라이언트 → 서버: initialize 요청
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": { "name": "my-client", "version": "1.0.0" },
    "capabilities": {
      "sampling": {},
      "roots": { "listChanged": true }
    }
  }
}

// 2. 서버 → 클라이언트: initialize 응답
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": { "name": "tools-server", "version": "2.0.0" },
    "capabilities": {
      "tools": { "listChanged": true }
    }
  }
}

// 결과:
// - tools 사용 가능 (서버가 제공)
// - sampling 사용 가능 (클라이언트가 지원, 서버가 요청 가능)
// - resources 사용 불가 (서버가 미제공)
// - prompts 사용 불가 (서버가 미제공)
// - roots 사용 가능 (클라이언트가 제공, 서버가 조회 가능)

프로토콜 버전 관리

MCP는 날짜 기반 버전 관리 체계를 사용하여 프로토콜의 발전과 하위 호환성을 동시에 관리합니다. 버전 협상은 초기화 과정에서 이루어지며, 클라이언트와 서버가 호환 가능한 버전을 합의합니다.

버전 형식

MCP 프로토콜 버전은 YYYY-MM-DD 형식의 날짜 문자열을 사용합니다. 이 형식은 프로토콜 사양이 확정된 날짜를 기반으로 하며, 시맨틱 버저닝과 달리 직관적으로 시점을 파악할 수 있다는 장점이 있습니다.

JSON
// 프로토콜 버전 예시
{
  "protocolVersion": "2024-11-05"   // 2024년 11월 5일 확정 사양
}

// 향후 새로운 버전 예시
{
  "protocolVersion": "2025-03-15"   // 가상의 차기 버전
}

버전 협상 과정

버전 협상은 다음과 같은 절차로 이루어집니다:

  1. 클라이언트가 자신이 지원하는 최신 프로토콜 버전을 initialize 요청에 포함합니다.
  2. 서버는 클라이언트가 제시한 버전을 지원할 수 있는지 확인합니다.
  3. 서버가 해당 버전을 지원하면, 동일한 버전 또는 호환 가능한 버전으로 응답합니다.
  4. 서버가 해당 버전을 지원하지 못하면, 자신이 지원하는 버전을 응답합니다.
  5. 클라이언트는 서버가 응답한 버전을 수용할 수 있는지 결정합니다.
JSON
// 시나리오 1: 버전 일치 (정상 케이스)

// 클라이언트 → 서버
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": { "name": "claude-desktop", "version": "1.5.0" },
    "capabilities": {}
  }
}

// 서버 → 클라이언트 (동일 버전 수용)
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": { "name": "my-server", "version": "1.0.0" },
    "capabilities": {}
  }
}
JSON
// 시나리오 2: 버전 불일치 시 다운그레이드

// 클라이언트가 최신 버전을 요청
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-15",
    "clientInfo": { "name": "new-client", "version": "2.0.0" },
    "capabilities": {}
  }
}

// 서버가 구 버전만 지원하는 경우
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": { "name": "legacy-server", "version": "0.9.0" },
    "capabilities": {}
  }
}

// 클라이언트는 서버 버전을 수용하고 해당 버전의 기능만 사용
// 또는 호환 불가 시 연결을 거부할 수 있음

하위 호환성 규칙

MCP 프로토콜은 다음과 같은 하위 호환성 원칙을 따릅니다:

하위 호환성 보장 원칙
변경 유형 호환성 예시
새 메서드 추가 하위 호환 새로운 선택적 메서드는 기존 클라이언트에 영향 없음
새 선택적 필드 추가 하위 호환 기존 메시지에 선택적 필드 추가
새 capability 추가 하위 호환 새 capability는 선언하지 않으면 비활성
기존 메서드 변경 비호환 필수 필드 변경이나 동작 변경은 새 버전 필요
메서드 제거 비호환 기존 메서드 제거는 새 버전 필요

구현 시 버전 관리 권장사항

TypeScript
// 서버 측 버전 관리 구현 예제
const SUPPORTED_VERSIONS = ["2024-11-05"];
const LATEST_VERSION = "2024-11-05";

function handleInitialize(request: InitializeRequest): InitializeResult {
  const clientVersion = request.params.protocolVersion;

  // 클라이언트 요청 버전을 지원하는지 확인
  if (SUPPORTED_VERSIONS.includes(clientVersion)) {
    return {
      protocolVersion: clientVersion,
      serverInfo: { name: "my-server", version: "1.0.0" },
      capabilities: { tools: {} }
    };
  }

  // 지원하지 않으면 최신 지원 버전으로 응답
  return {
    protocolVersion: LATEST_VERSION,
    serverInfo: { name: "my-server", version: "1.0.0" },
    capabilities: { tools: {} }
  };
}
버전 관리 모범 사례
  • 서버와 클라이언트 모두 최신 프로토콜 버전을 지원하되, 이전 버전과의 호환성도 유지하세요.
  • 버전 불일치 시 우아하게(gracefully) 처리하고, 사용자에게 명확한 에러 메시지를 제공하세요.
  • 프로토콜 버전 변경 로그를 관리하여, 각 버전에서 추가/변경/제거된 기능을 추적하세요.
  • 테스트 시 다양한 프로토콜 버전 조합을 검증하여 호환성을 확인하세요.

핵심 정리

  • MCP 개념의 핵심 개념과 흐름을 정리합니다.
  • MCP란 무엇인가?를 단계별로 이해합니다.
  • 실전 적용 시 기준과 주의점을 확인합니다.

실무 팁

  • 입력/출력 예시를 고정해 재현성을 확보하세요.
  • MCP 개념 범위를 작게 잡고 단계적으로 확장하세요.
  • MCP란 무엇인가? 조건을 문서화해 대응 시간을 줄이세요.