부록 A. MCP 서버 직접 개발 실습

Model Context Protocol(MCP) 서버를 TypeScript와 Python으로 직접 만들어 Claude Code/Claude Desktop에 연결하는 전 과정을 다룹니다. 공용 MCP 서버가 없는 사내 시스템(JIRA, 사내 ERP, 자체 DB)을 AI에 연결해야 할 때 필요한 핵심 기술입니다.

선수 지식: 5.10 MCP 개념 · Node.js 또는 Python 기본 · JSON-RPC 개념. 클라이언트 사용 경험(Playwright MCP 등)이 있으면 이해가 쉽습니다.
2026-04 갱신 — MCP 응답 한도 500K 문자
Claude Code v2.1.89(2026-04 첫째 주 릴리스)부터 MCP 도구의 응답 한도가 500,000 문자로 대폭 상향됐습니다. 큰 파일·DB 쿼리 결과·로그를 잘림 없이 그대로 반환할 수 있어, 페이지네이션 래퍼나 서머라이저 미들웨어가 더 이상 필수가 아닙니다. 또한 PreToolUse 훅에 새 옵션 defer가 추가되어 외부 시그널(인간 승인·CI 게이트 등) 대기 패턴을 자연스럽게 구현할 수 있습니다.

A.1 언제 MCP 서버를 직접 만들까?

상황해결책개발 비용
공개 서비스(GitHub, Slack, Gmail, Notion 등)공식/커뮤니티 MCP 서버 설치낮음(설정만)
사내 REST API(주문 조회, 사용자 검색 등)커스텀 MCP 서버 작성중간(1~3일)
레거시 DB 직접 쿼리커스텀 MCP 서버 + SQL 래퍼중간(2~5일)
멀티 시스템 오케스트레이션에이전트 레벨 하네스 + MCP높음(1~4주)

핵심 판단 기준: AI가 같은 내부 데이터/작업을 반복적으로 요구받고 있고, 프롬프트나 RAG로 처리하기에는 "실행(write)"이 필요할 때 MCP 서버가 정답입니다.

A.2 MCP 서버 아키텍처 요약

MCP 서버는 3가지 기본 기능을 JSON-RPC 2.0 위에서 노출합니다:

  • Tools — AI가 호출할 수 있는 함수(예: search_orders(customer_id)). 가장 자주 씁니다.
  • Resources — AI가 읽을 수 있는 문서/데이터(예: file://runbook.md). RAG 대체로 활용.
  • Prompts — 재사용 가능한 프롬프트 템플릿. 팀 표준 프롬프트를 배포할 때 유용.

전송 계층은 stdio(로컬 프로세스) 또는 HTTP/SSE(원격). 사내 배포는 stdio로 시작해 팀 공유 필요 시 HTTP로 승격하는 패턴이 안전합니다.

A.3 실습 1 — Hello MCP 서버 (TypeScript, 5분)

가장 작은 MCP 서버를 만들어 Claude Desktop에 연결해 봅니다.

Step 1: 프로젝트 초기화

mkdir hello-mcp && cd hello-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init

Step 2: 서버 코드 작성 (src/index.ts)

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: "hello-mcp", version: "0.1.0" });

// 가장 단순한 Tool: 현재 시간 반환
server.tool(
  "get_current_time",
  "현재 서버 시간을 ISO 8601로 반환합니다.",
  {},
  async () => ({
    content: [{ type: "text", text: new Date().toISOString() }]
  })
);

// 인자 받는 Tool: 두 수 덧셈
server.tool(
  "add",
  "두 숫자를 더합니다.",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);

Step 3: Claude Desktop에 등록

~/.config/claude/claude_desktop_config.json(Windows: %APPDATA%\Claude\claude_desktop_config.json)에 추가:

{
  "mcpServers": {
    "hello": {
      "command": "npx",
      "args": ["tsx", "C:/path/to/hello-mcp/src/index.ts"]
    }
  }
}

Claude Desktop을 재시작하면 연결된 MCP가 🔌 아이콘으로 표시됩니다. "지금 몇 시야?" 또는 "3 더하기 5"라고 물어보면 Tool이 호출됩니다.

A.4 실습 2 — 사내 REST API 연동 MCP (Python, 30분)

사내 주문 관리 API를 MCP로 감싸 AI가 주문을 조회/취소할 수 있게 합니다.

Step 1: 의존성

pip install mcp httpx python-dotenv

Step 2: 서버 코드 (order_mcp.py)

import os
import httpx
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv

load_dotenv()
BASE = os.environ["ORDER_API_BASE"]
TOKEN = os.environ["ORDER_API_TOKEN"]

mcp = FastMCP("order-mcp")

def _client():
    return httpx.Client(
        base_url=BASE,
        headers={"Authorization": f"Bearer {TOKEN}"},
        timeout=10.0,
    )

@mcp.tool()
def search_orders(customer_id: str, status: str | None = None) -> list[dict]:
    """고객 ID로 주문 목록을 조회합니다. status로 필터링 가능(pending/paid/shipped/cancelled)."""
    params = {"customer_id": customer_id}
    if status: params["status"] = status
    with _client() as c:
        r = c.get("/orders", params=params)
        r.raise_for_status()
        return r.json()["items"]

@mcp.tool()
def cancel_order(order_id: str, reason: str) -> dict:
    """주문을 취소합니다. reason은 취소 사유(감사 로그용, 필수)."""
    with _client() as c:
        r = c.post(f"/orders/{order_id}/cancel", json={"reason": reason})
        r.raise_for_status()
        return r.json()

if __name__ == "__main__":
    mcp.run()

Step 3: 안전 장치 3가지 (필수)

  1. 쓰기 작업(cancel_order)은 화이트리스트

    Claude Code .claude/settings.jsonpermissions.allow에 특정 MCP Tool만 허용하고, 나머지는 프롬프트로 확인.

  2. 토큰은 최소 권한

    ORDER_API_TOKEN은 주문 조회/취소 스코프만. 계정 삭제 스코프는 절대 포함하지 말 것.

  3. 감사 로그

    Tool 내부에서 audit.log() 호출. 누가/언제/왜 실행했는지 남겨야 사고 시 추적 가능.

A.5 디버깅과 테스트

MCP Inspector — 공식 디버거

npx @modelcontextprotocol/inspector python order_mcp.py

브라우저 UI에서 Tool 목록, 입력/출력 스키마, 실제 호출 결과를 확인할 수 있습니다. Claude에 연결하기 전 반드시 Inspector로 먼저 검증하세요.

단위 테스트 패턴

import pytest
from order_mcp import search_orders

def test_search_orders_empty(httpx_mock):
    httpx_mock.add_response(
        url="http://api/orders?customer_id=c1",
        json={"items": []}
    )
    assert search_orders("c1") == []

A.6 배포 체크리스트

항목질문통과 기준
인증토큰이 코드/레포에 하드코딩되어 있지 않은가?.env / Secret Manager
스코프Tool 권한이 실제 업무 범위를 넘지 않는가?읽기/쓰기 분리, 최소 권한
감사쓰기 작업이 모두 로그되는가?구조화 로그 + 보존 90일↑
에러API 장애 시 AI에게 명확한 에러 메시지를 주는가?HTTPError → 한국어 설명으로 변환
성능Tool 응답이 5초 이내인가?타임아웃 + 페이지네이션
테스트Inspector로 정상 동작 확인 + 단위 테스트전체 Tool 커버리지

A.7 더 읽을거리

← 메인으로 돌아가기 · 최근 업데이트: 2026.04.17