먼저 결론부터: MCP(Model Context Protocol)는 Function Calling의 다른 이름이 아니고, 모델도 아닙니다. AI 앱(Host)과 외부 도구 프로세스(MCP Server) 사이의 개방 프로토콜이며, 메시지는 JSON-RPC 2.0입니다. 모델은 여전히 각 벤더의 Tool Calling / Function Calling을 씁니다. Host가 tools/list를 모델의 tools 배열로 옮기고, tool_calls를 tools/call로 옮깁니다. 이 세 레이어가 겹쳐야 2026년 Agent가 도구를 호출하는 흔한 모습입니다.
이 글은 2026년 9월 7일 기준입니다. 현재 규격은 2026-07-28입니다. 프로토콜 세션 없음, initialize 핸드셰이크 없음, 모든 요청이 _meta를 실으며, 능력 발견은 server/discover입니다. 8월에 올린 Agent JSON 데이터 흐름은 여전히 옛 initialize 예시를 씁니다. 이 가이드를 현재 기준으로 읽으세요. 「Server 코드를 바꿔야 하나?」는 MCP 2026 마이그레이션 가이드를 보세요.
MCP란 무엇인가
Model Context Protocol은 AI 애플리케이션이 외부 컨텍스트를 발견하고, 읽고, 호출하는 방식을 정한 개방 표준입니다. Anthropic이 2024년 11월에 공개했고, 이후 거버넌스는 Agentic AI Foundation으로 옮겼습니다. 컨텍스트를 어떻게 교환하는지만 규정합니다. 어떤 모델을 쓸지, 다단계 Agent를 어떻게 오케스트레이션할지, 비즈니스 코드를 어떻게 쓸지는 규정하지 않습니다.
USB-C로 기억하세요. 소켓 모양은 같고, 뒤에 디스크·디스플레이·전원이 붙는지는 프로토콜이 관여하지 않습니다. MCP가 표준화하는 것은 Host ↔ Server 소켓입니다. 파일시스템, GitHub, 내부 주문 API, 이 사이트 같은 JSON 검증 서비스는 모두 Server일 뿐입니다.
| 표현 | 실제 의미 | 흔한 오해 |
|---|---|---|
| MCP | Host와 도구 프로세스 사이의 JSON-RPC 프로토콜 | 모델, Agent 프레임워크, 또는 OpenAI의 Tools API |
| MCP Server | tools / resources / prompts를 노출하는 프로그램 | 반드시 공개 인터넷에 두거나, REST API를 대체해야 한다 |
| MCP Client | Host 안에서 Server 하나와 연결을 관리하는 객체 | 언어 모델 그 자체 |
| MCP Host | Cursor, VS Code, Claude Desktop 같은 AI 앱 | MCP 규격 또는 SDK |
레이어는 둘입니다. 데이터 레이어는 JSON-RPC 2.0(메서드, 파라미터, 오류 코드, 알림)이고, 전송 레이어는 그 JSON 프레임을 옮기는 방법입니다——같은 머신에서는 stdio, 원격에서는 Streamable HTTP. 전송을 바꿔도 메시지 형태는 그대로입니다. 그래서 MCP를 디버깅할 때는 먼저 「봉투는 JSON-RPC, 업무 페이로드도 흔히 JSON」을 나눕니다.
Host, Client, Server
규격의 삼각형을 일상 「클라이언트 / 서버」 말과 섞기 쉽습니다:
- Host: 사용자가 연 AI 앱. Client를 만들고, 도구 Schema를 모델에 넘기고, 실행 전 인가와 검증을 하며, 결과를 대화에 다시 씁니다.
- Client: Host 안의 연결 객체 하나. Server 하나당 Client 하나. VS Code가 파일시스템과 Sentry에 동시에 붙으면 런타임 Client는 둘입니다.
- Server: 컨텍스트를 제공하는 프로그램. Host와 같은 머신(stdio)이거나 다른 머신(Streamable HTTP)일 수 있습니다. 「Server」는 역할이지, 공용 호스트명이 있어야 한다는 뜻은 아닙니다.
모델은 이 삼각형 밖에 있습니다. GPT-5.5, Claude 4.8, Gemini 3.7이 보는 것은 Host가 옮긴 tools 배열입니다. JSON-RPC도 보이지 않고, Mcp-Session-Id도 보이지 않습니다(2026-07-28에서 세션 헤더는 사라졌습니다). 「모델이 MCP를 말한다」는 마케팅 문구입니다. 엔지니어링에서는 항상 가운데에 Host가 있습니다.
JSON-RPC 2.0 읽는 법
JSON-RPC는 JSON으로 원격 프로시저를 호출하는 약속이며, REST보다 「함수 하나를 호출한다」에 가깝습니다. MCP가 고른 이유는 메서드 이름이 안정적이고(tools/list, tools/call), 요청 / 응답 / 알림 삼분법이 분명하며, 봉투 전체가 모델 친화적인 JSON이기 때문입니다.
| 필드 | 누가 쓰나 | 의미 |
|---|---|---|
jsonrpc | 모든 메시지 | 항상 "2.0" |
id | 요청과 응답 | 짝을 맞출 때 씀; 알림에는 id가 없음 |
method | 요청 / 알림 | 예: tools/call, server/discover |
params | 요청 | 파라미터 객체; 2026-07-28부터 흔히 _meta를 포함 |
result / error | 응답 | 둘 중 하나; 성공은 result, 실패는 error |
규격 2026-07-28의 tools/call은 이렇게 보입니다. 기억하세요: 핸드셰이크 없음, 세션 헤더 없음. 버전과 Client 신원은 _meta에 있으므로, 어떤 Server 인스턴스든 이 프레임을 처리할 수 있습니다.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "validate_json",
"arguments": {
"payload": {"orderId": "A-1001", "total": 42.5},
"schemaId": "order.v1"
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "json-toolbox-host",
"version": "1.0.0"
}
}
}
}
성공 응답도 같은 봉투입니다. 업무 결과는 result.content에 있고, 흔히 type: "text"이며, 그 텍스트 자체가 JSON 문자열일 수 있습니다——바깥은 프로토콜, 안은 페이로드. 디버깅할 때는 먼저 id가 요청과 맞는지 보고, 그다음 안쪽 객체를 Schema로 검증하세요.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "{\"valid\":true,\"schemaId\":\"order.v1\"}"
}
]
}
}
실패는 JSON-RPC error 객체를 씁니다: code, message, 선택적 data. 2026-07-28은 「리소스 없음」을 MCP 전용 -32002에서 표준 -32602(Invalid Params)로 바꿨습니다. 옛 오류 코드를 그대로 매칭하는 Client는 놓칩니다. 알림(notification)에는 id가 없고 응답도 기다리지 않습니다——예를 들어 도구 목록 변경입니다.
Tools, Resources, Prompts
Server는 세 종류의 프리미티브를 노출할 수 있습니다. Agent가 매일 쓰는 것은 Tools입니다. 나머지 둘은 건너뛰기 쉽지만, 모델이 한 턴 추측하는 비용을 자주 줄입니다.
| 프리미티브 | 발견 | 사용 | 용도 |
|---|---|---|---|
| Tools | tools/list | tools/call | 실행 가능한 동작: DB 조회, API 호출, 파일 쓰기, JSON 검증 |
| Resources | resources/list | resources/read | URI로 컨텍스트 읽기: Schema 파일, 로그 조각, 설정 |
| Prompts | prompts/list | prompts/get | 재사용 가능한 프롬프트 템플릿, 선택적 파라미터 |
도구의 핵심은 name, description, inputSchema입니다. inputSchema는 JSON Schema입니다(2026-07-28부터 2020-12; 루트는 여전히 type: "object"여야 하고, oneOf / $ref / $defs는 허용). 선택적 outputSchema가 반환 형태를 제약합니다. Host는 inputSchema를 거의 1:1로 모델 API의 parameters / input_schema에 넣습니다.
{
"name": "validate_json",
"title": "Validate JSON",
"description": "Check a JSON payload against a named schema. Returns valid and errors.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"payload": { "type": "object", "description": "Already parsed JSON object, not a raw string" },
"schemaId": { "type": "string", "description": "Stable schema id such as order.v1" }
},
"required": ["payload", "schemaId"]
}
}
Resources는 「먼저 읽고 생각하기」에 맞습니다. schema://order.v1을 컨텍스트에 넣는 편이, 모델에게 대화에서 200줄 Schema를 외우게 하는 것보다 쌉니다. Prompts는 팀이 고정한 시작 프롬프트에 맞습니다. Roots, Sampling, Logging은 2026-07-28에서 폐기되었습니다. 워크스페이스 경로는 도구 인자나 리소스 URI로 넘기세요. Server는 Host에 컴플리션을 요청하지 마세요. 로그는 stderr 또는 OpenTelemetry로 보내세요.
Tool Calling과 어떻게 겹치나
세 이름이 자주 하나로 뭉개집니다. 데이터 흐름에서 같은 레이어가 아닙니다——Agent JSON 데이터 흐름 글이 홉마다 추적합니다. 여기선 매핑만 적습니다:
| 레이어 | 양쪽 | 전형적인 메시지 |
|---|---|---|
| Function Calling / Tool Calling | 모델 API ↔ Host | tools[] + tool_calls.arguments |
| MCP | Host ↔ Server | JSON-RPC tools/list, tools/call |
| JSON Schema | 계약이지 전송이 아님 | inputSchema / parameters |
Function Calling은 초기 OpenAI 이름이고, Tool Calling은 이후 더 일반적인 말입니다(Claude tools, Gemini Function Calling, OpenAI Tools API). 개발자에게는 같은 흐름입니다. Host가 Schema를 모델에 보내고, 모델이 JSON 인자를 담은 호출을 반환하고, Host가 실행한 뒤 JSON 결과를 대화에 다시 넣습니다.
MCP는 이 레이어를 대체하지 않습니다. Host가 프로세스 안에서 로컬 함수를 Tool Calling만으로 부르는 것도 완전히 유효합니다. MCP가 있으면 도구가 발견 가능하고, 프로세스를 넘고, Host를 바꿔 재사용할 수 있는 Server가 됩니다. 기업 Agent는 거의 항상 두 레이어를 겹칩니다. 스크립트와 데모는 종종 MCP를 건너뜁니다.
매핑에서 빠지기 쉬운 두 곳: 모델 API는 arguments를 자주 문자열로 주고, MCP의 params.arguments는 객체입니다. 그리고 tools/list의 name은 모델과 tools/call까지 그대로 가야 합니다——중간에 「더 친근한」 별명을 만들지 마세요. 진짜 tools/call 전에 검증하세요. Tool Calling과 JSON Schema 검증을 보세요.
한 번의 완전한 도구 호출
사용자가 말합니다: 「이 주문 JSON을 order.v1로 검증해 줘.」 2026-07-28에서 엔드투엔드는 이렇습니다:
- Host → Server:
server/discover(캐시 가능)로 tools가 있는지 확인하거나, 다음 요청을 보내고 버전 오류가 나면 재시도합니다. - Host → Server:
tools/list가inputSchema가 있는 목록을 반환합니다. 결과에ttlMs/cacheScope가 붙을 수 있습니다. - Host → 모델: 목록을
tools[].parameters로 매핑합니다(여전히 JSON Schema). - 모델 → Host:
tool_calls,name은validate_json,arguments는 흔히 문자열화 JSON입니다. - Host 검증:
JSON.parse한 뒤inputSchema로 검사합니다. 실패하면 오류를 tool 결과로 쓰고, 실제 Server는 건드리지 않습니다. - Host → Server:
tools/call,arguments는 객체,_meta에 프로토콜 버전. - Server → Host:
result.content; Host는 필요하면outputSchema로 한 번 더 검사합니다. - Host → 모델:
role: toolJSON 문자열. 모델이 사용자에게 답하거나 다음 도구 턴을 시작합니다.
사용자 자연어
│
▼
Host ──JSON Schema──► LLM Tool Calling
│ │
│ ▼
│ arguments JSON
▼ │
MCP JSON-RPC ◄──── 검증 통과 후에만 tools/call
│
▼
result JSON ──► tool message ──► 모델의 최종 답변
원격 전송에서는 HTTP 헤더에 MCP-Protocol-Version, Mcp-Method, Mcp-Name이 있어야 하고, body와 일치하지 않으면 Server가 거절해야 합니다. 로드 밸런서는 JSON을 뜯지 않고 헤더만 보고 라우팅할 수 있습니다. 로컬 stdio에는 이 헤더가 없습니다. JSON-RPC 메서드 이름은 같습니다.
2026-07-28에서 기억할 것
7월 규격은 출시 이후 가장 큰 개정이며, 2026년 7월 28일이 정식 공개일입니다. 「MCP란 무엇인가」에는 아래만 기억하세요. Server 코드를 바꿀지는 여전히 마이그레이션 글의 결정 트리입니다.
- 핸드셰이크 없음, 프로토콜 세션 없음:
initialize/initialized와Mcp-Session-Id는 사라졌습니다. 모든 요청이 그 자체로 완결됩니다. 애플리케이션 상태는basket_id같은 명시적 인자를 보통 파라미터로 이어 붙이세요. 전송 레이어가 기억해 주길 기대하지 마세요. - 발견은
server/discover: 선택 사항이지만, 한 번에 지원 버전, capabilities, serverInfo를 받습니다. 목록 결과에ttlMs가 붙습니다. 긴 SSE 스트림만이 도구 변경을 아는 방법은 아닙니다. - Schema는 JSON Schema 2020-12: 입력 루트는 여전히 object이고, 조합과 참조는 허용됩니다. 외부
$ref를 자동으로 풀지 마세요. 출력 Schema는 이제 object로만 제한되지 않습니다. - Roots / Sampling / Logging은 폐기: 1년 유예 기간 안에서는 메서드가 아직 동작합니다. 새 Server는 Host에 컴플리션을 요청하는 Sampling을 구현하지 마세요.
- Extensions: Tasks와 MCP Apps는 공식 확장이지, 코어 필수 구현이 아닙니다. 긴 작업은 task handle +
tasks/get을 쓰세요. 자체 세션을 만들지 마세요.
아직 2025-11-25를 쓰는 Host / Server는 initialize를 계속 씁니다. 버전이 섞이면 협상된 protocolVersion을 따르세요. 이 글의 세션 없는 프레임을 옛 Server에 보내지 마세요. 무엇을 설치할지는 2026 MCP Server 순위를 보세요.
지금 할 일
- 코드보다 먼저 세 레이어를 그리세요: 모델 API의 Tool Calling, Host 오케스트레이션, MCP Server. 스크립트는 앞 두 레이어에서 멈춰도 됩니다. IDE를 넘어 도구를 재사용할 때 Server를 씁니다.
- 공식 SDK를 쓰고, JSON-RPC 프레임을 손수 짜지 마세요:
@modelcontextprotocol/sdk와 다른 언어 공식 패키지가 이미 발견, 전송, 오류 코드를 처리합니다. 손수 짠 SSE나 비공개 필드는 마이그레이션 글에서 「코드를 바꿔야 한다」의 전형입니다. inputSchema를 단독으로 검증할 수 있는 계약으로 쓰세요:additionalProperties: false,required, 열거형, 길이 상한. 모델은 필드를 빠뜨리고 숫자를 문자열로 씁니다. 실행 전에 같은 Schema로 한 번 막으세요.- 로컬은 stdio, 원격은 Streamable HTTP: 개인 디버깅에 HTTP는 필요 없습니다. 팀 공유, 다중 클라이언트, 게이트웨이를 지날 때 원격 전송을 올리고, OAuth와 최소 권한을 더하세요.
- 목록은 캐시하고, 결과는 자르세요:
ttlMs를 지키세요. 스택 원문을 모델에 다시 붓지 마세요. 창이 커도 오염된 JSON은 안전하지 않습니다——1M 토큰 컨텍스트 윈도우를 보세요. - 실제 Server를 치기 전에 브라우저에서 픽스처를 맞추세요:
inputSchema, 좋은 / 나쁜 arguments, Server 반환 샘플을 JSON으로 저장하고, 이 사이트에서 검증과 Diff를 하세요. 아무것도 업로드되지 않습니다. REST 계약을 테스트하는 것과 같은 습관입니다.
FAQ
MCP는 모델인가, 프레임워크인가?
둘 다 아닙니다. MCP는 Host와 외부 도구 프로세스 사이의 개방 프로토콜입니다. 메시지는 JSON-RPC 2.0입니다. 모델은 여전히 벤더 API에서 오고, 오케스트레이션은 여전히 Host / Agent 런타임에 있습니다. 「MCP 모델」이란 것은 없습니다.
이미 Tool Calling이 있으면 MCP가 필요한가?
도구가 프로세스 안에 있고 Host에 하드코딩되어 있으면 Tool Calling만으로 충분합니다. 앱을 넘어 재사용하거나, 프로세스 격리가 필요하거나, 도구를 동적으로 발견해야 할 때 MCP를 더하세요. 2026년 IDE Agent는 보통 두 레이어를 모두 씁니다. 한 번 쓰는 CLI 스크립트에는 흔히 MCP가 없습니다.
MCP는 JSON-RPC인가, REST인가?
데이터 레이어는 JSON-RPC 2.0이지, 「도구마다 HTTP 경로 하나」가 아닙니다. 원격 전송은 Streamable HTTP를 쓸 수 있지만, body는 여전히 JSON-RPC 객체이고, 메서드는 method와 Mcp-Method 헤더 양쪽에 있습니다. MCP를 REST 리소스처럼 쪼개지 마세요.
2026-07-28 이후에도 initialize를 써야 하나?
새 규격에는 initialize / initialized도 없고 Mcp-Session-Id도 없습니다. 버전과 Client 신원은 모든 요청의 _meta에 넣습니다. 2025-11-25 Server만 상대하면 옛 핸드셰이크를 유지하세요. 협상된 protocolVersion을 따르고, 봉투를 섞지 마세요.
MCP가 OpenAPI를 대체할까?
아닙니다. OpenAPI는 HTTP API를 설명하고, MCP는 Agent 런타임이 도구를 발견하고 호출하는 방식을 설명합니다. 흔한 패턴은 REST 서비스에 OpenAPI를 유지하고, 경로를 tools/call로 매핑하는 얇은 MCP Server를 씌우는 것입니다.
MCP가 쓰는 JSON을 로컬에서 어떻게 검사하나?
inputSchema, 모델 arguments 샘플, tools/call 반환 샘플을 파일로 저장하세요. 브라우저의 JSON 툴박스에서 구문과 구조를 검사한 뒤, Schema 두 버전을 Diff하세요. 데이터는 브라우저를 떠나지 않습니다.
정리
MCP는 2026년 Agent의 도구 소켓입니다. JSON-RPC 2.0이 Host와 Server 사이에서 발견과 호출을 옮기고, 모델 쪽은 여전히 Tool Calling이며, JSON Schema는 양쪽이 공유하는 계약입니다. 모델도 아니고, 프레임워크도 아니며, OpenAPI를 대체하지도 않습니다. 규격 2026-07-28은 프로토콜에서 세션을 빼냈으므로 요청은 그 자체로 완결되어야 합니다. Tools / Resources / Prompts 세 프리미티브는 바뀌지 않았습니다.
이 가이드는 레이어링만 잡습니다. 홉마다의 바이트 형태는 데이터 흐름 글에, 옛 Server의 코드 변경 여부는 마이그레이션 글에, 어떤 Server를 설치할지는 순위 글에 있습니다. 실제로 연결하기 전에 Schema와 샘플 JSON을 로컬에서 검증하세요——모델은 바꿔도 됩니다. 필드명과 required는 움직이면 안 됩니다.