Model Context Protocol (MCP) 개요
MCP가 등장한 이유
섹션 제목: “MCP가 등장한 이유”AI 에이전트가 외부 시스템(데이터베이스, API, 파일 시스템)과 통합될 때, 기존에는 각 LLM 제공자마다 서로 다른 방식으로 도구를 정의해야 했습니다. OpenAI 방식으로 작성한 도구는 Anthropic 모델에서 그대로 쓸 수 없고, 새 모델이 나올 때마다 통합 코드를 다시 작성해야 했습니다.
**MCP(Model Context Protocol)**는 Anthropic이 2024년 11월 공개한 오픈 표준입니다. LLM과 외부 도구 사이의 공통 언어를 정의하여, 한 번 만든 MCP 서버를 모든 호환 클라이언트에서 사용할 수 있습니다.
“MCP is to AI tools what USB-C is to device charging.” — 다양한 기기를 하나의 표준 포트로 연결하듯, MCP는 다양한 AI 클라이언트를 하나의 서버 인터페이스로 연결합니다.
기존 방식과의 비교
섹션 제목: “기존 방식과의 비교”기존 function calling 방식에서는 OpenAI와 Anthropic이 서로 다른 스키마를 요구했습니다.
// OpenAI function calling 스키마{ "type": "function", "function": { "name": "read_file", "parameters": { "type": "object", "properties": { "path": { "type": "string" } } } }}
// Anthropic tool_use 스키마{ "name": "read_file", "input_schema": { "type": "object", "properties": { "path": { "type": "string" } } }}MCP를 사용하면 서버에 도구를 한 번 정의하면, 호환 클라이언트는 모델에 관계없이 동일한 서버를 사용할 수 있습니다. 통합 코드를 모델마다 작성하는 N×M 문제가 N+M 문제로 줄어듭니다.
아키텍처 개요
섹션 제목: “아키텍처 개요”┌─────────────────────────────────────────────────────┐│ Host Application ││ (Claude Desktop, Cursor, 커스텀 에이전트 등) ││ ││ ┌──────────────┐ ┌──────────────────────────┐ ││ │ MCP Client 1 │ │ MCP Client 2 │ ││ └──────┬───────┘ └────────────┬─────────────┘ │└─────────│────────────────────────│─────────────────┘ │ JSON-RPC 2.0 │ JSON-RPC 2.0 ▼ ▼┌─────────────────┐ ┌──────────────────────────┐│ MCP Server A │ │ MCP Server B ││ (파일 시스템) │ │ (GitHub, Slack, DB 등) │└─────────────────┘ └──────────────────────────┘각 MCP 클라이언트는 단일 MCP 서버와 1:1로 연결됩니다. 하나의 호스트 애플리케이션은 여러 클라이언트를 통해 여러 서버에 동시에 연결할 수 있습니다.
전송 계층과 메시지 형식
섹션 제목: “전송 계층과 메시지 형식”통신은 JSON-RPC 2.0 프로토콜을 사용합니다. 전송 계층은 연결 방식에 따라 다릅니다.
| 전송 방식 | 사용 시나리오 | 특징 | |----------|------------|------| | Stdio | 로컬 프로세스 | 클라이언트가 서버를 자식 프로세스로 실행 | | Streamable HTTP | 원격·로컬 HTTP 서버 | 단일 MCP 엔드포인트에서 POST/GET을 사용하고, 필요 시 SSE로 서버 메시지를 스트리밍 | | Custom transport | 특수 런타임 | JSON-RPC 메시지와 라이프사이클 요구사항을 보존하는 범위에서 구현 |
JSON-RPC 2.0 메시지 구조
섹션 제목: “JSON-RPC 2.0 메시지 구조”MCP의 모든 통신은 JSON-RPC 2.0 형식을 따릅니다. 요청, 응답, 알림 세 종류의 메시지가 있습니다.
요청 (Request): 클라이언트가 서버에 작업을 요청할 때
{ "jsonrpc": "2.0", "id": "req-001", "method": "tools/call", "params": { "name": "read_file", "arguments": { "path": "/workspace/src/main.ts" } }}응답 (Response): 서버가 요청에 대한 결과를 반환할 때
{ "jsonrpc": "2.0", "id": "req-001", "result": { "content": [ { "type": "text", "text": "import { createServer } from 'http';\n..." } ] }}알림 (Notification): 응답을 기다리지 않는 단방향 메시지 (예: 진행 상황 업데이트)
{ "jsonrpc": "2.0", "method": "notifications/progress", "params": { "progressToken": "task-001", "progress": 45, "total": 100 }}연결 초기화 과정
섹션 제목: “연결 초기화 과정”클라이언트와 서버는 연결 시 핸드셰이크를 통해 서로의 기능을 협상합니다.
클라이언트 → 서버: initialize (프로토콜 버전, 클라이언트 기능)서버 → 클라이언트: initialize 응답 (서버 기능, 사용 가능한 프리미티브 목록)클라이언트 → 서버: initialized (핸드셰이크 완료 확인)---이후 정상 통신 시작MCP 기능 구분
섹션 제목: “MCP 기능 구분”MCP 기능은 누가 기능을 제공하느냐에 따라 나누어 이해하면 쉽습니다. 서버 기능은 MCP 서버가 클라이언트에 제공하는 기능이고, 클라이언트 기능은 서버가 작업 중 호스트 애플리케이션 쪽 능력을 요청하는 기능입니다.
| 구분 | 기능 | 핵심 역할 | |------|------|----------| | 서버 기능 | Tools | 모델이 실행할 수 있는 함수 | | 서버 기능 | Resources | 모델이나 사용자가 읽을 수 있는 데이터 | | 서버 기능 | Prompts | 재사용 가능한 워크플로우 템플릿 | | 클라이언트 기능 | Sampling | 서버가 클라이언트를 통해 LLM 생성을 요청 | | 클라이언트 기능 | Roots | 서버가 접근 가능한 파일·URI 경계를 확인 | | 클라이언트 기능 | Elicitation | 서버가 사용자에게 추가 정보를 요청 |
1. Tools (도구)
섹션 제목: “1. Tools (도구)”LLM이 호출할 수 있는 함수입니다. 타입이 명시된 입력/출력 스키마를 가집니다. 기존의 function calling과 가장 유사한 개념입니다.
{ "name": "read_file", "description": "파일 내용을 읽습니다", "inputSchema": { "type": "object", "properties": { "path": { "type": "string", "description": "읽을 파일 경로" } }, "required": ["path"] }}실제 GitHub MCP 서버의 Tool 예시입니다.
{ "name": "create_pull_request", "description": "GitHub 저장소에 PR을 생성합니다", "inputSchema": { "type": "object", "properties": { "owner": { "type": "string" }, "repo": { "type": "string" }, "title": { "type": "string" }, "head": { "type": "string", "description": "소스 브랜치" }, "base": { "type": "string", "description": "대상 브랜치" }, "body": { "type": "string" } }, "required": ["owner", "repo", "title", "head", "base"] }}2. Resources (리소스)
섹션 제목: “2. Resources (리소스)”LLM이 읽을 수 있는 데이터입니다. 파일, 데이터베이스 레코드, API 응답 등을 URI로 노출합니다. 도구와 달리 부작용(side effect) 없이 데이터를 제공합니다.
{ "uri": "file:///project/src/main.ts", "name": "main.ts", "mimeType": "text/typescript"}Resources는 정적 리소스와 동적 리소스 두 종류로 나뉩니다.
정적: file:///docs/api-spec.md (고정된 파일)동적: db://customers/{id}/orders (파라미터 기반 조회) github://repos/{owner}/{repo}/issues (실시간 데이터)동적 리소스는 URI 템플릿을 통해 파라미터를 받아 실시간으로 데이터를 생성합니다.
3. Prompts (프롬프트)
섹션 제목: “3. Prompts (프롬프트)”재사용 가능한 프롬프트 템플릿입니다. 서버가 클라이언트에게 제안하는 프롬프트 구조로, 일관된 방식으로 특정 작업을 요청할 때 사용합니다.
{ "name": "code_review", "description": "코드 리뷰 요청 템플릿", "arguments": [ { "name": "language", "required": true }, { "name": "focus", "required": false } ]}클라이언트가 이 프롬프트를 호출하면 서버는 채워진 메시지 배열을 반환합니다.
{ "messages": [ { "role": "user", "content": { "type": "text", "text": "다음 TypeScript 코드를 리뷰해 주세요. 보안 측면에 집중하세요:\n\n{{code}}" } } ]}4. Sampling (샘플링)
섹션 제목: “4. Sampling (샘플링)”서버가 클라이언트를 통해 LLM을 호출하는 역방향 메커니즘입니다. MCP 서버가 복잡한 작업 처리 중 LLM의 추론을 활용해야 할 때 사용합니다.
일반적 흐름: 클라이언트 → LLM → 서버 도구 호출Sampling: 서버 → 클라이언트 → LLM → 서버로 결과 반환Sampling 요청 메시지 형식입니다.
{ "method": "sampling/createMessage", "params": { "messages": [ { "role": "user", "content": { "type": "text", "text": "이 함수의 의도를 한 문장으로 설명해 주세요:\n\nfunction calcExpiry(t) { return t + 86400; }" } } ], "maxTokens": 200 }}5. Roots (루트)
섹션 제목: “5. Roots (루트)”Roots는 서버가 작업 가능한 URI나 파일 시스템 경계를 클라이언트에게 확인하는 메커니즘입니다. 파일 시스템 서버처럼 로컬 경로를 다루는 MCP 서버에서는 “어디까지 읽고 써도 되는가”를 명확히 하는 데 중요합니다.
6. Elicitation (정보 요청)
섹션 제목: “6. Elicitation (정보 요청)”Elicitation은 서버가 작업 도중 사용자 입력이 더 필요할 때 클라이언트를 통해 추가 정보를 요청하는 기능입니다. 예를 들어 CRM MCP 서버가 고객 레코드를 수정하기 전에 “어느 고객을 의미하는가”를 사용자에게 확인하도록 요청할 수 있습니다.
프리미티브 선택 가이드
섹션 제목: “프리미티브 선택 가이드”| 상황 | 사용할 프리미티브 | |------|----------------| | 파일 읽기, 검색, 조회 | Resources | | 파일 쓰기, API 호출, 상태 변경 | Tools | | 표준화된 워크플로우 요청 | Prompts | | 서버에서 LLM 추론이 필요 | Sampling | | 서버의 파일·URI 접근 경계 확인 | Roots | | 사용자에게 추가 입력 요청 | Elicitation |
MCP 서버 구체 예시
섹션 제목: “MCP 서버 구체 예시”파일 시스템 서버
섹션 제목: “파일 시스템 서버”로컬 파일 시스템을 MCP로 노출하는 가장 기본적인 서버입니다. Claude Desktop에 다음과 같이 등록합니다.
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/user/Documents" ] } }}등록 후 Claude는 /Users/user/Documents 하위 파일을 읽고 쓸 수 있습니다.
PostgreSQL 서버
섹션 제목: “PostgreSQL 서버”데이터베이스를 MCP Resource로 노출합니다. LLM이 직접 SQL을 실행하는 대신, 서버가 안전하게 쿼리를 중개합니다.
{ "mcpServers": { "postgres": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-postgres"], "env": { "POSTGRES_URL": "postgresql://localhost/mydb" } } }}이 서버는 postgres://mydb/tables 같은 Resource URI로 스키마 정보를 노출하고, query Tool로 SELECT 쿼리를 실행합니다.
MCP 보안 고려 사항
섹션 제목: “MCP 보안 고려 사항”MCP 서버는 강력한 기능을 제공하는 만큼, 보안 설계에 주의가 필요합니다.
도구 권한 최소화
섹션 제목: “도구 권한 최소화”MCP 서버가 제공하는 도구는 필요한 최소 권한만 가져야 합니다. 읽기 전용 작업에 쓰기 도구를 함께 노출하지 않습니다.
# 잘못된 설계: 모든 권한을 하나의 서버에 노출tools = [read_file, write_file, delete_file, exec_shell]
# 올바른 설계: 역할별로 서버를 분리read_only_server_tools = [read_file, list_directory, search_files]write_server_tools = [write_file, create_directory]exec_server_tools = [run_test, run_lint] # 별도 승인 필요입력 검증
섹션 제목: “입력 검증”Tool의 inputSchema만으로는 악의적인 입력을 막기 어렵습니다. 서버 내부에서 추가 검증을 수행합니다.
def read_file(path: str) -> str: # Path traversal 공격 방지 resolved = os.path.realpath(path) allowed_base = os.path.realpath("/workspace")
if not resolved.startswith(allowed_base): raise PermissionError(f"허용되지 않는 경로: {path}")
with open(resolved) as f: return f.read()원격 서버의 신뢰 문제
섹션 제목: “원격 서버의 신뢰 문제”Stdio 방식은 로컬 프로세스이므로 신뢰도가 높습니다. 그러나 Streamable HTTP로 원격 MCP 서버에 연결할 때는 추가 보안 조치가 필요합니다.
| 위협 | 완화 전략 | |------|---------| | 중간자 공격 | TLS 적용, 서버 인증서 검증 | | 무단 접근 | API 키 또는 OAuth 토큰 인증, Resource Indicator 사용 | | 서버 사칭 | 서버 신원 검증 (핀닝 또는 CA 검증) | | 과도한 권한 요청 | Tool 목록 검토 후 선택적 허용 |
최신 MCP 변화 관찰 포인트
섹션 제목: “최신 MCP 변화 관찰 포인트”MCP는 2025년 이후 빠르게 발전하고 있습니다.
- 전송 계층: 새 구현은 Streamable HTTP와 협상 헤더, 목표 명세 버전을 함께 점검
- 상태와 라우팅: stateless core와 헤더 라우팅을 고려해 프록시·로드밸런서 경계를 설계
- 인증과 확장: 원격 인증·권한 부여와 확장 기능은 서버가 지원하는 범위만 노출
- 인증/인가: OAuth Resource Server, Resource Indicator, Protected Resource Metadata 등 원격 서버 보안 요구사항 강화
- 사용자 확인 흐름: Elicitation처럼 서버가 추가 정보를 요청하는 패턴이 표준 기능으로 정리
- 구조화된 결과: 도구 결과를 단순 텍스트가 아니라 구조화된 출력·리소스 링크와 함께 전달하는 방향으로 확장
MCP는 LLM과 외부 도구 통합의 공통 표준입니다. JSON-RPC 2.0 기반으로 동작하며 표준 전송 계층은 Stdio와 Streamable HTTP입니다. 서버는 Tools, Resources, Prompts를 제공하고, 클라이언트는 Sampling, Roots, Elicitation 같은 기능을 제공할 수 있습니다. 보안 측면에서는 최소 권한 원칙, 입력 검증, 원격 서버 신뢰 검토가 필수입니다. 한 번 작성한 MCP 서버는 모든 호환 AI 클라이언트에서 재사용할 수 있습니다.