02-구조
02 — SDK 골격
섹션 제목: “02 — SDK 골격”Python: FastMCP
섹션 제목: “Python: FastMCP”FastMCP는 Python SDK의 고수준 API다. 데코레이터로 도구·리소스·프롬프트를 선언한다 — mcp-python-sdk.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("acme-erp")
@mcp.tool()def get_invoice(invoice_id: str) -> dict: """청구서 ID로 청구서를 조회한다.
Args: invoice_id: 8자리 청구서 번호 (예: "INV-0042") """ # ... 사내 API 호출 return {"id": invoice_id, "amount": 1234.56, "status": "paid"}
@mcp.resource("erp://customer/{customer_id}")def customer_profile(customer_id: str) -> str: """고객 프로필을 마크다운으로 반환.""" return f"# Customer {customer_id}\n..."
@mcp.prompt()def monthly_report_template(month: str) -> str: """월간 보고서 작성 프롬프트.""" return f"다음 형식으로 {month}월 매출 보고서를 작성하라: ..."
if __name__ == "__main__": mcp.run(transport="stdio")핵심 포인트:
- 타입 힌트가 곧 입력 스키마다. SDK가 JSON Schema를 자동 생성.
- docstring이 곧 도구 설명이다. LLM이 이걸 보고 호출 여부를 판단.
- 리소스 URI는 템플릿 변수를 가질 수 있다(
{customer_id}). mcp.run(transport="stdio")— Streamable HTTP는transport="streamable-http".
라이프사이클·로깅
섹션 제목: “라이프사이클·로깅”import sys, logging
# 절대 stdout으로 로깅하지 말 것logging.basicConfig(stream=sys.stderr, level=logging.INFO)복잡한 상태(DB 커넥션, HTTP 세션)는 lifespan 컨텍스트로 관리한다 — mcp-python-sdk.
TypeScript: @modelcontextprotocol/sdk
섹션 제목: “TypeScript: @modelcontextprotocol/sdk”import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod";
const server = new McpServer({ name: "acme-erp", version: "0.1.0" });
server.tool( "get_invoice", "청구서 ID로 청구서를 조회한다", { invoice_id: z.string().describe("8자리 청구서 번호") }, async ({ invoice_id }) => ({ content: [{ type: "text", text: JSON.stringify({ invoice_id, amount: 1234.56 }) }], }));
const transport = new StdioServerTransport();await server.connect(transport);핵심: Zod 스키마가 입력 검증과 JSON Schema 생성을 동시에. — mcp-typescript-sdk
도구 반환 형식
섹션 제목: “도구 반환 형식”도구는 content 배열을 반환한다. 가장 흔한 형식:
{ "content": [{ "type": "text", "text": "결과 텍스트" }] }이미지(type: "image"), 리소스 참조(type: "resource")도 가능. 큰 결과는 요약 + 페이지네이션.
에러 처리
섹션 제목: “에러 처리”도구가 실패하면 예외를 던지지 말고 isError: true로 반환하는 게 안전하다(SDK가 자동 처리). 이유: LLM이 에러 메시지를 읽고 다음 행동을 결정할 수 있어야 한다.
샘플링(Sampling) — 서버가 LLM을 부를 때
섹션 제목: “샘플링(Sampling) — 서버가 LLM을 부를 때”고급 기능: 서버가 클라이언트에 “이 텍스트를 LLM으로 요약해줘”를 요청할 수 있다 — mcp-sampling. 호스트는 사용자 동의 후 모델을 호출. 첫 서버에는 필요 없음, 에이전트형 서버를 만들 때 검토.