04. MCP 프로토콜 심화
개요
에이전트 내부에 도구 함수를 하드코딩하는 대신, **MCP (Model Context Protocol)**를 사용하면 데이터베이스 커넥터, Slack 통합, 파일 시스템 접근 같은 외부 서버에 표준화된 방식으로 연결할 수 있습니다.
핵심 개념
| 개념 | 설명 |
|---|---|
| MCP Server | Resources, Prompts, Tools를 노출 |
| MCP Client | 서버에 연결하고 기능을 탐색하는 에이전트 |
| Handshake | 클라이언트가 서버에 "무엇을 할 수 있나요?"라고 묻는 과정 |
| Resources | 서버가 노출하는 데이터 (파일, DB 레코드, 로그) |
| Tools | 실행 가능한 함수 (get_user(), send_message()) |
MCP 서버 구현
class MockMCPServer:
"""최소한의 MCP 서버 구현"""
def __init__(self, name: str):
self.name = name
self.tools = {}
def register_tool(self, name: str, description: str, handler: Callable):
self.tools[name] = {
"description": description,
"handler": handler
}
def list_tools(self) -> List[Dict]:
"""Discovery 엔드포인트 - 이 서버가 무엇을 할 수 있나요?"""
return [
{"name": name, "description": tool["description"]}
for name, tool in self.tools.items()
]
def call_tool(self, name: str, arguments: Dict) -> Any:
"""이름으로 도구 실행"""
if name not in self.tools:
raise ValueError(f"Unknown tool: {name}")
return self.tools[name]["handler"](**arguments)MCP 클라이언트 (에이전트 측)
class MCPClient:
"""MCP 서버에 연결하는 클라이언트"""
def __init__(self, servers: List[MockMCPServer]):
self.servers = servers
def discover_all_tools(self) -> List[Dict]:
"""연결된 모든 서버에서 도구 탐색"""
all_tools = []
for server in self.servers:
tools = server.list_tools()
for tool in tools:
tool["server"] = server.name
all_tools.extend(tools)
return all_tools
def execute(self, server_name: str, tool_name: str, args: Dict):
"""특정 서버에서 도구 실행"""
server = next(s for s in self.servers if s.name == server_name)
return server.call_tool(tool_name, args)MCP Handshake 흐름
MCP가 중요한 이유
범용 연결성: 통합 코드를 한 번 작성하면 어떤 LLM과도 사용 가능
| MCP 이전 | MCP 이후 |
|---|---|
| N×M 통합 (N LLMs × M tools) | N+M 통합 |
| LLM별 커스텀 코드 | 표준화된 인터페이스 |
| 일관성 없는 구현 | 커넥터 생태계 |
실습
Mock 서버 구축
사용자 데이터 도구가 있는 MCP 서버 생성
Discovery 구현
클라이언트가 동적으로 사용 가능한 도구 탐색
LLM 연결
MCP 도구로 OpenAI function calling 사용
흐름 테스트
쿼리를 실행하고 handshake 과정 관찰