
【AI 개발 지침서: 제1회】 MCP (Model Context Protocol)의 내부 구조 해부와 자체 서버 구현
요약
Anthropic이 제창한 MCP(Model Context Protocol)의 내부 구조와 JSON-RPC 2.0 메커니즘을 분석합니다. Python의 FastMCP 프레임워크를 사용하여 Tools, Resources, Prompts를 포함한 자체 MCP 서버를 구현하는 방법을 다룹니다.
핵심 포인트
- MCP는 LLM 클라이언트와 데이터 소스 간의 표준화된 인터페이스를 제공합니다.
- 기존 N×M 방식의 밀결합 문제를 해결하여 Plug & Play 방식의 확장을 가능하게 합니다.
- JSON-RPC 2.0 기반의 메시지 프로토콜을 통해 도구와 문맥을 교환합니다.
- Python SDK를 활용한 실무적인 MCP 서버 구현 가이드를 제공합니다.
【
신연재: MCP · 고도화된 RAG · 자율 에이전트의 현장 설계론】 ※ 본 연재는 MCP (Model Context Protocol), Context Engineering, GraphRAG, Self-Healing Agent, 로컬 SLM 등의 최첨단 기술 스택을 사용하여, 실무 프로덕션에서 진정으로 견딜 수 있는 차세대 AI 시스템을 구축하는 중후한 핸즈온(Hands-on) 연재입니다.
AI 기술의 주축은 단순히 채팅 UI에서 문장을 생성하게 하는 「일문일답」 단계에서, 외부 시스템, 데이터베이스, 로컬 툴, 클라우드 서비스와 연계하여 자율적으로 태스크를 수행하는 「AI 에이전트 시스템」으로 급격히 시프트(Shift)하고 있습니다.
하지만 기존의 AI 시스템 구축에 있어서는 OpenAI, Anthropic, Google 등 모델 프로바이더마다 서로 다른 Function Calling이나 Tool 이용 인터페이스가 존재하여, 시스템 측에서도 독자적인 API 연결 로직을 개별적으로 구현해야 했습니다.
이 「N×M」의 밀결합된 카오스를 해결하기 위해, 2024년 말 Anthropic에 의해 제창되어 업계 표준 프로토콜로서 급속히 보급되고 있는 것이 「MCP (Model Context Protocol)」 입니다.
본 연재의 기념비적인 제1회에서는 MCP의 기초가 되는 아키텍처 사상과, 내부에서 주고받는 JSON-RPC 2.0 메시지 프로토콜의 메커니즘을 해부합니다. 그리고 Python의 최신 SDK가 제공하는 FastMCP 프레임워크를 사용하여, Tools (도구), Resources (문맥 데이터), Prompts (정형 지시)의 3대 요소를 망라한 오리지널 MCP 서버를 완전 구현 및 기동하는 것을 목표로 합니다.
Python 버전: Python 3.10 이상 -
동작 확인 완료 주요 라이브러리:-
mcp>=1.2.0
(Anthropic 공식 MCP SDK)
권장 개발 환경:- VS Code 또는 Cursor
- 터미널 환경 (Bash / zsh / PowerShell)
기존의 LLM 애플리케이션 개발에 있어서, 모델에 외부 툴이나 데이터베이스를 이용하게 할 경우 다음과 같은 구조적 과제가 존재했습니다.
서로 다른 LLM 기반 (OpenAI, Claude, Gemini, 로컬 LLM 등) 및 다양한 데이터 소스 (PostgreSQL, Slack, GitHub, 로컬 파일 등)가 존재하는 가운데, 각각의 조합마다 전용 프롬프트 변환이나 API 클라이언트 코드를 기술해야 했습니다.
【기존의 밀결합 어프로치 (N × M)】
[Claude] ---> (전용 툴 정의) ---> [PostgreSQL]
[OpenAI] ---> (전용 Function) ---> [GitHub API]
...
MCP는 컴퓨터에서의 USB Type-C와 같은 표준 규격으로서 기능합니다. LLM 클라이언트 (Claude Desktop, Cursor, AI Agent 등)와 데이터 소스/툴 (MCP Server) 사이에 표준화된 추상화 레이어(Abstraction Layer)를 끼워 넣음으로써, 한 번 작성한 MCP 서버는 어떤 MCP 대응 클라이언트로부터도 즉시 공통 이용 (Plug & Play) 할 수 있게 됩니다.
【MCP에 의한 표준화 어프로치 (N + M)】
[Claude Desktop] ---\ /---> [PostgreSQL MCP Server]
[Cursor Editor] ----[ MCP Protocol ]------> [GitHub MCP Server]
...
| 평가 항목 | 기존의 Function Calling 구현 | MCP (Model Context Protocol) |
|---|---|---|
| 결합도 | 특정 LLM API / 프레임워크에 대한 밀결합 (Tight Coupling) | 클라이언트·서버 완전 분리 (희소 결합 (Sparse Coupling)) |
| 확장성 | 새로운 도구 추가 시마다 클라이언트 코드 수정 필요 | MCP 서버를 프로세스로 추가하는 것만으로 자동 인식 |
| 통신 규격 | 각 사 고유의 HTTP/JSON 구조 | JSON-RPC 2.0 (Stdio 또는 SSE / HTTP) |
| 보안 | 애플리케이션 프로세스 내에 키(Key)나 로직이 공존 | 도구 실행 프로세스를 별도 환경으로 격리 가능 |
| 추상화 범위 | 함수 호출 (Tool)만 지원 | Tools (함수) + Resources (데이터) + Prompts (정형 문구) |
MCP는 클라이언트와 서버 간에 JSON-RPC 2.0 메시지 규격에 기반한 양방향 메시징으로 동작합니다. 전송 계층(Transport Layer)으로는 로컬 환경에서 표준 입출력을 사용하는 Stdio (Standard I/O), 그리고 네트워크를 통해 비동기 통신을 수행하는 **SSE (Server-Sent Events)**가 정의되어 있습니다.
MCP 서버는 클라이언트(LLM)에 대해 다음과 같은 세 가지 기능을 노출(Expose)합니다.
-
Tools (도구): LLM이 호출하는 '실행 가능한 함수'. 부작용 (Side-effect)을 동반하는 액션(예: DB 쓰기, 외부 API 호출, 계산 등)을 실행합니다.
-
Resources (리소스): URI (예:
file:///path/to/data.json또는db://users/profile)로 지정되는 '읽기 전용 문맥 데이터'. 파일 내용이나 로그 등 LLM이 지식으로서 참조하는 정보를 제공합니다. -
Prompts (프롬프트): 서버 측에서 사전 정의된 '재사용 가능한 프롬프트 템플릿'. 사용자나 시스템이 복잡한 태스크를 호출하기 위한 정형화된 문구를 제공합니다.
그럼 이제, 실제로 Python의 최신 SDK에 포함된 고수준 프레임워크인 FastMCP를 사용하여 Tools, Resources, Prompts의 모든 요소를 포함하는 독자적인 MCP 서버 MyFirstMCPServer를 구축해 보겠습니다. 로컬 환경에서 새로운 Python 파일 mcp_server_demo.py를 생성하고 다음 코드를 작성합니다.
# 동작 확인 완료된 라이브러리 버전: mcp>=1.2.0
import os
import sys
...
5. 코드 라인별·로직 상세 해설
구현한 mcp_server_demo.py의 내부 로직에 대해 기술적인 포인트를 하나씩 심층적으로 해설합니다.
1. FastMCP("EnterpriseAssistantServer")를 통한 인스턴스화
FastMCP는 MCP의 저수준(Low-level) 메시지(JSON-RPC 파싱, 세션 핸드셰이크, Stdio 스트림 관리)를 내부적으로 모두 자동화해 주는 고수준 래퍼(High-level Wrapper)입니다.- 인자로 전달한 서버 이름은 MCP 클라이언트와의 초기화 통신 시
serverInfo.name으로서 상대방에게 전달됩니다.
2. @mcp.tool() 데코레이터와 Pydantic / 타입 힌트를 통한 스키마 자동 생성
- 함수에
@mcp.tool()을 부여하면, FastMCP는 함수의 **타입 힌트 (Type Hints:weight_kg: float)**와 **Docstring (docstring의 텍스트)**을 분석하여 자동으로JSON Schema를 생성합니다. - 클라이언트가
tools/list를 요청했을 때, 이 생성된 타입 정보와 설명문이 그대로 송신되므로, LLM은 "인자에 어떤 타입의 변수를 전달해야 하는가"를 정확하게 이해할 수 있습니다.
3. @mcp.resource("URI 패턴")를 통한 문맥 데이터 제공
@mcp.resource()는 URI 형식의 파라미터를 받는 데이터 제공 인터페이스입니다."user://profile/{user_id}"와 같이 중괄호{user_id}를 사용함으로써, 동적인 경로 파라미터(Path Parameter)를 함수의 인자로 그대로 받을 수 있습니다.
4. mcp.run()을 통한 Stdio 트랜스포트 구동
- 스크립트가 직접 실행될 때,
mcp.run()은 프로세스의sys.stdin(표준 입력)으로부터의 JSON-RPC 요청을 대기하며,sys.stdout(표준 출력)으로 응답을 출력하는 무한 루프에 진입합니다. - 로그 출력을 수행할 경우,
sys.stdout을 오염시켜 JSON-RPC 통신을 망가뜨리지 않도록 반드시sys.stderr(표준 에러 출력)로 출력하는 것이 MCP 개발의 철칙입니다.
6. 프로덕션 도입·보안·성능 최적화
자체 제작한 MCP 서버를 실제 운영 환경이나 사내 도구에 통합할 때의 베스트 프랙티스(Best Practice)와 방어선에 대해 해설합니다.
1. 디버깅 기법: FastMCP Inspector 활용
작성한 MCP 서버가 JSON-RPC를 올바르게 반환하고 있는지 브라우저 상에서 그래픽적으로 테스트하려면, MCP 공식 Inspector 도구를 이용하는 것이 가장 효율적입니다.
# FastMCP Inspector 도구를 설치하고 로컬 개발 UI를 실행
npx @modelcontextprotocol/inspector python mcp_server_demo.py
실행하면 로컬 서버가 구동되며, 브라우저 상에서 도구 실행 테스트, 리소스 취득 테스트, 프롬프트 전개 테스트를 대화형으로 수행할 수 있습니다.
작성한 서버를 Anthropic 공식 Claude Desktop 앱에 연결하려면, 설정 파일(claude_desktop_config.json)에 다음 JSON을 추가합니다.
{
"mcpServers": {
"enterprise_assistant": {
...
}
}
}
- 입력 유효성 검사(Input Validation) 강제: LLM으로부터 전달되는 Tool의 인자는 의도하지 않은 잘못된 문자열이나 수치가 포함될 가능성이 있으므로, 함수 내에서 반드시 타입 범위 체크나 새니타이징(Sanitizing, 예:
height_m <= 0거부 등)을 실시하십시오. - 최소 권한 원칙 (Least Privilege): 데이터베이스 쓰기나 삭제 등 부작용(Side Effect)이 큰 조작을 수행하는 Tool에는 승인 플로우(Human-in-the-Loop)를 마련하거나, 읽기 전용 권한으로 동작하게 하는 것이 안전합니다.
이번에는 업계의 공통 프로토콜로서 급격히 확장 중인 **MCP (Model Context Protocol)**의 이론적 배경, JSON-RPC 2.0의 내부 동작 메커니즘, 그리고 FastMCP...
를 이용한 Tools / Resources / Prompts의 완전한 구현 코드를 해설했습니다.
MCP를 채택함으로써, 특정 LLM 벤더에 의존하지 않는, 매우 확장성이 높은 "플러그 앤 플레이(Plug & Play) 방식의 툴·지식 연결"이 가능하다는 것을 실감할 수 있었을 것입니다.
하지만 실제 엔터프라이즈(Enterprise) 개발에 있어서는, 단일 Python 파일을 로컬에서 실행하는 것에 그치지 않고, "사내 PostgreSQL 데이터베이스"나 "REST API", "Docker 컨테이너 환경"과 타입 안전(Type-safe)하게 연결하여, 여러 사용자가 안전하게 이용할 수 있도록 만들어야 합니다.
다음, 제2회.
이번에 구축한 기초를 바탕으로, **『Claude·Cursor와 자사 DB를 연결하는 실전 MCP 서버 구축』**으로 나아갑니다.
Cursor나 Windsurf, Claude Desktop으로부터 자사의 운영 환경에 준하는 데이터베이스를 안전하게 조작하는, 진정한 실무용 MCP 서버의 실전 개발로 나아갑시다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기