byteforce

CPN 한국어 자습서 · 외부 문서 한국어 미러

MCP 문서 · Develop

MCP 서버 구축

Build an MCP server · 원문: modelcontextprotocol.io/docs/develop/build-server

아래는 원문을 한국어로 옮긴 미러입니다. 코드·명령은 원문 그대로이며, 가장 최신 정보는 하단 원문 링크에서 확인하세요.

Claude for Desktop 및 기타 클라이언트에서 사용할 수 있는 서버를 직접 만들어 봅니다.

이 튜토리얼에서는 간단한 MCP 날씨 서버를 구축하고 호스트(Claude for Desktop)에 연결합니다.

만들 것

get_alertsget_forecast 두 가지 도구(tool)를 노출하는 서버를 구축합니다. 그런 다음 MCP 호스트(Claude for Desktop)에 서버를 연결합니다.

참고: 서버는 어떤 클라이언트에도 연결할 수 있습니다. 여기서는 간단히 Claude for Desktop을 사용하지만, 클라이언트 직접 구축 가이드도 있습니다.

MCP 핵심 개념

MCP 서버(server)는 다음 세 가지 주요 기능을 제공할 수 있습니다.

  1. 리소스(Resources): 클라이언트가 읽을 수 있는 파일 형태의 데이터 (API 응답, 파일 내용 등)
  2. 도구(Tools): LLM이 사용자 승인 하에 호출할 수 있는 함수
  3. 프롬프트(Prompts): 특정 작업을 수행하는 데 도움이 되는 미리 작성된 템플릿

이 튜토리얼은 주로 도구(tool)에 집중합니다.

Python

날씨 서버 구축을 시작합니다! 완성된 코드는 여기서 확인할 수 있습니다.

사전 지식

이 퀵스타트는 다음에 익숙하다고 가정합니다.

MCP 서버에서의 로깅

MCP 서버를 구현할 때 로깅 처리에 주의해야 합니다.

STDIO 기반 서버의 경우: stdout에 절대 쓰지 마세요. stdout에 쓰면 JSON-RPC 메시지가 손상되어 서버가 중단됩니다. print() 함수는 기본적으로 stdout에 쓰지만, file=sys.stderr를 사용하면 안전하게 사용할 수 있습니다.

HTTP 기반 서버의 경우: HTTP 응답에 영향을 주지 않으므로 표준 출력 로깅을 사용해도 됩니다.

모범 사례

빠른 예제

코드 · 명령
import sys
import logging

# ❌ Bad (STDIO)
print("Processing request")

# ✅ Good (STDIO)
print("Processing request", file=sys.stderr)

# ✅ Good (STDIO)
logging.info("Processing request")

시스템 요구사항

환경 설정

먼저 uv를 설치하고 Python 프로젝트와 환경을 설정합니다.

코드 · 명령
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
코드 · 명령
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

설치 후에는 터미널을 재시작해서 uv 명령어가 인식되도록 하세요.

이제 프로젝트를 생성하고 설정합니다.

코드 · 명령
# macOS/Linux
# 프로젝트 디렉터리 생성
uv init weather
cd weather

# 가상환경 생성 및 활성화
uv venv
source .venv/bin/activate

# 의존성 설치
uv add "mcp[cli]" httpx

# 서버 파일 생성
touch weather.py
코드 · 명령
# Windows
# 프로젝트 디렉터리 생성
uv init weather
cd weather

# 가상환경 생성 및 활성화
uv venv
.venv\Scripts\activate

# 의존성 설치
uv add mcp[cli] httpx

# 서버 파일 생성
new-item weather.py

이제 서버 구축을 시작합니다.

서버 구축

패키지 임포트 및 인스턴스 설정

weather.py 상단에 다음을 추가합니다.

코드 · 명령
from typing import Any

import httpx
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("weather")

# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"

FastMCP 클래스는 Python 타입 힌트와 독스트링을 사용해 도구 정의를 자동으로 생성하므로 MCP 도구를 쉽게 만들고 유지관리할 수 있습니다.

헬퍼 함수

다음으로 National Weather Service API에서 데이터를 조회하고 포맷하는 헬퍼 함수를 추가합니다.

코드 · 명령
async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None


def format_alert(feature: dict) -> str:
    """Format an alert feature into a readable string."""
    props = feature["properties"]
    return f"""
Event: {props.get("event", "Unknown")}
Area: {props.get("areaDesc", "Unknown")}
Severity: {props.get("severity", "Unknown")}
Description: {props.get("description", "No description available")}
Instructions: {props.get("instruction", "No specific instructions provided")}
"""

도구 실행 구현

도구 실행 핸들러는 각 도구의 실제 로직을 담당합니다. 다음과 같이 추가합니다.

코드 · 명령
@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)

    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."

    if not data["features"]:
        return "No active alerts for this state."

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\n---\n".join(alerts)


@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        forecast = f"""
{period["name"]}:
Temperature: {period["temperature"]}°{period["temperatureUnit"]}
Wind: {period["windSpeed"]} {period["windDirection"]}
Forecast: {period["detailedForecast"]}
"""
        forecasts.append(forecast)

    return "\n---\n".join(forecasts)

서버 실행

마지막으로 서버를 초기화하고 실행합니다.

코드 · 명령
def main():
    # Initialize and run the server
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()

서버가 완성되었습니다! uv run weather.py를 실행하면 MCP 서버가 시작되고 MCP 호스트의 메시지를 수신합니다.

이제 기존 MCP 호스트인 Claude for Desktop으로 서버를 테스트합니다.

Claude for Desktop으로 서버 테스트

참고: Claude for Desktop은 Linux에서는 아직 지원되지 않습니다. Linux 사용자는 클라이언트 구축 튜토리얼을 통해 방금 만든 서버에 연결하는 MCP 클라이언트를 구축할 수 있습니다.

먼저 Claude for Desktop이 설치되어 있는지 확인하세요. 여기서 최신 버전을 설치할 수 있습니다. 이미 설치된 경우 최신 버전으로 업데이트했는지 확인하세요.

사용하려는 MCP 서버를 Claude for Desktop에 설정해야 합니다. 텍스트 편집기에서 ~/Library/Application Support/Claude/claude_desktop_config.json 파일을 엽니다. 파일이 없으면 새로 만드세요.

예를 들어 VS Code가 설치된 경우:

코드 · 명령
# macOS/Linux
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
코드 · 명령
# Windows
code $env:AppData\Claude\claude_desktop_config.json

mcpServers 키에 서버를 추가합니다. MCP UI 요소는 최소한 하나의 서버가 올바르게 설정된 경우에만 Claude for Desktop에 표시됩니다.

날씨 서버를 다음과 같이 추가합니다.

코드 · 명령
{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
        "run",
        "weather.py"
      ]
    }
  }
}

주의: command 필드에 uv 실행 파일의 전체 경로를 입력해야 할 수도 있습니다. macOS/Linux에서는 which uv, Windows에서는 where uv로 경로를 확인할 수 있습니다.

참고: 서버의 절대 경로를 입력해야 합니다. macOS/Linux에서는 pwd, Windows 명령 프롬프트에서는 cd로 확인할 수 있습니다. Windows에서는 JSON 경로에 이중 백슬래시(\\) 또는 슬래시(/)를 사용하세요.

이 설정은 Claude for Desktop에 다음을 알려줍니다.

  1. "weather"라는 MCP 서버가 있습니다.
  2. uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py를 실행하여 시작합니다.

파일을 저장하고 Claude for Desktop을 재시작합니다.

TypeScript

날씨 서버 구축을 시작합니다! 완성된 코드는 여기서 확인할 수 있습니다.

사전 지식

이 퀵스타트는 다음에 익숙하다고 가정합니다.

MCP 서버에서의 로깅

MCP 서버를 구현할 때 로깅 처리에 주의해야 합니다.

STDIO 기반 서버의 경우: console.log()는 기본적으로 표준 출력(stdout)에 쓰므로 절대 사용하지 마세요. stdout에 쓰면 JSON-RPC 메시지가 손상됩니다.

HTTP 기반 서버의 경우: HTTP 응답에 영향을 주지 않으므로 표준 출력 로깅을 사용해도 됩니다.

모범 사례

빠른 예제

코드 · 명령
// ❌ Bad (STDIO)
console.log("Server started");

// ✅ Good (STDIO)
console.error("Server started"); // stderr is safe

시스템 요구사항

TypeScript의 경우 최신 버전의 Node.js가 설치되어 있어야 합니다.

환경 설정

nodejs.org에서 Node.js와 npm을 다운로드하여 설치하세요. 설치 확인:

코드 · 명령
node --version
npm --version

이 튜토리얼은 Node.js 16 이상이 필요합니다.

프로젝트를 생성하고 설정합니다.

코드 · 명령
# macOS/Linux
mkdir weather
cd weather
npm init -y
npm install @modelcontextprotocol/sdk zod@3
npm install -D @types/node typescript
mkdir src
touch src/index.ts

package.jsontype: "module"과 빌드 스크립트를 추가합니다.

코드 · 명령
{
  "type": "module",
  "bin": {
    "weather": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js"
  },
  "files": ["build"]
}

프로젝트 루트에 tsconfig.json을 생성합니다.

코드 · 명령
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

패키지 임포트 및 인스턴스 설정

src/index.ts 상단에 다음을 추가합니다.

코드 · 명령
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";

// Create server instance
const server = new McpServer({
  name: "weather",
  version: "1.0.0",
});

서버 실행 (TypeScript)

마지막으로 서버를 실행하는 main 함수를 구현합니다.

코드 · 명령
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

서버를 연결하려면 반드시 npm run build를 실행하세요.

Claude for Desktop으로 테스트 (TypeScript)

Claude for Desktop에서 다음과 같이 설정합니다.

코드 · 명령
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather/build/index.js"]
    }
  }
}

명령어로 테스트

Claude for Desktop이 weather 서버의 두 도구를 인식했는지 확인합니다. "파일, 커넥터 및 더보기 /" 아이콘을 찾아보세요.

더하기 아이콘을 클릭한 후 "커넥터" 메뉴를 마우스로 가리키면 weather 서버가 나열됩니다.

서버가 Claude for Desktop에 표시되지 않으면 문제 해결 섹션을 참조하세요.

서버가 "커넥터" 메뉴에 표시되면 Claude for Desktop에서 다음 명령어로 테스트할 수 있습니다.

참고: 이 날씨 서비스는 미국 National Weather Service를 사용하므로 미국 지역 쿼리만 작동합니다.

내부 동작 원리

질문을 하면 다음 과정이 진행됩니다.

  1. 클라이언트가 질문을 Claude에 전송합니다.
  2. Claude가 사용 가능한 도구를 분석하고 사용할 도구를 결정합니다.
  3. 클라이언트가 MCP 서버를 통해 선택된 도구를 실행합니다.
  4. 결과가 Claude에 반환됩니다.
  5. Claude가 자연어 응답을 생성합니다.
  6. 응답이 표시됩니다.

문제 해결

Claude for Desktop 통합 문제

Claude for Desktop 로그 확인

MCP 관련 Claude.app 로그는 ~/Library/Logs/Claude의 로그 파일에 기록됩니다.

다음 명령어로 최근 로그를 확인하고 새 로그를 실시간으로 볼 수 있습니다.

코드 · 명령
# Check Claude's logs for errors
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log

서버가 Claude에 표시되지 않는 경우

  1. claude_desktop_config.json 파일 구문을 확인하세요.
  2. 프로젝트 경로가 상대 경로가 아닌 절대 경로인지 확인하세요.
  3. Claude for Desktop을 완전히 재시작하세요.

주의: Claude for Desktop을 올바르게 재시작하려면 애플리케이션을 완전히 종료해야 합니다. * Windows: 시스템 트레이의 Claude 아이콘을 우클릭하고 "종료" 또는 "나가기"를 선택합니다. * macOS: Cmd+Q를 사용하거나 메뉴 모음에서 "Claude 종료"를 선택합니다. 창을 닫는 것만으로는 애플리케이션이 완전히 종료되지 않아 MCP 서버 설정 변경이 적용되지 않습니다.

도구 호출이 자동으로 실패하는 경우

Claude가 도구를 사용하려 하지만 실패하는 경우:

  1. Claude 로그에서 오류를 확인하세요.
  2. 서버가 오류 없이 빌드되고 실행되는지 확인하세요.
  3. Claude for Desktop을 재시작해 보세요.

아무것도 작동하지 않으면 어떻게 해야 하나요?

더 나은 디버깅 도구와 자세한 지침은 디버깅 가이드를 참조하세요.

날씨 API 문제

오류: Failed to retrieve grid point data

이 오류는 보통 다음을 의미합니다.

  1. 좌표가 미국 외 지역입니다.
  2. NWS API에 문제가 있습니다.
  3. 요청 속도가 제한되었습니다.

해결 방법:

참고: 고급 문제 해결은 MCP 디버깅 가이드를 참조하세요.

다음 단계

원문(영어): https://modelcontextprotocol.io/docs/develop/build-server · 본 문서는 학습용 한국어 번역이며 원본의 권리는 원저작자(Model Context Protocol)에게 있습니다.

원문(영어): https://modelcontextprotocol.io/docs/develop/build-server