레슨 5 15분

나만의 MCP 서버 만들기 (Python)

Python FastMCP SDK로 첫 MCP 서버를 처음부터 구축 — 도구 정의, 리소스 노출, Claude Desktop 연결까지.

🔄 레슨 4에서 도구, 리소스, 프롬프트 — MCP의 세 가지 핵심 요소를 배웠습니다. 이론은 됐으니 이제 코드를 작성합니다.

FastMCP: Python MCP SDK

MCP 서버를 만드는 가장 쉬운 방법은 FastMCP SDK입니다. 프로토콜의 복잡한 부분(JSON-RPC, 핸드셰이크, 세션 관리)을 전부 처리해주기 때문에, 여러분은 도구가 “무엇을 하는지"만 작성하면 됩니다.

설치:

pip install fastmcp

첫 번째 서버: 할 일 관리

간단한 할 일(Todo) 관리 서버를 만들어보겠습니다. Claude가 할 일을 추가하고, 목록을 보고, 완료 처리할 수 있는 서버입니다.

from fastmcp import FastMCP

mcp = FastMCP("todo-manager")

# 메모리에 할 일 저장 (실제 서비스라면 DB 사용)
todos = []

@mcp.tool()
def add_todo(task: str, priority: str = "medium") -> str:
    """새 할 일을 추가합니다.

    Args:
        task: 할 일 내용
        priority: 우선순위 (high, medium, low)
    """
    todo = {"task": task, "priority": priority, "done": False}
    todos.append(todo)
    return f"추가됨: {task} (우선순위: {priority})"

@mcp.tool()
def list_todos() -> str:
    """현재 할 일 목록을 반환합니다."""
    if not todos:
        return "할 일이 없습니다."
    lines = []
    for i, t in enumerate(todos):
        status = "✅" if t["done"] else "⬜"
        lines.append(f"{i+1}. {status} [{t['priority']}] {t['task']}")
    return "\n".join(lines)

@mcp.tool()
def complete_todo(index: int) -> str:
    """할 일을 완료 처리합니다.

    Args:
        index: 할 일 번호 (1부터 시작)
    """
    if 1 <= index <= len(todos):
        todos[index - 1]["done"] = True
        return f"완료: {todos[index - 1]['task']}"
    return f"잘못된 번호: {index}"

핵심 포인트:

  • @mcp.tool() 데코레이터로 함수를 MCP 도구로 노출
  • 함수의 docstring이 AI에게 보이는 도구 설명
  • 타입 힌트(str, int)가 자동으로 JSON Schema로 변환
  • 반환값은 문자열 — AI가 읽을 수 있는 형태

Quick Check: 위 코드에서 priority에 기본값 "medium"이 설정되어 있습니다. AI가 이 도구를 호출할 때 priority를 생략하면 어떻게 될까요? (기본값 “medium"이 적용됩니다. MCP는 타입 힌트와 기본값을 JSON Schema로 변환해서 AI에게 알려줍니다.)

리소스 추가하기

도구만으로는 부족합니다. AI에게 배경 정보도 제공해야 합니다. 리소스를 추가합니다:

@mcp.resource("todo://stats")
def get_todo_stats() -> str:
    """할 일 통계를 반환합니다."""
    total = len(todos)
    done = sum(1 for t in todos if t["done"])
    high = sum(1 for t in todos if t["priority"] == "high" and not t["done"])
    return f"전체: {total}, 완료: {done}, 미완료: {total - done}, 긴급: {high}"

리소스는 @mcp.resource() 데코레이터에 URI를 지정합니다. 읽기 전용이고, 앱이 AI의 컨텍스트에 넣어줍니다.

Claude Desktop에 연결하기

서버 파일을 todo_server.py로 저장한 뒤, Claude Desktop 설정에 추가합니다:

{
  "mcpServers": {
    "todo": {
      "command": "uvx",
      "args": ["fastmcp", "run", "/full/path/to/todo_server.py"]
    }
  }
}

Claude Desktop을 재시작하면 망치 아이콘 옆에 도구 수가 늘어납니다. “할 일에 ‘이번 주 코드 리뷰’ 추가해줘"라고 말하면 Claude가 add_todo 도구를 호출합니다.

에러 처리: 서버가 죽지 않게

MCP 도구에서 에러가 나면 서버가 크래시될 수 있습니다. 반드시 try-except로 감싸세요:

@mcp.tool()
def query_database(sql: str) -> str:
    """SQL 쿼리를 실행합니다."""
    try:
        result = db.execute(sql)
        return format_result(result)
    except Exception as e:
        return f"쿼리 에러: {str(e)}"

에러 메시지를 문자열로 반환하면, AI가 에러 내용을 이해하고 다른 접근 방식을 시도할 수 있습니다.

실전 예제: 날씨 API 서버

좀 더 현실적인 예제 — 외부 API를 호출하는 MCP 서버:

import httpx
from fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_weather(city: str) -> str:
    """도시의 현재 날씨를 조회합니다.

    Args:
        city: 도시 이름 (예: Seoul, Busan)
    """
    try:
        url = f"https://wttr.in/{city}?format=j1"
        resp = httpx.get(url, timeout=10)
        data = resp.json()
        current = data["current_condition"][0]
        return (
            f"{city} 날씨: {current['weatherDesc'][0]['value']}, "
            f"기온: {current['temp_C']}°C, "
            f"체감: {current['FeelsLikeC']}°C, "
            f"습도: {current['humidity']}%"
        )
    except Exception as e:
        return f"날씨 조회 실패: {str(e)}"

이렇게 하면 Claude한테 “서울 날씨 알려줘"라고 말하면 실시간 데이터를 가져옵니다.

Quick Check: 위 날씨 서버를 리소스로 만들면 어떤 차이가 있을까요? (리소스는 읽기 전용 데이터 제공입니다. 도시를 동적으로 지정해서 API를 호출하는 건 도구가 적합합니다. 리소스는 “서울의 연간 평균 기온” 같은 고정 데이터에 적합합니다.)

서버 테스트: 로컬에서 확인

Claude Desktop에 연결하기 전에 로컬에서 테스트할 수 있습니다:

# FastMCP 개발 서버 실행
fastmcp dev todo_server.py

브라우저에서 MCP Inspector가 열리며, 도구 목록 확인, 수동 호출, 결과 검증이 가능합니다.

서버 구조 정리

my-mcp-server/
├── server.py          # MCP 서버 메인 코드
├── pyproject.toml     # Python 프로젝트 설정
└── README.md          # 서버 사용법 문서

pyproject.toml 최소 설정:

[project]
name = "my-mcp-server"
version = "0.1.0"
dependencies = ["fastmcp"]

핵심 정리

  • FastMCP SDK로 Python MCP 서버를 간단하게 구축 가능
  • @mcp.tool() = 도구 정의, @mcp.resource() = 리소스 정의
  • docstring과 타입 힌트가 자동으로 AI에게 보이는 인터페이스가 됨
  • Claude Desktop 연결: claude_desktop_config.json에 uvx 명령어 추가
  • 에러 처리 필수 — try-except로 감싸서 에러 메시지를 문자열로 반환
  • fastmcp dev로 로컬 테스트 후 Claude Desktop에 연결

다음 레슨

MCP 서버를 만드는 법을 알았습니다. 레슨 6에서는 Claude Code — 터미널 기반 AI 코딩 도구 — 에서 MCP를 활용하는 방법을 다룹니다. claude mcp add 명령어로 개발 워크플로우를 자동화하는 실전 기법.

이해도 체크

1. FastMCP에서 도구를 정의할 때 사용하는 데코레이터는?

2. Python MCP 서버를 Claude Desktop에 연결할 때 command 필드에 넣는 값은?

3. MCP 서버에서 에러가 발생했을 때의 올바른 처리 방법은?

모든 문제에 답해야 확인할 수 있어요

먼저 위의 퀴즈를 완료하세요

관련 스킬