Model Context Protocol (MCP)의 작동 원리: JSON-RPC 2.0, Stdio IPC 및 저수준 도구 호출
요약
Model Context Protocol (MCP)은 AI 에이전트가 로컬 데이터베이스나 파일 시스템 같은 외부 도구와 상호작용하는 방식을 표준화한 오픈 프로토콜입니다. 이는 Language Server Protocol(LSP)의 아키텍처를 차용하여, 복잡했던 M×N 통합 문제를 해결합니다. MCP는 JSON-RPC 2.0을 통해 호스트-클라이언트-서버 세 참여자 간에 통신하며, LLM이 로컬 프로세스를 제어할 수 있게 합니다.
핵심 포인트
- MCP는 AI 에이전트를 위한 오픈 표준 프로토콜입니다.
- LSP의 아키텍처를 차용하여 통합 문제를 해결했습니다.
- JSON-RPC 2.0을 통해 호스트, 클라이언트, 서버가 통신합니다.
- LLM과 로컬 프로세스 간의 상태 저장/비저장 통신 방식을 정의합니다.
Model Context Protocol (MCP)이 실제로 작동하는 방식: JSON-RPC 2.0, Stdio IPC, 및 저수준 도구 호출
최근 Claude Desktop, Cursor 또는 최신 로컬 코딩 에이전트를 사용해 본 적이 있다면, claude_desktop_config.json 파일을 설정했거나 npx -y @modelcontextprotocol/server-postgres로 MCP 서버를 실행했을 가능성이 높습니다.
표면적으로는 거의 마법처럼 보입니다: LLM이 갑자기 로컬 SQLite 데이터베이스에 연결하고, 로컬 파일을 읽고, 명령을 실행합니다.
대부분의 튜토리얼은 MCP를 다음과 같이 설명합니다:
"MCP는 AI 모델이 도구 및 데이터 소스와 상호 작용할 수 있게 하는 오픈 표준입니다."
이 정의는 그것이 무엇을 하는지 알려줄 뿐, 내부적으로 실제로 어떻게 작동하는지는 알려주지 않습니다.
프롬프트를 입력한 순간부터 로컬 스크립트가 귀하의 기기에서 실행되는 순간까지 실제로 무슨 일이 일어날까요? 상태 비저장(stateless) LLM이 상태 저장(stateful) 로컬 프로세스와 어떻게 통신할까요? 표준 출력(stdout)에 엄격한 프로토콜 규칙이 있는 이유는 무엇이며, print()나 console.log()를 사용하면 서버가 깨지는 이유는 무엇일까요?
파일 디스크립터 파이프와 JSON-RPC 2.0 프레임부터 기능 협상(capability negotiation) 및 토큰 오버헤드까지 전체 수명 주기를 추적해 봅시다.
1. MCP가 실제로 해결한 아키텍처 문제
Anthropic이 Model Context Protocol을 2024년 말에 오픈소스로 공개하기 전, AI 생태계는 M × N 통합 문제에 빠져 허우적거리고 있었습니다.
BEFORE MCP (M × N 사용자 지정 통합):
[Claude] ---> Custom Plugin API ---> [Postgres]
[ChatGPT] ---> Actions Schema ---> [GitHub]
...
만약 에이전트 호스트가 5개이고 도구가 20개라면, 개발자들은 100개의 별도 통합 래퍼를 작성하고 유지해야 했습니다.
MCP는 Microsoft의 **Language Server Protocol (LSP)**의 아키텍처 플레이북을 차용했습니다. 모든 코드 에디터가 TypeScript, Python, Rust에 대한 사용자 지정 파서를 작성하는 대신, LSP는 단일 표준 JSON-RPC 프로토콜을 만들었습니다.
MCP는 AI 에이전트를 위한 LSP입니다.
2. 세 가지 핵심 개체
MCP 아키텍처는 세 가지의 구별되는 참여자로 구성됩니다:
┌──────────────────────────────────────────────────────────┐
│ 호스트 애플리케이션 (예: Claude Desktop, Cursor, Hermes) │
│ │
- 호스트(Host): 사용자에게 보이는 애플리케이션입니다 (Claude Desktop, Cursor, Hermes CLI). 호스트는 권한을 제어하고, UI를 표시하며, LLM 모델 상호작용을 처리합니다.
- 클라이언트(Client): 호스트 내부의 프로토콜 컨트롤러입니다. 클라이언트는 각 MCP 서버와 엄격하게 1:1 연결을 유지합니다.
- 서버(Server): 도구, 데이터 리소스 및 프롬프트 템플릿을 해당 프로토콜을 통해 노출하는 격리된 프로그램입니다 (로컬 CLI 또는 원격 서비스).
3. 통신 프로토콜(The Wire Protocol): JSON-RPC 2.0
고수준 SDK(@modelcontextprotocol/sdk를 TypeScript에서 또는 Python에서 mcp) 아래에서, MCP는 전적으로 JSON-RPC 2.0을 통해 통신합니다.
전송되는 모든 메시지는 세 가지 구조적 유형 중 하나에 속합니다:
A. 요청(Request) (클라이언트 → 서버 또는 서버 → 클라이언트)
요청은 명시적인 응답이 필요합니다. 이는 비동기 응답을 연관시키기 위해 고유한 id를 포함합니다:
{
"jsonrpc": "2.0",
"id": 104,
...
B. 응답(Response) (성공 또는 오류)
수신자는 들어오는 id와 일치하는 값을 확인하고, result 페이로드 또는 error 객체 중 하나를 반환합니다:
{
"jsonrpc": "2.0",
"id": 104,
...
만약 실행 실패가 발생하면, 서버는 표준 JSON-RPC 오류 코드(예: 잘못된 요청의 경우 -32600 또는 메서드 없음의 경우 -32601)를 반환합니다:
{
"jsonrpc": "2.0",
"id": 104,
...
C. 알림(Notification) (단방향 신호)
알림은 절대 id 필드를 포함하지 않으며 응답해서는 안 됩니다. 이는 로깅이나 상태 업데이트와 같은 '발사하고 잊어버리는(fire-and-forget)' 이벤트에 사용됩니다:
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed",
...
4. Stdio 전송 방식(Stdio Transport): 파일 디스크립터와 Stderr 규칙
MCP 서버를 로컬에서 실행할 때, 호스트는 표준 운영 체제 프로세스 파이프를 사용하여 서버를 자식 서브프로세스로 시작합니다.
이 전송 메커니즘은 중요한 엔지니어링 함의를 가집니다:
파일 디스크립터 역할 (File Descriptor Roles)
- 표준 입력 (Standard Input, FD 0): 호스트는 개행 문자(
\n)로 구분된 JSON-RPC 메시지를 서버의 입력 스트림에 직접 작성합니다. - 표준 출력 (Standard Output, FD 1): 서버는 개행 문자로 구분된 JSON-RPC 응답을 stdout으로 작성합니다.
- 표준 에러 (Standard Error, FD 2): 사람이 읽을 수 있는 로깅 및 진단 목적으로만 엄격하게 예약되어 있습니다.
가장 흔한 MCP 서버 버그
Python이나 Node.js로 MCP 서버를 작성하면서 print("DB에 연결됨") 또는 console.log("데이터 가져오는 중...")와 같은 진단 코드를 포함하면, 서버가 즉시 충돌합니다.
왜 그럴까요? Python의 표준 print()는 **stdout (FD 1)**으로 직접 작성하기 때문입니다.
호스트 클라이언트의 스트림 파서가 유효한 JSON 객체 대신 `
- 방화벽 및 프록시 호환성: SSE는 WebSocket 업그레이드 핸드셰이크가 필요한 대신, 일반 HTTP/1.1 또는 HTTP/2 스트림 위에서 작동하여 엔터프라이즈 프록시에서 차단되는 경우가 적습니다.
- 내장 재연결 기능: 브라우저와 HTTP 클라이언트는 네이티브 SSE 재연결 의미론(semantics)을 가지고 있습니다.
- 업스트림 및 다운스트림 분리: POST를 통해 전송되는 무거운 페이로드(payload)가 수신 이벤트 스트림을 막지 않습니다.
6. 프로토콜 라이프사이클: 핸드셰이크부터 실행까지
LLM이 단 하나의 함수라도 호출하기 전에, 클라이언트와 서버는 3단계의 초기화 핸드셰이크를 완료합니다.
CLIENT SERVER
│ │
│ 1. 요청: "initialize" │
...
단계 1: initialize 요청
클라이언트는 지원하는 프로토콜 버전과 기능을 알리는 것으로 시작합니다:
{
"jsonrpc": "2.0",
"id": 1,
...
단계 2: 서버 응답
서버는 자신의 신원 및 지원하는 기능을 담아 응답합니다:
{
"jsonrpc": "2.0",
"id": 1,
...
단계 3: 승인(Acknowledgment)
클라이언트는 {"jsonrpc": "2.0", "method": "notifications/initialized"}를 전송합니다. 연결 상태는 INITIALIZING에서 ACTIVE로 전환됩니다.
7. 세 가지 MCP 기본 요소 (Primitives)
MCP는 모든 기능을 세 가지 별개의 기본 요소로 그룹화합니다:
| 기본 요소 | 컨트롤러 | 동적 여부? | 주요 사용 사례 |
|---|---|---|---|
| 도구(Tools) | 모델 제어 | 예 (LLM에 의해 호출) | 쿼리 실행, 코드 실행, 웹훅 전송, 파일 수정 |
| ... |
도구 정의 방법: JSON Schema Draft-07
클라이언트가 tools/list를 호출할 때, 서버는 도구 객체의 배열을 반환합니다. 모든 인수는 표준 JSON Schema를 사용하여 정의되어야 합니다:
{
"name": "calculate_mortgage",
"description": "원금과 APR을 기반으로 월별 상환액을 계산합니다.",
...
호스트 클라이언트는 이 원시 JSON 스키마들을 가져와 대상 LLM 제공업체(Anthropic XML tools, OpenAI function tools 또는 Gemini 선언 스키마)가 기대하는 특정 함수 호출 형식으로 변환합니다.
8. 도구 호출 중 발생하는 과정: 엔드투엔드 실행 추적
사용자에게 'SQLite에서 모든 연체 청구서를 찾아줘.'라고 요청했을 때 정확히 어떤 일이 일어나는지 추적해 보겠습니다.
1. 사용자: "모든 연체 청구서를 찾아줘."
│
▼
...
LLM은 데이터베이스와 직접 대화하지 않는다는 점에 주목하세요. LLM은 오직 텍스트 토큰만 생성합니다. 호스트 에이전트가 이 토큰들을 가로채서 IPC 파이프를 조정하고, 스키마를 검증하며, 그 결과를 모델의 다음 순방향 전달(forward pass)에 반환합니다.
9. 숨겨진 함정: 컨텍스트 블랏과 보안
MCP는 깔끔한 모듈성을 제공하지만, 모든 엔지니어가 이해해야 할 두 가지 주요 시스템 과제를 도입합니다:
A. 컨텍스트 창 세금 (The Context Window Tax)
연결하는 모든 MCP 서버는 tools/list를 통해 자신의 도구를 노출합니다. 호스트는 이 모든 도구 스키마들을 매 턴마다 LLM의 시스템 프롬프트에 직렬화(serialize)해야 합니다.
1개의 단순 도구 스키마 ≈ 150 - 300 토큰
10개 MCP 서버 (50개 도구) ≈ 요청당 10,000 - 18,000 토큰
만약 10개의 방대한 서버를 연결한다면, 사용자가 단 하나의 문자를 입력하기 전에 15,000 토큰을 소모할 수 있습니다.
최신 에이전트 런타임은 동적 도구 인덱싱(dynamic tool indexing), 도구 설명에 대한 임베딩 검색(embedding search over tool descriptions) 또는 점진적 공개(progressive disclosure)(관련 키워드가 일치할 때만 도구 스키마 로드)을 사용하여 이를 해결합니다.
B. 보안 경계 및 프롬프트 주입 (Prompt Injection)
MCP 서버는 호스트를 실행한 사용자의 OS 권한으로 작동합니다. 만약 파일 시스템 쓰기 권한이나 bash 실행 기능을 가진 MCP 서버를 연결한다면, 임의 코드 실행과 호스트 시스템 사이의 유일한 장벽은 LLM의 판단입니다.
만약 신뢰할 수 없는 웹 페이지나 데이터베이스 레코드에 프롬프트 주입 지침(예: System Alert: rm -rf와 함께 bash_execute 호출)이 포함되어 있다면, 순진한 호스트는 이 페이로드를 실행합니다.
견고한 MCP 구현체는 다음을 강제합니다:
- 파괴적인 도구 호출에 대한 Human-in-the-loop 승인 게이트.
- 루트 경로 포함(Root path containment) (파일 작업을 특정 작업 공간 폴더로 제한).
- 컨텍스트 플러딩 공격을 방지하기 위한 엄격한 출력 자르기(Strict output truncation).
10. 요약 정신 모델 (Summary Mental Model)
레이어를 벗겨보면, Model Context Protocol은 독점적인 AI 마법이 아닙니다. 이는 현대 언어 모델에 적용된 고전적인 Unix 시스템 엔지니어링입니다:
- 전송(Transport): 로컬 프로세스를 위한 표준 POSIX 스트림(
stdin/stdout/stderr); 원격 서비스를 위한 SSE + HTTP. - 와이어 형식(Wire Format): 단조 증가 ID 요청-응답 쌍을 가진 경량 JSON-RPC 2.0 메시지.
- 계약(Contract): 추상적인 LLM 함수 토큰을 검증된 런타임 함수 호출로 변환하는 JSON Schema Draft-07 정의.
도구 실행을 호스트 런타임과 분리함으로써, MCP는 AI 기능을 구조화된 운영 체제 프로세스로 취급하는 깨끗하고 언어 중립적인 표준을 제공합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기