Python 또는 TypeScript 개발자로서 Claude, GPT, Cursor가 데이터베이스에 접근하고 API를 호출하며 파일을 읽기를 원하지만 모델 자체에는 「손발」이 없다는 사실을 이미 알고 계실 것입니다. 학습 데이터에는 컷오프가 있고 모델은 외부 동작을 스스로 실행할 수 없습니다. 본 가이드는 빈 프로젝트에서 프로덕션 수준의 MCP Server까지 안내합니다. Tools, Resources, Prompts를 구현하고 stdio와 HTTP+SSE 전송을 모두 실행한 뒤 Docker로 배포합니다. 완료 후 Cursor 또는 Claude Desktop에 자체 도구 체인을 연결할 수 있습니다. 노드 요금은 NOVAKVM 가격 페이지를 참조하세요.
[ SECTION_01 ] // WHAT_IS_MCP MCP란 무엇인가: Function Calling에서 통합 도구 프로토콜로
AI 도구 통합은 명확한 단계를 거쳤습니다. Function Calling(OpenAI, 2023)은 모델이 구조화된 JSON 호출 페이로드를 출력하게 했습니다. Plugins와 GPT Actions는 HTTP 엔드포인트를 채팅 플러그인으로 감쌌습니다. MCP(Model Context Protocol)는 Anthropic이 2024년 11월 오픈소스로 공개한 표준으로, 모델이 도구를 발견·기술·호출하는 방식을 표준화합니다. 한 번 구현하면 Claude Desktop, Cursor, VS Code Continue 등 다양한 호스트가 동일 Server를 재사용합니다.
- LLM 능력 한계: 실시간 데이터 부재, 파일시스템·데이터베이스 직접 접근 불가. 외부 도구가 감각과 손발을 제공합니다.
- 스키마 파편화: OpenAI Function Calling, Claude Tool Use, LangChain Agent는 각각 도구 스키마를 다르게 정의합니다. 모델이나 IDE를 바꾸면 어댑터를 다시 작성해야 합니다.
- 발견 메커니즘 부재: 전통 REST API는 AI에게 무엇을 할 수 있는지 알려주지 않습니다. MCP 런타임은
tools/list로 실시간 기능 목록을 노출합니다. - 세션과 컨텍스트: MCP는 다단계 Agent 워크플로를 위해 지속 연결을 유지합니다. 무상태 REST는 복잡한 작업 연쇄에 불리합니다.
- 보안과 권한: Server는 어떤 도구가 쓰기 가능한지, 어떤 리소스가 읽기 전용인지 제한하여 모델의 프로덕션 데이터 과도 접근을 차단할 수 있습니다.
클라이언트-서버 JSON-RPC 아키텍처는 다음과 같이 구성됩니다.
- Host: Claude Desktop, Cursor, VS Code — 사용자 대면 셸입니다.
- MCP Client: Host에 내장되며 각 Server와 1:1 세션을 유지합니다.
- MCP Server(직접 작성): 세 가지 원시 타입을 노출합니다 — Tools(실행 가능 동작), Resources(읽기 전용 데이터), Prompts(재사용 프롬프트 템플릿).
- Transport: 로컬 개발은 stdio(자식 프로세스 stdin/stdout). 원격 배포는 HTTP + SSE(Server-Sent Events 푸시)를 사용합니다.
- Protocol: 모든 메시지는 JSON-RPC 2.0을 따릅니다. 예:
initialize→tools/list→tools/call→notifications/cancelled. - Lifecycle: 핸드셰이크 → 기능 협상 → 정상 상태 → 우아한 종료. Client는
ping으로 세션을 유지할 수 있습니다.
| 차원 | MCP | OpenAI FC | LangChain Tools |
|---|---|---|---|
| 표준화 | 오픈 프로토콜, 벤더 간 호환 | OpenAI API 종속 | 프레임워크 내부 형식 |
| 도구 발견 | 런타임 tools/list |
요청마다 스키마 임베드 | 코드 등록, 정적 |
| Resources / Prompts | 네이티브 지원 | 미지원 | 커스텀 래퍼 필요 |
| Transport | stdio / HTTP+SSE | HTTPS API | 프로세스 내 호출 |
| IDE 네이티브 통합 | Cursor, Claude Desktop 등 | ChatGPT 플러그인 생태계 | 브릿지 레이어 필요 |
MCP는 API를 호출할 수 있는지 여부가 아니라, AI가 도구를 균일하게 발견·선택·정확히 호출하는 방법, 즉 Agent 시대의 핵심 문제를 다룹니다.
[ SECTION_02 ] // DEV_ENV 개발 환경 구성: Python mcp SDK vs TypeScript SDK
공식 스택은 두 가지 일급 SDK를 제공합니다. Python mcp 패키지(FastMCP 고수준 API 포함)와 TypeScript @modelcontextprotocol/sdk입니다. Python은 빠른 프로토타입과 데이터·ML 워크로드에 적합합니다. TypeScript는 Node.js 생태계와 프론트엔드 중심 팀에 맞습니다. 본 튜토리얼은 Python FastMCP를 중심으로 진행하며 TypeScript 레이아웃도 동일한 구조를 따릅니다.
mkdir my-mcp-server && cd my-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic python-dotenv
npm install -g @modelcontextprotocol/inspector
권장 프로젝트 레이아웃은 다음과 같습니다.
my-mcp-server/
├── server.py
├── tools/
├── resources/
├── prompts/
├── tests/
├── Dockerfile
├── requirements.txt
└── .env
디버깅과 Host 통합에 유용한 세 가지 도구입니다.
- MCP Inspector: 공식 시각 디버거입니다. Tools, Resources, Prompts 목록을 표시하고 수동 호출을 트리거합니다.
- Claude Desktop: macOS에서는
~/Library/Application Support/Claude/claude_desktop_config.json을 편집하여 stdio Server를 등록합니다. - Cursor: Settings → MCP → Server 설정 추가. stdio와 원격 HTTP 엔드포인트를 모두 지원합니다.
[ SECTION_03 ] // HELLO_WORLD Hello World: 10줄로 첫 MCP Server 실행
FastMCP는 say_hello Tool이 있는 최소 Server를 제공합니다.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server")
@mcp.tool()
def say_hello(name: str) -> str:
return f"Hello, {name}! MCP Server is running."
if __name__ == "__main__":
mcp.run()
Inspector로 검증합니다.
npx @modelcontextprotocol/inspector python server.py
브라우저에서 Inspector UI를 엽니다. Tools 패널에서 {"name": "NOVAKVM"}으로 say_hello를 호출하면 인사말이 반환됩니다.
Claude Desktop 설정(claude_desktop_config.json):
{
"mcpServers": {
"hello-server": {
"command": "/path/to/my-mcp-server/.venv/bin/python",
"args": ["/path/to/my-mcp-server/server.py"]
}
}
}
Cursor MCP 설정: 프로젝트 루트에 .cursor/mcp.json을 만들거나 Cursor Settings → MCP에 동일 JSON을 추가합니다. Cursor를 재시작하면 Agent 모드에서 say_hello Tool이 표시됩니다.
[ SECTION_04 ] // TOOLS Tools 심화: 다섯 가지 Tool 유형과 오류 처리 패턴
MCP에서 함수 시그니처가 곧 문서입니다. 매개변수 이름, 타입 힌트, docstring이 모델용 JSON Schema로 변환됩니다. 복잡한 입력에는 Pydantic 모델을 사용합니다.
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(..., description="Search keywords")
limit: int = Field(10, ge=1, le=100, description="Max results")
@mcp.tool()
async def search_docs(input: SearchInput) -> list[dict]:
...
프로덕션 Server는 보통 다음 다섯 가지 Tool 카테고리를 제공합니다.
- calculator: 안전한 수식 평가. 원시
eval은 피하고ast또는 전용 라이브러리로 파싱합니다. - file_read / file_write: 루트 디렉터리(chroot 방식)로 제한하여 경로 탐색을 차단합니다. 쓰기는 확인 또는 확장자 화이트리스트가 필요할 수 있습니다.
- fetch_url: 비동기 HTTP 요청 — 아래 예시를 참조하세요.
- db_query: 매개변수화된 읽기 전용 SQL(
SELECT). 쓰기는 감사 로그가 있는 별도 Tool로 분리합니다. - get_current_time: ISO 8601 타임스탬프를 반환하여 모델이 현재 시각을 알 수 있게 합니다.
import httpx
@mcp.tool()
async def fetch_url(url: str, timeout: int = 30) -> str:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(url)
resp.raise_for_status()
return resp.text[:50000]
오류 처리 패턴:
- Tool 내부에서 예외를 잡아 프로세스 크래시 대신 구조화된 오류 텍스트를 반환합니다. Client가 결과를 모델에 전달하여 재시도나 대안 전략을 시도합니다.
- 외부 API에 타임아웃과 재시도 한도를 설정합니다. 모델의 무한 호출 루프를 방지합니다.
- API 키와 연결 문자열은 환경 변수에서만 읽습니다. Tool 출력이나 Resources에 시크릿을 반환하지 않습니다.
- 쓰기 Tool은 변경 내용, 영향받은 행 수 등 작업 요약을 반환하여 모델이 사용자와 확인할 수 있게 합니다.
[ SECTION_05 ] // RESOURCES Resources 프로토콜: 읽기 전용 데이터 vs 실행 가능 Tool
Resource vs Tool: Resource는 읽기 전용 컨텍스트(설정 파일, 사용자 프로필, 로그 조각)입니다. 모델은 resources/read로 가져옵니다. Tool은 실행 가능 동작(쿼리, 쓰기, HTTP 호출)입니다. AI가 보기만 하고 수정하지 않아야 하는 정보에 Resource가 적합합니다.
URI 스킴 관례:
config://app/settings— 시작 시 등록되는 정적 설정입니다.user://{id}/profile— 동적 템플릿. Client가 id를 제공하면 Server가 요청 시 콘텐츠를 생성합니다.file://— 로컬 파일시스템에 매핑됩니다(경로 샌드박스 필요).
Resource는 네 가지 MIME 카테고리를 지원합니다. text/plain, application/json, binary blob(Base64 인코딩), stream(대용량 파일 청크 푸시).
@mcp.resource("config://app/settings")
def get_app_settings() -> str:
return json.dumps({"env": "production", "version": "1.2.0"})
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
profile = db.fetch_user(user_id)
return json.dumps(profile)
파일시스템 Resource Server는 디렉터리 하위 파일을 재귀 등록할 수 있습니다. AI는 코딩 중 파일마다 read Tool을 호출하지 않고도 프로젝트 구조와 핵심 설정을 볼 수 있습니다.
[ SECTION_06 ] // PROMPTS Prompts: 재사용 가능한 다턴 대화 스캐폴드
MCP Prompt는 Host가 나열하고 대화에 주입할 수 있는 사전 정의 템플릿입니다. 코드 리뷰, 장애 사후 분석, API 설계 리뷰 등 반복 워크플로에 사용합니다. Prompts는 플레이스홀더 매개변수를 받고 다턴 메시지 구조를 지원합니다.
from mcp.types import PromptMessage, TextContent
@mcp.prompt()
def code_review_prompt(language: str, focus: str = "security") -> list[PromptMessage]:
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Review the following {language} code for {focus} issues. List problems and improvements:\n\n"
)
)
]
다턴 예시: 1라운드에서 역할과 제약을 설정하고, 2라운드에서 코드 플레이스홀더를 주입하며, 3라운드에서 구조화 JSON 출력(심각도 / 파일 / 줄 / 제안)을 요청합니다. 팀은 모범 사례를 Prompts로 코드화하여 신규 멤버가 시스템 프롬프트를 매번 다시 쓰지 않아도 됩니다.
[ SECTION_07 ] // HTTP_TRANSPORT HTTP 전송: stdio에서 Streamable HTTP 원격 서비스로
| 차원 | stdio | HTTP + SSE |
|---|---|---|
| 배포 | 로컬 자식 프로세스 | 원격 Server / 컨테이너 |
| 네트워크 | 불필요 | 공용 또는 사설 접근성 |
| 수평 확장 | 단일 머신 | 로드 밸런서 + 세션 어피니티 |
| 인증 | 프로세스 격리 | Bearer Token / API Key |
| 적합 용도 | 개인 개발, Claude Desktop | 팀 공유, 24/7 클라우드 호스팅 |
FastMCP는 streamable-http 전송을 지원합니다. 한 줄로 전환할 수 있습니다.
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)
프로덕션 HTTP 배포에는 다음 레이어가 필요합니다.
- Bearer Token / API Key: 리버스 프록시 또는 미들웨어에서
Authorization헤더를 검증합니다. - CORS: 브라우저 기반 Inspector가 교차 출처일 경우 올바른 소스 도메인을 허용합니다.
- Rate limiting: IP 또는 API 키별로 스로틀하여 모델 주도 호출 폭주를 막습니다.
- TLS: 공개 노출에는 HTTPS가 필요합니다. Caddy 또는 Nginx로 SSL을 종료합니다.
[ SECTION_08 ] // DEBUG_TEST 디버깅·테스트·트러블슈팅: Inspector, pytest, 6단계 배포 체크리스트
MCP Inspector 워크플로: 실행 후 왼쪽 패널에 Server 기능 트리가 표시됩니다. Tools 패널에서 JSON 매개변수로 수동 호출이 가능합니다. Notifications는 Server 푸시 이벤트를, Logs는 stderr를 보여줍니다. 코드 변경 시 Inspector가 자식 프로세스를 재시작합니다.
import pytest
from server import say_hello
def test_say_hello():
result = say_hello("World")
assert "Hello, World!" in result
@pytest.mark.asyncio
async def test_fetch_url():
result = await fetch_url("https://httpbin.org/get")
assert "origin" in result
| 증상 | 가능 원인 | 해결 |
|---|---|---|
| Tools 미표시 | 데코레이터 누락 또는 Server 기동 실패 | stderr 확인, Inspector 재연결 |
| JSON 직렬화 오류 | datetime, Decimal 등 비JSON 타입 반환 | json.dumps 수동 호출 또는 str 캐스트 |
| 호출 타임아웃 | 느린 외부 API, 타임아웃 미설정 | Tool 내부 타임아웃 설정, Client 한도 상향 |
| Permission denied | 샌드박스 밖 경로 또는 DB 자격 증명 오류 | ALLOWED_PATHS와 .env 확인 |
6단계 프로덕션 배포 체크리스트:
- 의존성 고정:
pip freeze > requirements.txt실행. CI에서 Python과 mcp SDK 버전을 고정하여 업스트림 breaking change를 방지합니다. - Dockerfile 작성: 멀티 스테이지 빌드. 최종 이미지에는 런타임 의존성만 포함합니다. non-root 사용자로 Server를 실행합니다.
- 환경 변수 구성: API 키, DB 연결 문자열, ALLOWED_PATHS는 시크릿 관리로 전달합니다. 하드코딩하지 않습니다.
- 호스팅 플랫폼 선택: Railway 또는 Render로 빠른 검증. 저빈도 호출은 HTTP 어댑터가 있는 AWS Lambda. 24/7 상시 가동은 Cloud Run 또는 VPS.
- 모니터링 추가: 구조화 JSON 로그,
/health엔드포인트, Prometheus 메트릭, Sentry 예외 보고. - Cursor / Claude 통합 검증: MCP 설정에 원격 URL과 Bearer Token을 넣습니다. 롤아웃 전
tools/list로 전체 Tool 목록을 확인합니다.
[ SECTION_09 ] // PRODUCTION 프로덕션 배포: Docker, 호스팅 플랫폼, 관측성
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER nobody
EXPOSE 8080
CMD ["python", "server.py"]
호스팅 플랫폼 옵션:
- Railway / Render: Git push 배포. 무료 티어는 MVP에 적합합니다. SSE 장기 연결에 cold-start 영향을 주의하세요.
- AWS Lambda: 호출당 과금. HTTP-to-stdio 어댑터가 필요합니다. 저빈도 내부 Tool에 적합합니다.
- Google Cloud Run: 컨테이너 네이티브 자동 확장. SSE 세션 유지를 위해 min-instances ≥ 1을 설정합니다.
- VPS / 베어메탈: 네트워크와 디스크를 완전히 제어합니다. 민감 데이터를 온프레미스에 두어야 할 때 최적입니다.
관측성 스택:
- Logging: structlog 또는 python-json-logger. 모든 호출마다 Tool 이름, duration_ms, status를 기록합니다.
- Prometheus:
mcp_tool_calls_total과mcp_tool_duration_seconds카운터·히스토그램을 노출합니다. - Sentry: 처리되지 않은 예외를 캡처합니다. 빠른 롤백을 위해 Server 버전으로 태그합니다.
버전 호환성: MCP spec revision 2025-03-26에서 Streamable HTTP가 도입되었습니다. 배포 전 Client(Cursor 버전, Claude Desktop 버전)와 Server SDK가 동일 전송을 지원하는지 확인하세요. README에 최소 버전을 문서화합니다. 예: mcp>=1.2.0.
[ SECTION_10 ] // ECOSYSTEM 실습: ChromaDB 지식베이스 MCP와 2026 생태계·학습 경로
캡스톤 프로젝트 — 지식베이스 MCP Server: ChromaDB 또는 Qdrant에 문서 벡터를 저장하고, watchfiles로 디렉터리 변경 시 재인덱싱하며, search_knowledge와 write_note Tool을 노출합니다. Cursor에 연결하면 Agent가 학습 데이터만이 아니라 팀 문서를 검색할 수 있습니다.
@mcp.tool()
async def search_knowledge(query: str, top_k: int = 5) -> list[dict]:
embedding = await embed(query)
results = collection.query(query_embeddings=[embedding], n_results=top_k)
return [{"text": doc, "score": score} for doc, score in zip(...)]
@mcp.tool()
async def write_note(title: str, content: str) -> str:
doc_id = collection.add(documents=[content], metadatas=[{"title": title}])
return f"Note saved: {doc_id}"
재사용 가치가 높은 커뮤니티 MCP Server:
- @modelcontextprotocol/server-filesystem — 샌드박스 파일 읽기/쓰기
- server-github — 이슈, PR, 코드 검색
- server-brave-search — 실시간 웹 검색
- server-postgres — 읽기 전용 SQL 쿼리
- server-slack — 채널 메시지와 알림
2026 MCP 생태계 스냅샷:
- 생태계 규모: 2026년 기준 공개 MCP Server는 10,000개를 넘습니다. modelcontextprotocol 조직 저장소 GitHub 스타 합계는 50,000을 돌파했습니다.
- 벤더 채택: OpenAI는 2026년 1분기 네이티브 MCP 지원을 발표했고, Google Gemini와 Microsoft Copilot이 2분기에 뒤따랐습니다. 거버넌스는 Linux Foundation 산하 AAIF로 이관되었습니다.
- 엔터프라이즈 통합 비용: 업계 조사에 따르면 MCP 인터페이스 표준화 후 AI 도구체인 통합 비용이 약 38–55% 감소했다고 보고됩니다.
권장 학습 경로:
- 공식 spec을 읽고 JSON-RPC 메시지 흐름을 이해합니다.
- FastMCP로 Hello World를 만들고 Inspector에서 디버깅합니다.
- 실제 Tool 세 가지 이상(파일, HTTP, DB)을 구현합니다.
- Resources와 Prompts를 추가하여 세 가지 원시 타입을 모두 경험합니다.
- HTTP 전송으로 전환하고 Docker로 클라우드에 배포합니다.
- Cursor에 연결하고 지식베이스 또는 GitHub 통합 프로젝트를 완료합니다.
아래 공개 참고 자료로 spec과 SDK를 검증하세요. 업스트림 저장소가 변경되면 링크된 출처를 권위 있는 기준으로 삼습니다.
Model Context Protocol — 공식 규격 및 문서
modelcontextprotocol/python-sdk — Python MCP SDK
modelcontextprotocol/typescript-sdk — TypeScript MCP SDK
modelcontextprotocol/inspector — MCP Inspector 디버깅 도구
절전 모드에 들어가는 노트북에서 MCP Server를 돌리면 tools/list 세션 끊김, 만료된 OAuth 토큰, 디스크 포화로 인한 벡터 저장소 손상, 네트워크 변경 후 SSE 연결 해제 등 반복 장애가 발생합니다. 이런 문제는 모델 선택보다 일상 개발에 더 큰 영향을 줍니다. 노트북 Docker도 호스트 절전을 그대로 물려받습니다. 무료 PaaS cold start는 장기 연결을 끊고, Lambda는 상시 SSE에 부적합합니다.
24/7 MCP Tool, 안정적 SSH, 예측 가능한 Apple Silicon 컴퓨트가 필요하다면 Cursor Agent와 커스텀 Server를 전용 베어메탈로 옮기는 것이 더 나은 선택일 수 있습니다. NOVAKVM은 Cursor MCP, 원격 Claude Desktop 개발, 동일 위치 벡터 저장소를 위한 다리역 Mac Mini M4 / M4 Pro 유연 대여를 제공합니다. 가격 페이지, 주문 페이지, 배포 문의는 고객 센터를 확인하세요.