Model Context Protocol (MCP) 작성 가이드 - 상세 버전
Model Context Protocol (MCP)는 대형 언어 모델(LLM)이 외부 데이터 소스와 도구에 안전하고 표준화된 방식으로 접근할 수 있게 해주는 개방형 프로토콜입니다^1. 2024년 11월 Anthropic에 의해 발표된 이 프로토콜은 AI 애플리케이션의 USB-C 포트라고 할 수 있으며, 다양한 데이터 소스와 도구들을 표준화된 방식으로 연결할 수 있게 해줍니다^2.
MCP가 해결하는 핵심 문제
기존 AI 시스템들은 각각의 데이터 소스마다 별도의 커스텀 통합이 필요했습니다^3. 이는 개발 복잡도를 증가시키고, 확장성을 제한하며, 보안 문제를 야기했습니다^4. MCP는 이러한 문제들을 해결하기 위해 다음과 같은 핵심 가치를 제공합니다^1:
- 표준화된 통합: 하나의 프로토콜로 다양한 데이터 소스 연결
- 확장성: 새로운 데이터 소스 추가 시 기존 코드 재사용 가능
- 상호운용성: 다양한 LLM 제공업체 간 호환성
- 보안: 프로토콜에 내장된 보안 기능으로 API 키 공유 불필요
MCP 아키텍처 및 핵심 구성 요소
클라이언트-서버 아키텍처
MCP는 JSON-RPC 2.0 프로토콜을 기반으로 한 클라이언트-서버 아키텍처를 따릅니다^6^8. 이 구조는 다음과 같은 주요 구성 요소들로 이루어집니다:
MCP 호스트 (Host): AI 모델이 내장된 애플리케이션으로, Claude Desktop, IDE 어시스턴트, 채팅 애플리케이션 등이 해당됩니다^3. 호스트는 사용자 요청을 받아 모델에게 전달하고, 응답을 사용자에게 보여주는 전체 흐름을 조율합니다.
MCP 클라이언트 (Client): 호스트 애플리케이션 내부에서 동작하며, 하나의 MCP 서버와 1:1 연결을 담당하는 컴포넌트입니다^3. 클라이언트는 AI 모델 측에서 MCP 프로토콜을 구현하며, 서버로 요청을 보내고 응답을 받아 모델에 전달합니다.
MCP 서버 (Server): 외부 데이터나 기능을 제공하는 백엔드 서비스입니다^3. 서버는 특정 서비스나 데이터 소스를 감싸서 모델이 이해할 수 있는 형태로 컨텍스트를 제공합니다.
JSON-RPC 2.0 메시지 포맷
MCP의 모든 통신은 JSON-RPC 2.0 규격을 따릅니다^6^8. 기본 메시지 구조는 다음과 같습니다:
{
"jsonrpc": "2.0",
"id": "request-1234",
"method": "methodName",
"params": {
// 메서드별 파라미터
}
}
응답 메시지는 다음과 같은 형태를 가집니다:
{
"jsonrpc": "2.0",
"id": "request-1234",
"result": {
// 메서드별 결과
}
}
에러 발생 시에는 다음과 같은 구조로 응답합니다:
{
"jsonrpc": "2.0",
"id": "request-1234",
"error": {
"code": -32000,
"message": "Server error",
"data": {
// 추가 오류 정보
}
}
}
MCP 서버 개발 단계별 가이드
1. 개발 환경 설정
MCP 서버 개발을 위해서는 다음과 같은 환경이 필요합니다^9:
- Python 3.10 이상 또는 Node.js 20 이상
- MCP SDK 또는 FastMCP 등 개발 라이브러리
- VS Code, Cursor AI 등 코드 편집기
Python 환경 설정 (uv 사용)^10:
# uv 설치 (macOS)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 프로젝트 생성
uv init my-mcp-server
cd my-mcp-server
# 가상환경 생성 및 활성화
uv venv
source .venv/bin/activate
# MCP 라이브러리 설치
uv add "mcp[cli]" httpx
Node.js 환경 설정^12:
# MCP 프레임워크 전역 설치
npm install -g mcp-framework
# 새 프로젝트 생성
mcp create my-mcp-server
cd my-mcp-server
# 종속성 설치
npm install
2. FastMCP를 활용한 Python 서버 구현
FastMCP는 Python에서 MCP 서버를 쉽게 구축할 수 있게 해주는 고수준 라이브러리입니다^9. 다음은 기본적인 서버 구현 예시입니다:
from fastmcp import FastMCP
import logging
# 로깅 설정
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("mcp-server")
# MCP 서버 인스턴스 생성
mcp = FastMCP("Demo Server", debug=True)
# 리소스(Resource) 구현: 정적 데이터 제공
@mcp.resource("config://app")
def get_config() -> str:
"""애플리케이션 설정 정보를 제공하는 리소스"""
logger.info("Config resource accessed")
return "App configuration here"
# 동적 리소스: 매개변수를 받는 리소스
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""사용자 프로필 정보를 제공하는 동적 리소스"""
logger.info(f"User profile requested: {user_id}")
return f"Profile data for user {user_id}"
# 도구(Tool) 구현: 계산 기능
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
"""두 숫자를 더하는 도구"""
logger.info(f"Adding {a} and {b}")
return a + b
# 프롬프트(Prompt) 구현: 템플릿 제공
@mcp.prompt()
def echo_prompt(message: str) -> str:
"""메시지를 에코하는 프롬프트 템플릿"""
logger.info(f"Echo prompt with message: {message}")
return f"Please process this message: {message}"
# 서버 실행
if __name__ == "__main__":
logger.info("Starting MCP server...")
mcp.run()
3. TypeScript를 활용한 서버 구현
TypeScript로 MCP 서버를 구현하는 경우, 공식 MCP SDK를 사용할 수 있습니다^14:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
// 서버 인스턴스 생성
const server = new Server({
name: "example-mcp-server",
version: "1.0.0",
}, {
capabilities: {
resources: {},
tools: {},
prompts: {}
}
});
// 리소스 핸들러 등록
server.setRequestHandler("resources/list", async () => {
return {
resources: [
{
uri: "note:///1",
mimeType: "text/plain",
name: "First Note",
description: "A text note: First Note"
}
]
};
});
// 도구 핸들러 등록
server.setRequestHandler("tools/list", async () => {
return {
tools: [
{
name: "create_note",
description: "Create a new note",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Title of the note" },
content: { type: "string", description: "Content of the note" }
},
required: ["title", "content"]
}
}
]
};
});
// 서버 시작
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
MCP의 핵심 기능 요소
1. 리소스 (Resources)
리소스는 LLM이 읽을 수 있는 읽기 전용 데이터를 제공합니다^15. REST API의 GET 엔드포인트와 유사하며, 복잡한 계산이나 부작용 없이 단순히 구조화된 데이터를 반환합니다^16.
리소스의 특징:
- 순수 데이터 제공 목적
- 캐싱 가능
- URI 패턴으로 동적 리소스 지원
- 다양한 MIME 타입 지원 (text/plain, application/json 등)
2. 도구 (Tools)
도구는 LLM이 호출할 수 있는 실행 가능한 함수입니다^15. 계산을 수행하거나 외부 시스템에 변경을 가할 수 있는 명령적 함수로 작동합니다^16.
도구의 특징:
- 액션 수행 가능
- 입력 스키마 정의 필수
- 부작용 허용
- JSON Schema로 매개변수 검증
3. 프롬프트 (Prompts)
프롬프트는 재사용 가능한 템플릿으로, LLM과의 상호작용을 구조화하고 일관성을 제공합니다^15. 동적 입력을 받아 컨텍스트를 포함한 메시지를 생성할 수 있습니다.
프롬프트의 활용:
- 시스템 프롬프트 템플릿
- 사용자 입력 템플릿
- 워크플로우 가이드
- 복잡한 상호작용 체인 구성
4. 전송 계층 (Transports)
MCP는 다양한 전송 방식을 지원합니다^17:
STDIO Transport: 표준 입출력을 통한 로컬 통신으로, 개발 및 테스트에 적합합니다^17.
HTTP+SSE Transport: 웹 환경에서의 원격 통신을 위한 방식으로, HTTP POST와 Server-Sent Events를 결합합니다^17.
WebSocket Transport: 실시간 양방향 통신을 위한 방식입니다^17.
MCP 서버 테스트 및 디버깅
MCP Inspector 활용
MCP Inspector는 서버를 테스트하고 디버깅할 수 있는 공식 도구입니다^19:
# Inspector 실행 (UI 모드)
npx @modelcontextprotocol/inspector
# 특정 서버와 함께 실행
npx @modelcontextprotocol/inspector node build/index.js
# CLI 모드로 실행
npx @modelcontextprotocol/inspector --cli node build/index.js
Inspector의 주요 기능:
- 서버 연결 상태 확인
- 도구, 리소스, 프롬프트 테스트
- 실시간 메시지 모니터링
- 에러 진단 및 로깅
로깅 및 모니터링
효과적인 디버깅을 위해서는 적절한 로깅이 필수입니다^10:
import logging
# 상세한 로깅 설정
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger("mcp-server")
@mcp.tool()
def example_tool(param: str) -> str:
logger.info(f"Tool called with parameter: {param}")
try:
result = process_data(param)
logger.info(f"Tool completed successfully: {result}")
return result
except Exception as e:
logger.error(f"Tool failed with error: {e}")
raise
MCP 서버 배포 및 설정
Claude Desktop과의 연동
Claude Desktop에서 MCP 서버를 사용하기 위해서는 설정 파일을 수정해야 합니다^20^22:
- Claude Desktop 실행 후 Settings → Developer → Edit Config 클릭
claude_desktop_config.json
파일 편집
설정 예시^21:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/path/to/your/directory"
]
},
"my-custom-server": {
"command": "python",
"args": ["path/to/your/server.py"],
"env": {
"API_KEY": "your-api-key"
}
}
}
}
환경별 설정 관리
개발, 테스트, 프로덕션 환경별로 다른 설정을 관리할 수 있습니다^23:
개발 환경 (.env.development):
MCP_SERVER_NAME=dev-server
MCP_DEBUG=true
MCP_LOG_LEVEL=debug
MCP_ROOT_DIR=./workspace
MCP_ALLOW_SYMLINKS=true
프로덕션 환경 (.env.production):
MCP_SERVER_NAME=prod-server
MCP_DEBUG=false
MCP_LOG_LEVEL=info
MCP_ROOT_DIR=/var/mcp/workspace
MCP_ALLOW_SYMLINKS=false
MCP_AUTH_REQUIRED=true
고급 MCP 서버 구현 패턴
복합 기능 서버 예시
실제 업무에서 활용할 수 있는 복합 기능 서버의 예시입니다^15:
from fastmcp import FastMCP
import requests
import json
import sqlite3
from typing import List, Dict
mcp = FastMCP("Business Analytics Server")
# 데이터베이스 연결 설정
@mcp.resource("database://connection")
def get_db_status() -> str:
"""데이터베이스 연결 상태 확인"""
try:
conn = sqlite3.connect('business.db')
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM customers")
count = cursor.fetchone()[^0]
conn.close()
return f"Database connected. Total customers: {count}"
except Exception as e:
return f"Database connection failed: {e}"
# 고객 정보 조회 도구
@mcp.tool()
def search_customers(keyword: str, limit: int = 10) -> List[Dict]:
"""고객 정보를 검색하는 도구"""
conn = sqlite3.connect('business.db')
cursor = conn.cursor()
query = """
SELECT id, name, email, created_date
FROM customers
WHERE name LIKE ? OR email LIKE ?
LIMIT ?
"""
cursor.execute(query, (f'%{keyword}%', f'%{keyword}%', limit))
results = cursor.fetchall()
conn.close()
return [
{
"id": row[^0],
"name": row[^1],
"email": row[^2],
"created_date": row[^3]
}
for row in results
]
# 보고서 생성 프롬프트
@mcp.prompt()
def generate_report_prompt(report_type: str, date_range: str) -> str:
"""비즈니스 보고서 생성을 위한 프롬프트 템플릿"""
return f"""
Generate a {report_type} report for the period: {date_range}
Please include:
1. Executive summary
2. Key metrics and KPIs
3. Trend analysis
4. Recommendations
Use the available customer data and analytics tools to gather the necessary information.
"""
# 외부 API 연동 도구
@mcp.tool()
def get_market_data(symbol: str) -> Dict:
"""주식 시장 데이터를 가져오는 도구"""
try:
# 실제 구현에서는 적절한 API 키와 엔드포인트 사용
response = requests.get(f"https://api.example.com/stock/{symbol}")
return response.json()
except Exception as e:
return {"error": f"Failed to fetch market data: {e}"}
if __name__ == "__main__":
mcp.run()
에러 처리 및 보안 고려사항
프로덕션 환경에서는 적절한 에러 처리와 보안 조치가 필요합니다:
from fastmcp import FastMCP
import os
from functools import wraps
mcp = FastMCP("Secure Server")
# 인증 데코레이터
def require_auth(func):
@wraps(func)
def wrapper(*args, **kwargs):
api_key = os.getenv('MCP_API_KEY')
if not api_key:
raise ValueError("Authentication required")
return func(*args, **kwargs)
return wrapper
# 입력 검증 및 제한
@mcp.tool()
@require_auth
def secure_file_read(file_path: str) -> str:
"""보안이 적용된 파일 읽기 도구"""
# 경로 검증
allowed_dirs = ['/safe/directory', '/public/data']
if not any(file_path.startswith(allowed_dir) for allowed_dir in allowed_dirs):
raise ValueError("Access denied: Invalid file path")
# 파일 크기 제한
if os.path.getsize(file_path) > 10 * 1024 * 1024: # 10MB
raise ValueError("File too large")
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
raise ValueError("File not found")
except PermissionError:
raise ValueError("Permission denied")
결론 및 향후 발전 방향
MCP는 AI 생태계에서 데이터 소스와 도구를 표준화하는 중요한 프로토콜로 자리잡고 있습니다. FastMCP와 같은 고수준 라이브러리를 활용하면 복잡한 프로토콜 구현 없이도 강력한 MCP 서버를 구축할 수 있습니다^9.
향후 MCP 개발에서 고려해야 할 주요 사항들:
- 보안 강화: 인증, 권한 관리, 데이터 암호화 등의 보안 조치
- 성능 최적화: 대량 데이터 처리, 캐싱, 연결 풀링 등
- 모니터링: 로깅, 메트릭 수집, 알림 시스템 구축
- 확장성: 마이크로서비스 아키텍처, 로드 밸런싱 고려
- 호환성: 다양한 클라이언트와의 호환성 유지
MCP는 AI 에이전트와 실제 업무 환경을 연결하는 핵심 기술로, 앞으로도 계속해서 발전할 것으로 예상됩니다. 개발자들은 이 표준을 활용하여 더욱 강력하고 유연한 AI 솔루션을 구축할 수 있을 것입니다.
'Code Story' 카테고리의 다른 글
LangGraph 개발 가이드 요약 및 참고사이트 (2) | 2025.06.18 |
---|---|
NL2SQL 쿼리의 자동 검증 방안 (2) | 2025.06.17 |
LLM 핵심 기술 심층 분석 요약 (3) | 2025.06.17 |
LLM 핵심 기술에 대해서... (0) | 2025.06.17 |
바이브 코딩 (개발자의 미래) (1) | 2025.06.16 |