[Spring AI] MCP(Model Context Protocol)란? 구조와JSON-RPC 2.0 통신
by 우와한개발자
1. MCP(Model Context Protocol)
- LLM을 활용하는 AI 애플리케이션이 외부 도구·데이터·서비스와 표준화된 방식으로 통신하기 위한 오픈 프로토콜
- Anthropic이 2024년 공개했으며, 한마디로 "AI를 위한 USB-C 포트" — 어떤 AI든 MCP 하나로 어떤 도구와도 연결
1) 등장 배경
| 구분 | 내용 |
| 이전 문제 | LLM마다 외부 도구 연결 방식이 제각각 (OpenAI Function Calling, LangChain Tool 등) |
| N×M 문제 | N개의 AI 플랫폼 × M개의 외부 도구(파일, DB, API 등) = N×M개의 커스텀 연동 코드 필요. 도구 스펙이 바뀌면 연동된 모든 AI 플랫폼 코드를 수정해야 함 |
| MCP 해결 | 외부 도구를 MCP 서버로 한 번만 구현하면 MCP를 지원하는 모든 AI에서 재사용 가능 |
| 결과 | N×M → N+M으로 연동 비용 절감. AI가 늘어나도, 도구가 늘어나도 각각 MCP만 구현하면 끝 |
2) MCP의 관심사 분리
- MCP가 기존과 다른 핵심은 AI 시스템에서 뒤섞여 있던 역할을 세 가지로 명확하게 분리한 것이다.
| 개념 | 역할 | 예시 |
| Model | 생성만 담당. 주어진 컨텍스트를 바탕으로 텍스트 생성 | GPT, Claude, Llama |
| Context | 모델이 답변 생성 시 참고하는 모든 정보 | 질문, 대화 히스토리, 파일 내용, DB 조회 결과 |
| Context Provider | 컨텍스트를 생성·조회·관리하는 시스템 요소. MCP Server가 바로 이 역할 | 파일 읽기, DB 조회, 외부 API 호출 |
2. MCP 전체 구조
- MCP는 Host, MCP Client, MCP Server 세 요소로 구성
- Host와 Server가 직접 통신하는 게 아니라 Host 안의 MCP Client가 Server와 통신하는 구조
[Host (Cursor, Claude Desktop)]
│
[MCP Client] ← Host 안에 내장된 통신 모듈
│
│ JSON-RPC 2.0 (STDIO or SSE)
│
[MCP Server] ← 직접 만들거나 공식 서버 사용
│
[외부 시스템 (파일, DB, API 등)]
1) Host / Client / Server
| 요소 | 역할 | 예시 |
| Host | 사용자가 직접 사용하는 AI 클라이언트 애플리케이션 전체 | Cursor, Claude Desktop, Claude Code |
| MCP Client | Host 내부에 내장된 MCP 통신 전담 모듈. Server와 1:1 연결 관리 | Host 내부 컴포넌트 |
| MCP Server | 외부 도구·데이터를 MCP 표준으로 포장해서 노출하는 서버 | 파일시스템 서버, DB 서버 |
2) 세 요소의 관계
- Host → 사용자 질문을 LLM에 전달하고, MCP Client를 통해 서버와 통신
- MCP Client → JSON-RPC 2.0 형식으로 메시지를 만들어 서버에 전달하고 응답을 LLM에 전달
- MCP Server → Tool 실행, 데이터 조회 등 실제 작업을 처리하고 결과 반환
3. JSON-RPC 2.0
1) 개념
- HTTP, WebSocket 등 전송 계층 위에서 동작하는 경량 원격 호출(RPC) 프로토콜
- 요청과 응답을 JSON 형식으로 표현한다.(사람이 읽기 쉽고 디버깅 용이)
- MCP의 모든 통신은 JSON-RPC 2.0 규격을 기반으로 동작한다.
[MCP Client] ──── JSON-RPC 2.0 메시지 ────→ [MCP Server]
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call", ← MCP 명령어
"params": { ... }
}
2) 메시지 종류
(1) Request (요청)
- Client → Server, 반드시 응답을 기대하는 메시지
- id 필드가 있어야 Request로 인식
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "readFile",
"arguments": { "filePath": "src/main/Application.java" }
}
}
(2) Response (응답)
- Server → Client, Request에 대한 결과 반환
- id는 대응되는 Request의 id와 반드시 동일
- 성공이면 result, 실패면 error (둘 중 하나만 존재)
// 성공
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "파일 내용..." }]
}
}
// 실패
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": "filePath 파라미터가 누락되었습니다."
}
}
- 주요 에러 코드
| 에러 코드 | 의미 |
| -32700 | Parse error — JSON 파싱 실패 |
| -32600 | Invalid Request — 잘못된 요청 구조 |
| -32601 | Method not found — 존재하지 않는 메서드 |
| -32602 | Invalid params — 잘못된 파라미터 |
| -32603 | Internal error — 서버 내부 오류 |
(3) Notification (알림)
- 응답을 기대하지 않는 단방향 메시지
- id 필드가 없는 것이 Request와의 차이가 있다.
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": { "progressToken": "abc123", "progress": 50, "total": 100 }
}
3) 메서드 종류 — domain/operation 규칙
- MCP 메서드는 <domain>/<operation> 형식의 네이밍 규칙을 따른다.
- 알림(Notification)은 notifications/<domain>/<operation> 형태
- domain은 작업의 범위, operation은 구체적인 작업을 나타낸다.
| 도메인 | 메서드 | 설명 |
| Roots | roots/list | 클라이언트가 서버로부터 접근 가능한 루트(파일 시스템 경로) 목록 요청 |
| - | notifications/roots/list_changed | 루트 목록이 변경되었음을 알리는 알림 |
| Sampling | sampling/requestCompletion | MCP 서버가 클라이언트(LLM)에게 텍스트 완성(completion)을 요청 |
| Elicitation | elicitation/create | 서버가 사용자 입력이나 확인을 요청하는 상호작용 생성 |
| - | notifications/elicitation/responded | 사용자가 elicitation 요청에 응답했을 때 서버에 알림 |
| Resources | resources/list | 서버가 제공하는 리소스 목록(문서, 파일 등) 조회 |
| - | resources/read | 특정 리소스 내용 읽기 |
| - | resources/subscribe | 특정 리소스의 변경 사항 구독 |
| - | notifications/resources/list_changed | 리소스 목록이 변경되었을 때 알림 |
| - | notifications/resources/updated | 구독 중인 리소스가 변경되었을 때 알림 |
| Prompts | prompts/list | 사용 가능한 프롬프트 템플릿 목록 조회 |
| - | prompts/get | 특정 프롬프트 템플릿 조회 |
| - | notifications/prompts/list_changed | 프롬프트 목록 변경 알림 |
| Tools | tools/list | MCP 서버가 제공하는 툴 목록 요청 |
| - | tools/call | 특정 툴 실행 요청 |
| - | notifications/tools/list_changed | 툴 목록이 변경되었음을 알림 |
| Completion | completion/complete | 사용자가 입력 중인 인자나 참조값에 대한 자동완성 제안 반환 |
| Notifications | notifications/<domain>/list_changed | 특정 도메인(resources, tools, prompts)의 목록이 변경됨을 알림 |
| (공통) | notifications/<domain>/updated | 특정 도메인의 항목이 업데이트됨을 알림 |
4) 통신 흐름
(1) 연결 시점 — initialize
- MCP 클라이언트(Cursor, Claude Desktop 등)가 MCP 서버에 연결하면 아래 메서드들이 순서대로 자동 호출된다
Client Server
│──── initialize ────────────────→ │ 프로토콜 버전, 클라이언트 정보 전달
│←─── initialize result ────────── │ 서버 정보, 보유 기능 목록 반환
│──── notifications/initialized ─→ │ 초기화 완료 알림 (Notification)
│──── tools/list ────────────────→ │ Tool 목록 요청
│←─── tools/list result ────────── │ Tool 목록 + description 전체 반환
(2) 질문 시점 — tools/call까지
- 사용자가 질문하면 LLM이 판단해서 자동으로 Tool을 호출하는 흐름이다.
사용자: "src 폴더에 Java 파일 몇 개야?"
↓
LLM이 연결 시점에 받아둔 Tool 목록(description)과 대조
├── readFile → ❌
├── searchFiles → ✅
└── writeFile → ❌
↓
Client ──── tools/call ──────────────→ Server
{
"method": "tools/call",
"params": {
"name": "searchFiles",
"arguments": { "dirPath": "src", "pattern": "*.java" }
}
}
↓
Server ──── result ──────────────────→ Client
{ "content": [{ "text": "23개" }] }
↓
LLM: "src 폴더에 Java 파일은 총 23개 있습니다."
- LLM이 Tool을 자동으로 선택하고 호출 — 개발자가 직접 호출 코드 작성 불필요
- 선택 기준은 Tool의 description — description이 명확할수록 정확한 Tool 선택한다.
- Cursor 설정에 따라 실행 전 사용자 승인을 묻는 경우도 있다.
'AI > Spring AI' 카테고리의 다른 글
블로그의 정보
우와한개발자 님의 블로그
우와한개발자