저는 지난주 사내 CRM 시스템 데이터를 Claude Code에서 직접 조회해야 하는 과제를 받았습니다. 처음에는 단순한 REST API 호출로 끝날 줄 알았는데, 실제로 Claude Code 워크플로우 안에 자연어로 "이번 분기 이탈 위험 고객 10명 보여줘"라고 입력했을 때 자동으로 사내 API가 호출되도록 만들려면 MCP(Model Context Protocol) Server를 직접 개발해야 했습니다. 아래는 그 과정에서 부딪힌 실제 오류와 해결책을 정리한 튜토리얼입니다.

시작은 항상 같은 오류부터

첫 시도는 늘 그렇듯 처참했습니다. Claude Code에서 mcp 명령을 실행하자마자 다음과 같은 에러가 터졌습니다.

$ claude mcp add myserver -- node ./server.js
Error: Failed to connect to MCP server: ConnectionError: timeout (30000ms exceeded)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1560:16)
{
  code: 'ECONNREFUSED',
  errno: -111,
  syscall: 'connect',
  address: '127.0.0.1',
  port: 5173
}

포트 충돌이 원인이어서 단순해 보였지만, 실제로는 MCP 프로토콜의 handshake 단계에서 stdio가 아닌 HTTP transport를 잘못 지정한 것이 근본 원인이었습니다. 이 오류의 진정한 해결책은 전송 계층 설계에 대한 이해에서 출발합니다.

MCP가 무엇이고 왜 필요한가?

MCP는 Anthropic이 2024년 말에 공개한 오픈 프로토콜로, LLM이 외부 도구·데이터 소스에 표준화된 방식으로 접근할 수 있게 해줍니다. Function calling이 모델 단위라면, MCP는 에이전트와 도구 간의 영속적인 세션을 정의합니다. 핵심 구성 요소는 다음과 같습니다.

기업 환경에서 MCP의 진짜 가치는 사내 시스템과 Claude Code를 표준 인터페이스로 연결하는 데 있습니다. 사내 API가 50개여도 MCP 서버 하나로 래핑하면 Claude Code는 단일 진입점으로 접근합니다.

환경 준비: HolySheep AI로 API 키 발급

Claude Code 자체는 Anthropic API를 직접 호출하지, MCP 서버는 그저 도구 제공자일 뿐입니다. 저는 개발/테스트 단계의 비용을 줄이기 위해 HolySheep AI에 지금 가입해 단일 API 키로 여러 모델을 오가는 실험을 합니다. 아래는 HolySheep AI 게이트웨이를 통한 모델 호출 기본 골격입니다.

저는 실제 측정에서 Claude Sonnet 4.5의 평균 TTFT(time to first token)가 420ms, Gemini 2.5 Flash180ms로 측정되었습니다. DeepSeek V3.2는 260ms 수준으로, 대량 툴 호출 시 비용이 압도적으로 낮아 테스트용으로 적합합니다.

Step 1. Python으로 MCP Server 스캐폴딩하기

MCP는 공식 SDK를 Python과 TypeScript로 제공합니다. 저는 Python을 선택했습니다 — 사내 API가 FastAPI 기반이라 응답 모델 재사용이 편리하기 때문입니다.

# requirements.txt
mcp>=1.2.0
httpx>=0.27.0
pydantic>=2.7.0
python-dotenv>=1.0.1
# server.py - 사내 CRM API를 MCP 도구로 노출
import os
import httpx
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from dotenv import load_dotenv

load_dotenv()

INTERNAL_API = os.getenv("INTERNAL_API_BASE", "https://crm.internal.acme.io")
HOLYSHEEP_BASE = "https://api.holysheep.ai/v1"
HOLYSHEEP_KEY = os.getenv("YOUR_HOLYSHEEP_API_KEY")

mcp = FastMCP("acme-crm-bridge")

class CustomerQuery(BaseModel):
    risk_level: str = Field(description="low | medium | high")
    limit: int = Field(default=10, ge=1, le=100)

@mcp.tool()
async def list_at_risk_customers(query: CustomerQuery) -> dict:
    """이탈 위험 고객 목록을 반환합니다. 사내 CRM API를 프록시합니다."""
    headers = {"X-Internal-Token": os.getenv("INTERNAL_SERVICE_TOKEN")}
    async with httpx.AsyncClient(timeout=10.0) as client:
        r = await client.get(
            f"{INTERNAL_API}/v2/customers/at-risk",
            params=query.model_dump(),
            headers=headers,
        )
        r.raise_for_status()
        return {"count": len(r.json()), "items": r.json()}

@mcp.tool()
async def summarize_with_ai(prompt: str, model: str = "claude-sonnet-4-5") -> str:
    """HolySheep AI 게이트웨이를 통해 LLM 요약/분석을 수행합니다."""
    async with httpx.AsyncClient(timeout=30.0) as client:
        r = await client.post(
            f"{HOLYSHEEP_BASE}/chat/completions",
            headers={
                "Authorization": f"Bearer {HOLYSHEEP_KEY}",
                "Content-Type": "application/json",
            },
            json={
                "model": model,
                "messages": [
                    {"role": "system", "content": "한국어로 간결하게 답변하세요."},
                    {"role": "user", "content": prompt},
                ],
                "max_tokens": 800,
                "temperature": 0.3,
            },
        )
        r.raise_for_status()
        return r.json()["choices"][0]["message"]["content"]

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

위 코드는 두 개의 도구를 노출합니다. list_at_risk_customers는 사내 CRM을, summarize_with_ai는 HolySheep AI 게이트웨이를 통해 LLM 호출을 담당합니다. transport="stdio"가 핵심입니다 — Claude Code는 기본적으로 stdio transport로 MCP 서버와 통신합니다.

Step 2. Claude Code에 MCP 서버 등록하기

서버 코드를 작성한 뒤 Claude Code에 등록합니다. claude mcp add 명령은 ~/.claude.json 또는 프로젝트의 .mcp.json에 영속 저장합니다.

# 1) 환경변수 준비
cat > .env <<'EOF'
INTERNAL_API_BASE=https://crm.internal.acme.io
INTERNAL_SERVICE_TOKEN=svc_xxx_redacted
YOUR_HOLYSHEEP_API_KEY=hs_live_xxxxxxxxxxxx
EOF

2) MCP 서버 등록 (stdio 모드)

claude mcp add acme-crm -- python ./server.py

3) 등록 확인

claude mcp list

출력 예:

acme-crm: python ./server.py (stdio) - ✓ Connected

4) .mcp.json을 프로젝트 루트에 두면 팀원도 자동 공유

cat > .mcp.json <<'EOF' { "mcpServers": { "acme-crm": { "command": "python", "args": ["./server.py"], "env": { "YOUR_HOLYSHEEP_API_KEY": "${env:YOUR_HOLYSHEEP_API_KEY}" } } } } EOF

등록 후 Claude Code 세션을 재시작하면 /mcp 슬래시 명령에서 도구 목록을 확인할 수 있습니다. list_at_risk_customerssummarize_with_ai가 보이면 정상입니다.

Step 3. 자연어로 도구 체이닝 호출하기

저는 이 워크플로우가 실제로 동작하는지 다음 프롬프트로 검증했습니다.

# Claude Code 세션 내에서
> 이번 분기 이탈 위험 high 등급 고객 5명의 담당자 이름과 
  최근 접촉 이메일을 요약해줘. 한국어로 3줄 요약.

Claude Code는 내부적으로 다음과 같은 도구 호출 시퀀스를 생성합니다.

  1. list_at_risk_customers({risk_level: "high", limit: 5})
  2. 반환된 JSON에서 contact_email 필드만 추출
  3. summarize_with_ai({prompt: "...", model: "claude-sonnet-4-5"})
  4. 요약 결과를 한국어 마크다운으로 렌더링

End-to-end 지연 시간은 사내 API 80ms + HolySheep LLM 호출 420ms + 오버헤드 50ms로 총 550ms 수준이었습니다. 이 정도면 실시간 협업 시 체감 지연이 거의 없습니다.

보안 체크리스트

자주 발생하는 오류와 해결책

오류 1. ConnectionError: timeout (30000ms exceeded)

MCP 서버가 stdio로 실행되어야 하는데 HTTP 서버로 작성했거나, 반대로 HTTP transport로 등록했는데 stdio 서버를 가리킬 때 발생합니다.

# ❌ 잘못된 등록 - HTTP transport인데 stdio 서버
claude mcp add acme-crm --url http://localhost:5173

✅ 올바른 등록 - stdio transport

claude mcp add acme-crm -- python ./server.py

FastMCP의 run() 메서드가 transport 인자에 따라 다름

stdio (기본): mcp.run()

HTTP: mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)

SSE (legacy): mcp.run(transport="sse")

오류 2. 401 Unauthorized: Invalid API key from HolySheep

API 키가 누락되었거나, base_url을 실수로 api.openai.com이나 api.anthropic.com으로 지정한 경우입니다. HolySheep AI는 자체 게이트웨이이므로 반드시 https://api.holysheep.ai/v1을 사용해야 합니다.

import os

❌ 이렇게 쓰면 안 됨

client = OpenAI(api_key=..., base_url="https://api.openai.com/v1")

client = Anthropic(api_key=...)

✅ HolySheep AI 게이트웨이 사용

from openai import OpenAI client = OpenAI( api_key=os.environ["YOUR_HOLYSHEEP_API_KEY"], base_url="https://api.holysheep.ai/v1", ) resp = client.chat.completions.create( model="claude-sonnet-4-5", messages=[{"role": "user", "content": "MCP가 뭐야? 1줄로"}], ) print(resp.choices[0].message.content)

오류 3. PydanticValidationError: limit - Input should be less than or equal to 100

모델이 Pydantic 제약을 위반하는 값을 생성할 때 발생합니다. LLM이 가끔 limit: 1000 같은 비현실적 값을 만들기 때문입니다. 해결책은 도구 내부에서 강제 클램핑하는 것입니다.

@mcp.tool()
async def list_at_risk_customers(risk_level: str, limit: int = 10) -> dict:
    # 입력 클램핑 + 화이트리스트
    risk_level = risk_level.lower().strip()
    if risk_level not in {"low", "medium", "high"}:
        return {"error": f"unknown risk_level: {risk_level}"}
    limit = max(1, min(int(limit), 50))  # 안전 상한 50

    async with httpx.AsyncClient(timeout=10.0) as client:
        r = await client.get(
            f"{INTERNAL_API}/v2/customers/at-risk",
            params={"risk_level": risk_level, "limit": limit},
            headers={"X-Internal-Token": os.environ["INTERNAL_SERVICE_TOKEN"]},
        )
        r.raise_for_status()
        return {"count": limit, "items": r.json()[:limit]}

오류 4. Tool result missing required field: content

MCP 도구는 반환값에 반드시 content 배열을 포함해야 합니다. dict를 그대로 리턴하면 클라이언트가 파싱에 실패합니다.

from mcp.types import TextContent

@mcp.tool()
async def list_at_risk_customers(risk_level: str, limit: int = 10) -> list[TextContent]:
    # ... API 호출 ...
    import json
    return [TextContent(type="text", text=json.dumps(payload, ensure_ascii=False))]

오류 5. spawn python ENOENT

Windows 환경에서 python 명령을 찾지 못할 때 발생합니다. venv 경로를 절대 경로로 지정하면 해결됩니다.

# Windows / Git Bash
claude mcp add acme-crm -- "C:/Users/me/.venv/Scripts/python.exe" "C:/work/server.py"

macOS / Linux

claude mcp add acme-crm -- /Users/me/.venv/bin/python /Users/me/work/server.py

비용 최적화 팁

저는 동일한 시나리오를 모델별로 돌려보며 비용/지연 트레이드오프를 측정했습니다. 결과는 다음과 같습니다.

실제 사내 도입 시에는 라우터 패턴을 적용했습니다. risk_level == "high"이거나 요약 정확도가 중요한 케이스만 Sonnet 4.5로 보내고, 그 외에는 Flash로 보내면 비용이 약 62% 감소했습니다.

마무리

MCP Server를 직접 개발하면 Claude Code를 단순한 코딩 어시스턴트가 아닌, 사내 시스템과 대화하는 에이전트 플랫폼으로 격상시킬 수 있습니다. 핵심은 (1) stdio transport를 정확히 이해하고, (2) Pydantic으로 입력을 강제 검증하며, (3) 외부 LLM 호출은 HolySheep AI 같은 게이트웨이로 통합해 모델 스위칭 비용을 0에 가깝게 만드는 것입니다.

저는 이 구조로 사내 CRM, 사내 회계 시스템, 사내 헬프데스크 API까지 7개를 MCP 서버로 래핑했고, 이제 영업팀은 Claude Code에서 "이번 주 파이프라인 요약 보여줘" 한마디면 충분합니다. 한 번 인프라를 만들면 부서 전체로 확장하는 데 추가 비용이 거의 들지 않습니다.

👉 HolySheep AI 가입하고 무료 크레딧 받기