MCP (Model Context Protocol) 완전 가이드 | AI 에이전트 표준 프로토콜
이 글의 핵심
MCP(Model Context Protocol)는 AI 모델이 파일 시스템, 데이터베이스, 웹 API 등 외부 리소스와 표준화된 방식으로 통신하는 오픈 프로토콜입니다. Claude Desktop, Cursor, VS Code 등에서 지원합니다.
MCP란?
MCP(Model Context Protocol)는 Anthropic이 2024년 말 공개한 오픈소스 프로토콜입니다. AI 모델이 파일 시스템, 데이터베이스, 웹 API, 개발 도구 등 외부 리소스와 표준화된 방식으로 통신할 수 있게 합니다.
클라이언트 (Claude Desktop, Cursor, VS Code)
↕ MCP 프로토콜
서버 (파일 시스템, DB, 웹 API, Git, Slack...)
왜 MCP인가?
이전에는 각 AI 앱마다 외부 도구 연동 코드를 따로 작성해야 했습니다. MCP는 USB처럼 표준 인터페이스를 제공해서 한 번 만든 MCP 서버를 모든 호환 클라이언트에서 재사용할 수 있습니다.
| 이전 방식 | MCP 방식 |
|---|---|
| 앱마다 API 연동 코드 별도 작성 | MCP 서버 한 번 작성 → 모든 클라이언트 사용 |
| 모델 변경 시 코드 수정 필요 | 모델/클라이언트 무관하게 동작 |
| 보안/인증 중복 구현 | 서버 측에서 일관되게 처리 |
MCP 아키텍처
┌─────────────────────────────────────────┐
│ MCP 클라이언트 │
│ (Claude Desktop / Cursor / VS Code) │
└────────────────┬────────────────────────┘
│ JSON-RPC over stdio / SSE
┌────────────────▼────────────────────────┐
│ MCP 서버 │
│ ┌──────────┐ ┌──────────┐ ┌───────┐ │
│ │ Tools │ │Resources │ │Prompts│ │
│ └──────────┘ └──────────┘ └───────┘ │
└────────────────┬────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
파일시스템 데이터베이스 웹 API
3가지 핵심 개념
- Tools: AI가 호출할 수 있는 함수 (Function Calling과 유사)
- Resources: AI가 읽을 수 있는 데이터 (파일, DB 레코드 등)
- Prompts: 재사용 가능한 프롬프트 템플릿
빠른 시작: Python MCP 서버 만들기
설치
pip install mcp
# 또는 uv 사용 (권장)
pip install uv
uv add mcp
첫 번째 MCP 서버
아래는 list_tools 함수 구현 예제입니다. 위에서 설명한 핵심 로직을 담고 있습니다.
# server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import mcp.types as types
# 서버 인스턴스 생성
server = Server("my-first-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
"""사용 가능한 도구 목록 반환"""
return [
Tool(
name="calculate",
description="수식을 계산합니다",
inputSchema={
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "계산할 수식 (예: 2 + 3 * 4)"
}
},
"required": ["expression"]
}
),
Tool(
name="get_current_time",
description="현재 시간을 반환합니다",
inputSchema={
"type": "object",
"properties": {}
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""도구 호출 처리"""
if name == "calculate":
expression = arguments.get("expression", "")
try:
# 안전한 수식 평가 (실제 프로덕션에서는 더 안전한 방법 사용)
result = eval(expression, {"__builtins__": {}}, {})
return [TextContent(type="text", text=f"{expression} = {result}")]
except Exception as e:
return [TextContent(type="text", text=f"오류: {str(e)}")]
elif name == "get_current_time":
from datetime import datetime
now = datetime.now().strftime("%Y년 %m월 %d일 %H:%M:%S")
return [TextContent(type="text", text=f"현재 시간: {now}")]
raise ValueError(f"알 수 없는 도구: {name}")
async def main():
# stdio 모드로 서버 실행 (로컬 Claude Desktop 연결용)
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="my-first-server",
server_version="1.0.0",
capabilities=server.get_capabilities(
notification_options=None,
experimental_capabilities={}
)
)
)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
실전 예제: 파일 시스템 MCP 서버
아래는 list_tools 함수 구현 예제입니다. 위에서 설명한 핵심 로직을 담고 있습니다.
# file_server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import Tool, Resource, TextContent, ResourceTemplate
import mcp.types as types
from pathlib import Path
import os
server = Server("file-system-server")
ALLOWED_DIR = Path(os.environ.get("ALLOWED_DIR", "/tmp/mcp-files"))
ALLOWED_DIR.mkdir(exist_ok=True)
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="read_file",
description="파일 내용을 읽습니다",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "파일 경로"}
},
"required": ["path"]
}
),
Tool(
name="write_file",
description="파일에 내용을 씁니다",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "파일 경로"},
"content": {"type": "string", "description": "저장할 내용"}
},
"required": ["path", "content"]
}
),
Tool(
name="list_files",
description="디렉토리의 파일 목록을 반환합니다",
inputSchema={
"type": "object",
"properties": {
"directory": {"type": "string", "description": "디렉토리 경로 (생략 시 기본 디렉토리)"}
}
}
)
]
def validate_path(path_str: str) -> Path:
"""보안: 허용된 디렉토리 내 경로만 허용"""
path = (ALLOWED_DIR / path_str).resolve()
if not str(path).startswith(str(ALLOWED_DIR)):
raise ValueError("허용된 디렉토리 밖의 경로는 접근할 수 없습니다")
return path
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "read_file":
path = validate_path(arguments["path"])
if not path.exists():
return [TextContent(type="text", text=f"오류: 파일을 찾을 수 없습니다: {path}")]
content = path.read_text(encoding="utf-8")
return [TextContent(type="text", text=content)]
elif name == "write_file":
path = validate_path(arguments["path"])
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(arguments["content"], encoding="utf-8")
return [TextContent(type="text", text=f"파일 저장 완료: {path.name}")]
elif name == "list_files":
dir_path = validate_path(arguments.get("directory", "."))
if not dir_path.is_dir():
return [TextContent(type="text", text="디렉토리가 아닙니다")]
files = []
for item in sorted(dir_path.iterdir()):
item_type = "📁" if item.is_dir() else "📄"
size = item.stat().st_size if item.is_file() else 0
files.append(f"{item_type} {item.name} ({size:,} bytes)" if item.is_file() else f"{item_type} {item.name}/")
result = "\n".join(files) if files else "파일 없음"
return [TextContent(type="text", text=result)]
raise ValueError(f"알 수 없는 도구: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream,
InitializationOptions(server_name="file-system-server", server_version="1.0.0",
capabilities=server.get_capabilities(notification_options=None, experimental_capabilities={})))
if __name__ == "__main__":
import asyncio
asyncio.run(main())
TypeScript로 MCP 서버 만들기
npm install @modelcontextprotocol/sdk
TypeScript/JavaScript 예제 코드입니다.
// server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";
const server = new Server(
{ name: "weather-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// 도구 목록 정의
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_weather",
description: "도시의 현재 날씨를 가져옵니다",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "도시 이름 (영문)",
},
},
required: ["city"],
},
},
],
}));
// 도구 호출 처리
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_weather") {
const city = args?.city as string;
const apiKey = process.env.OPENWEATHER_API_KEY;
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=kr`
);
const data = (await response.json()) as any;
if (data.cod !== 200) {
return {
content: [{ type: "text", text: `오류: ${data.message}` }],
};
}
return {
content: [
{
type: "text",
text: `${city} 날씨: ${data.weather[0].description}, 기온 ${data.main.temp}°C, 체감 ${data.main.feels_like}°C`,
},
],
};
}
throw new Error(`알 수 없는 도구: ${name}`);
});
// 서버 시작
const transport = new StdioServerTransport();
await server.connect(transport);
Claude Desktop에 연결하기
설정 파일 예시입니다.
// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
// %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"my-calculator": {
"command": "python",
"args": ["/path/to/server.py"],
"env": {}
},
"file-system": {
"command": "python",
"args": ["/path/to/file_server.py"],
"env": {
"ALLOWED_DIR": "/Users/username/Documents"
}
},
"weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"],
"env": {
"OPENWEATHER_API_KEY": "your-api-key-here"
}
}
}
}
Claude Desktop을 재시작하면 채팅 창 하단에 🔧 도구 아이콘이 표시됩니다.
Resources와 Prompts 사용
Resources (데이터 읽기)
아래는 list_resources 함수 구현 예제입니다. 위에서 설명한 핵심 로직을 담고 있습니다.
from mcp.types import Resource, BlobResourceContents, TextResourceContents
@server.list_resources()
async def list_resources() -> list[Resource]:
return [
Resource(
uri="file:///config.json",
name="설정 파일",
description="애플리케이션 설정",
mimeType="application/json"
)
]
@server.read_resource()
async def read_resource(uri: str) -> str | bytes:
if uri == "file:///config.json":
return '{"version": "1.0", "debug": false}'
raise ValueError(f"알 수 없는 리소스: {uri}")
Prompts (재사용 가능한 프롬프트)
아래는 list_prompts 함수 구현 예제입니다. 위에서 설명한 핵심 로직을 담고 있습니다.
from mcp.types import Prompt, PromptArgument, GetPromptResult, PromptMessage
@server.list_prompts()
async def list_prompts() -> list[Prompt]:
return [
Prompt(
name="code_review",
description="코드 리뷰 요청 프롬프트",
arguments=[
PromptArgument(name="language", description="프로그래밍 언어", required=True),
PromptArgument(name="focus", description="리뷰 중점 사항", required=False)
]
)
]
@server.get_prompt()
async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
if name == "code_review":
language = arguments.get("language", "코드") if arguments else "코드"
focus = arguments.get("focus", "전반적인 품질") if arguments else "전반적인 품질"
return GetPromptResult(
description=f"{language} 코드 리뷰",
messages=[
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"""다음 {language} 코드를 리뷰해주세요. 특히 {focus}에 집중해주세요:
1. 코드 품질 및 가독성
2. 잠재적 버그 및 오류
3. 성능 최적화 기회
4. 보안 취약점
5. 개선 제안 (코드 예제 포함)"""
)
)
]
)
raise ValueError(f"알 수 없는 프롬프트: {name}")
인기 MCP 서버 모음
Anthropic과 커뮤니티가 제공하는 공식 MCP 서버들입니다.
터미널에서 다음 명령어를 실행합니다.
# 파일시스템 서버 (공식)
npx -y @modelcontextprotocol/server-filesystem /path/to/directory
# SQLite 서버 (공식)
npx -y @modelcontextprotocol/server-sqlite /path/to/database.db
# GitHub 서버 (공식)
npx -y @modelcontextprotocol/server-github
# PostgreSQL 서버
npx -y @modelcontextprotocol/server-postgres postgresql://user:pass@localhost/db
# Slack 서버
npx -y @modelcontextprotocol/server-slack
# Brave Search 서버
npx -y @modelcontextprotocol/server-brave-search
마치며
MCP는 AI 에이전트 생태계의 핵심 인프라로 빠르게 자리잡고 있습니다. 지금 배워두면 AI 에이전트 개발에서 큰 경쟁력을 가질 수 있습니다.
다음 단계: