CPN 한국어 자습서 · 외부 문서 한국어 미러
MCP 문서 · Develop
Build an MCP server · 원문: modelcontextprotocol.io/docs/develop/build-server
아래는 원문을 한국어로 옮긴 미러입니다. 코드·명령은 원문 그대로이며, 가장 최신 정보는 하단 원문 링크에서 확인하세요.
Claude for Desktop 및 기타 클라이언트에서 사용할 수 있는 서버를 직접 만들어 봅니다.
이 튜토리얼에서는 간단한 MCP 날씨 서버를 구축하고 호스트(Claude for Desktop)에 연결합니다.
get_alerts와 get_forecast 두 가지 도구(tool)를 노출하는 서버를 구축합니다. 그런 다음 MCP 호스트(Claude for Desktop)에 서버를 연결합니다.
참고: 서버는 어떤 클라이언트에도 연결할 수 있습니다. 여기서는 간단히 Claude for Desktop을 사용하지만, 클라이언트 직접 구축 가이드도 있습니다.
MCP 서버(server)는 다음 세 가지 주요 기능을 제공할 수 있습니다.
이 튜토리얼은 주로 도구(tool)에 집중합니다.
날씨 서버 구축을 시작합니다! 완성된 코드는 여기서 확인할 수 있습니다.
이 퀵스타트는 다음에 익숙하다고 가정합니다.
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은 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에 다음을 알려줍니다.
uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py를 실행하여 시작합니다.파일을 저장하고 Claude for Desktop을 재시작합니다.
날씨 서버 구축을 시작합니다! 완성된 코드는 여기서 확인할 수 있습니다.
이 퀵스타트는 다음에 익숙하다고 가정합니다.
MCP 서버를 구현할 때 로깅 처리에 주의해야 합니다.
STDIO 기반 서버의 경우: console.log()는 기본적으로 표준 출력(stdout)에 쓰므로 절대 사용하지 마세요. stdout에 쓰면 JSON-RPC 메시지가 손상됩니다.
HTTP 기반 서버의 경우: HTTP 응답에 영향을 주지 않으므로 표준 출력 로깅을 사용해도 됩니다.
console.error()를 사용하거나 stderr나 파일에 기록하는 로깅 라이브러리를 사용하세요.// ❌ 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.json에 type: "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",
});
마지막으로 서버를 실행하는 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에서 다음과 같이 설정합니다.
{
"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를 사용하므로 미국 지역 쿼리만 작동합니다.
질문을 하면 다음 과정이 진행됩니다.
Claude for Desktop 로그 확인
MCP 관련 Claude.app 로그는 ~/Library/Logs/Claude의 로그 파일에 기록됩니다.
mcp.log: MCP 연결 및 연결 실패에 대한 일반 로그입니다.mcp-server-SERVERNAME.log: 해당 서버의 오류(stderr) 로그입니다.다음 명령어로 최근 로그를 확인하고 새 로그를 실시간으로 볼 수 있습니다.
# Check Claude's logs for errors tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
서버가 Claude에 표시되지 않는 경우
claude_desktop_config.json 파일 구문을 확인하세요.주의: Claude for Desktop을 올바르게 재시작하려면 애플리케이션을 완전히 종료해야 합니다. * Windows: 시스템 트레이의 Claude 아이콘을 우클릭하고 "종료" 또는 "나가기"를 선택합니다. * macOS: Cmd+Q를 사용하거나 메뉴 모음에서 "Claude 종료"를 선택합니다. 창을 닫는 것만으로는 애플리케이션이 완전히 종료되지 않아 MCP 서버 설정 변경이 적용되지 않습니다.
도구 호출이 자동으로 실패하는 경우
Claude가 도구를 사용하려 하지만 실패하는 경우:
아무것도 작동하지 않으면 어떻게 해야 하나요?
더 나은 디버깅 도구와 자세한 지침은 디버깅 가이드를 참조하세요.
오류: Failed to retrieve grid point data
이 오류는 보통 다음을 의미합니다.
해결 방법:
참고: 고급 문제 해결은 MCP 디버깅 가이드를 참조하세요.
원문(영어): https://modelcontextprotocol.io/docs/develop/build-server · 본 문서는 학습용 한국어 번역이며 원본의 권리는 원저작자(Model Context Protocol)에게 있습니다.
원문(영어): https://modelcontextprotocol.io/docs/develop/build-server