
아래 설명은 영상에서 다룬 MCP(Model Context Protocol) 날씨 서버 예제를 처음부터 끝까지 따라 하면서, 왜 이런 순서로 작업하는지까지 친절하게 풀어쓴 가이드입니다. 각 단계는 “무엇을 왜 하는지”→“어떻게 하는지” 순서로 설명해, 처음 접하셔도 맥락이 자연스럽게 잡히도록 구성했습니다.
| 구분 | 특징 | 사용 시점 |
|---|---|---|
| 로컬(Local) | 내 PC에서 실행, 네트워크 지연 거의 없음 | 개발·테스트, 보안이 민감할 때 |
| 원격(Remote) | 클라우드에 배포, 인터넷으로 접근 | 여러 팀원이 동시에 쓰거나, 24시간 가동해야 할 때 |
이 튜토리얼은 로컬 서버를 설치·실행하는 과정을 다룹니다.
mkdir MCP-servers
cd MCP-servers
git clone https://github.com/modelcontextprotocol/quickstart-resources.git
cd quickstart-resources/weather-server-typescript
버전 확인
node -v # 예: v20.x
npm -v # 예: 10.x
두 명령이 정상 출력되면 설치 완료입니다.
없다면 설치
https://nodejs.org/에서 LTS 버전을 내려받아 기본 옵션으로 설치하면 됩니다.
npm install
ERESOLVE 오류원인: package-lock.json이 내 Node/npm 버전과 맞지 않아 의존성 충돌이 발생.
해결
rm package-lock.json
npm install --legacy-peer-deps # 필요 시
Stack Overflow와 npm 변경 로그에서도 동일한 해법을 권장합니다.
팁:
--legacy-peer-deps플래그는 일시적 방편입니다. 최종적으로는 해당 패키지를 최신 버전으로 올려 충돌을 제거하는 편이 좋습니다.
npm run build
package.json의 scripts 항목에 정의된 build 스크립트가 TypeScript를 JS로 트랜스파일해 build/ 폴더에 index.js를 생성합니다. node build/index.js
3333)로 기동하며, /weather?lat=…&lon=… 형태의 요청을 받으면 api.weather.gov로부터 예보를 가져와 가공한 뒤 JSON으로 돌려줍니다. Cursor
File → Settings → MCP Servers → Add Local Serverhttp://localhost:3333 입력 후 테스트.Claude Desktop
두 클라이언트는 자동으로 서버의 스키마를 읽어 기능 목록을 표시합니다.
| 위험 | 완화 방법 |
|---|---|
| 공급망(서드파티) 공격 | 사용 전 코드 리뷰·패키지 서명 확인 |
| 악성 npm 패키지 | Trust Score 높은 패키지만 사용, npm audit 주기적 실행 |
| 원격 서버 노출 | 방화벽·인증 추가, HTTPS 적용 |
공격 사례로는 ua-parser-js 변조 사건이 유명합니다. GitHub도 서명 강화로 대응 중입니다.
package-lock.json 삭제 후 재설치를 먼저 시도하세요.이제 로컬 서버 설정은 마쳤습니다. 다음 단계로 Python 버전이나 직접 만든 MCP 서버를 추가해 보면 MCP의 진가를 더 깊이 체험하실 수 있습니다.
| Transport | 특징 | 쓰임새 |
|---|---|---|
| stdio | 서버가 표준 입력/출력(stdin/stdout)으로 JSON 메시지를 주고받음. 로컬 PC에서 가장 간단 | Weather MCP처럼 PC에서 실행할 때 주로 사용 |
| SSE | HTTP + Server‑Sent Events. 서버가 이벤트를 push하고, 클라이언트는 HTTP POST로 요청 | 원격 클라우드 MCP, 실시간 알림 등 |
| 범위 | 파일 경로 | 언제 쓰나 |
|---|---|---|
| 글로벌 | ~/.cursor/mcp.json | 모든 프로젝트에서 공통으로 쓰는 서버 (예: 사내 날씨 MCP) |
| 프로젝트별 | <프로젝트>/.cursor/mcp.json | 해당 프로젝트 전용 툴 (예: 특정 DB 조회 MCP) |
Cursor 설정 UI(⌘/Ctrl + Shift + J → MCP 탭)에서도 동일 JSON을 편집할 수 있습니다.
{
"servers": {
"weather": {
"command": "node /absolute/path/to/weather-server-typescript/build/index.js",
"transport": "stdio",
"env": {
// 예시: "API_KEY": "abcdef123456"
}
}
}
}
Node와 Python 서버 모두 “쉘로 실행할 수 있는 명령만 다를 뿐 구성 방식은 완전히 동일”합니다.
toolCall을 준비한다. Enable YOLO를 켜면 팝업 없이 자동 실행 → 빠르지만 기업 환경에선 권장되지 않는다. RCE(Remote Code Execution) 위험: 오픈소스 MCP 서버가 악성 코드일 경우, 내 PC에서 그대로 실행되므로 공급망 공격(Supply‑Chain Attack)에 노출된다. 신뢰할 수 없는 저장소는 삼가고, 컨테이너 격리를 고려한다.
| 단계 | 명령 | 비고 |
|---|---|---|
| (1) 빌드 끝낸 디렉터리로 이동 | cd ~/MCP-servers/weather-server-typescript | 앞선 강좌와 동일 |
| (2) 실행 확인 | node build/index.js --help | 서버가 뜨는지 체크 |
(3) ~/.cursor/mcp.json 수정 | 위 예시 JSON 추가 | 절대 경로 주의 |
| (4) Cursor 재시작 → MCP 탭 확인 | ⌘/Ctrl + Shift + J | 등록 상태 표시 |
| (5) 채팅창에서 “서울 날씨 알려줘” | 승인 팝업 → Allow | 툴 콜 정상 동작 |
docs.cursor.com/context/model-context-protocol 페이지를 참조하세요. command만 바뀝니다.transport:"sse"와 HTTPS 엔드포인트를 사용해 클라우드 서버도 붙일 수 있습니다. get‑forecast, get‑alerts 두 가지 도구를 노출합니다. npm run build 후 build/index.js로 실행할 수 있습니다. toolCall 메시지를 준비해 MCP 서버에 전달(stdio 전송). mcp.json 작성 — 단 2 줄로 끝내기{
"servers": {
"weather": {
"command": "node /Users/park/MCP-servers/quickstart-resources/weather-server-typescript/build/index.js",
"transport": "stdio"
}
}
}
stdio, 원격 서버는 sse를 주로 사용. ("env":{"API_KEY":"…"}" 식으로 추가. 파일 위치
- 전역 사용:
~/.cursor/mcp.json→ 모든 프로젝트에서 사용.- 프로젝트 한정:
<proj>/.cursor/mcp.json→ 해당 폴더에서만 사용.
빌드 후 생성된 JS 파일 위치 확인
cd ~/MCP-servers/quickstart-resources/weather-server-typescript
npm run build # build/index.js 생성
pwd # 절대 경로 복사
경로가 틀리면 Cursor MCP 탭에 “client close” 오류가 표시됩니다. 제대로 된 경로로 수정 후 저장하면 즉시 “valid” 상태로 바뀝니다.
Allow 클릭 → get‑forecast, 이어서 get‑alerts 호출 로그가 보임. | 증상 | 원인 | 해결 |
|---|---|---|
| “client close” 오류 | command 경로 오타 | 절대 경로 재확인 |
| 서버가 즉시 종료 | Node 버전 ↔ 패키지 충돌 | package-lock.json 삭제 후 npm install |
| 팝업이 안 뜸 | YOLO 모드 ON | 설정에서 OFF |
transport:"sse"로 연결. defineTool({name:"my‑sql‑query", ...}) 패턴으로 새 도구 추가. 이제 Cursor에서 실시간 외부 데이터 활용까지 완벽히 연결했습니다. 궁금한 점이 있으면 언제든 물어보세요!
stdio 또는 sse 전송 방식으로 주고받아, IDE나 채팅 애플리케이션이 언어·플랫폼과 무관하게 같은 서버를 재사용하게 해 줍니다. cloud-desktop-config.json(이름은 버전에 따라 다를 수 있음)을 엽니다. {
"mcpServers": {
"weather": {
"command": "node /Users/park/MCP-servers/weather-server-typescript/build/index.js",
"transport": "stdio",
"args": [],
"env": {}
}
}
}
실제 사용 코드
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"/Users/bemore/Library/CloudStorage/OneDrive-Personal/DEV_AI/MCP/MCP-servers/quickstart-resources/weather-server-typescript/build/index.js"
],
"transport": "stdio",
"env": {}
}
}
}
stdio; 원격 호스팅 시 sse를 사용합니다. 꿀팁 — 터미널에서
pwd로 절대 경로를 복사해 붙여 넣으면 오타를 줄일 수 있습니다. 오류 시 Cloud Settings에 “client close” 메시지가 표시됩니다.
JSON을 저장한 뒤 Cloud Desktop을 완전히 종료(Quit) 하고 다시 실행합니다.
좌측 사이드바에 🔨 아이콘이 생기며, get-forecast·get-alerts 두 도구가 녹색 불로 표시되면 연결 성공입니다.
채팅창에서 “샌프란시스코 지금 날씨?” 같은 질문을 입력하면,
get‑forecast → get‑alerts 순서로 MCP 툴을 호출하고weather 항목을 제거합니다. | 증상 | 원인 | 해결 |
|---|---|---|
| “client close” 또는 “failed to spawn” | command 경로 오타·권한 없음 | 절대 경로 확인, chmod +x 필요 시 적용 |
| 승인 팝업이 계속 나타남 | YOLO 모드 OFF | Settings에서 Enable YOLO 체크 |
| 서버 즉시 종료 | Node 버전 ↔ 패키지 충돌 | package-lock.json 삭제 후 npm install 재실행 |
build/index.js 확보.cloud-desktop-config.json 열고 mcpServers에 서버 정보 추가.이렇게 하면 Cursor와 동일한 MCP 워크플로를 Cloud Desktop에서도 그대로 활용할 수 있습니다. 추가로 궁금한 점이나 오류가 발생하면 알려 주세요!
물론입니다! 아래는 Anthropic의 Model Context Protocol(MCP)을 사용하여 Python으로 날씨 정보를 제공하는 MCP 서버를 처음부터 끝까지 구축하는 실습 가이드입니다. 이 가이드는 프로젝트 설정부터 클라이언트와의 연동까지 단계별로 설명합니다.
**Model Context Protocol (MCP)**는 AI 모델, 특히 대형 언어 모델(LLM)이 외부 데이터 소스 및 도구와 상호 작용할 수 있도록 표준화된 인터페이스를 제공하는 개방형 프로토콜입니다. 이 프로토콜은 LLM이 외부 정보를 실시간으로 활용하여 보다 정확하고 유용한 응답을 생성할 수 있도록 지원합니다.
공식문서: https://modelcontextprotocol.io/quickstart/server#what-we%E2%80%99ll-be-building
uv 설치 확인먼저, uv가 설치되어 있는지 확인합니다:
uv --version
버전 정보가 표시되지 않으면, 아래 명령어로 설치합니다:
curl -LsSf https://astral.sh/uv/install.sh | sh
exec $SHELL # 터미널 재시작 또는 셸 재실행
uv init weather
cd weather
uv venv
source .venv/bin/activate # Windows는 .venv\Scripts\activate
uv add "mcp[cli]" httpx
weather.py 작성물론입니다! 아래는 weather.py에 포함된 각 코드의 역할과 그 이유를 자세히 설명한 것입니다. 이 코드는 FastMCP 프레임워크를 사용하여 미국 국립 기상청(NWS) API로부터 날씨 경고와 예보를 가져오는 서버를 구축합니다.
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# FastMCP 서버 초기화
mcp = FastMCP("weather")
# 상수 정의
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
from typing import Any: Python의 타입 힌트를 사용하여 코드의 가독성과 유지보수성을 높입니다.import httpx: 비동기 HTTP 요청을 지원하는 라이브러리로, 외부 API와의 통신에 사용됩니다.from mcp.server.fastmcp import FastMCP: FastMCP는 MCP 서버를 쉽게 구축할 수 있도록 도와주는 프레임워크입니다.mcp = FastMCP("weather"): "weather"라는 이름의 MCP 서버 인스턴스를 생성합니다.NWS_API_BASE와 USER_AGENT: NWS API의 기본 URL과 사용자 에이전트를 정의하여 API 요청 시 식별 정보를 제공합니다.async def make_nws_request(url: str) -> dict[str, Any] | None:
"""NWS API에 요청을 보내고 응답을 처리합니다."""
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:
"""경고 정보를 읽기 쉬운 문자열로 포맷합니다."""
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')}
"""
make_nws_request: 주어진 URL로 비동기 HTTP GET 요청을 보내고, JSON 응답을 반환합니다. 오류 발생 시 None을 반환하여 오류 처리를 용이하게 합니다.format_alert: NWS API로부터 받은 경고 데이터를 사람이 읽기 쉬운 형식의 문자열로 변환합니다.@mcp.tool()
async def get_alerts(state: str) -> str:
"""미국 주(state)의 날씨 경고를 가져옵니다.
Args:
state: 두 글자의 미국 주 코드 (예: 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 "경고를 가져올 수 없거나 경고가 없습니다."
if not data["features"]:
return "이 주에 대한 활성 경고가 없습니다."
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:
"""지정된 위치의 날씨 예보를 가져옵니다.
Args:
latitude: 위치의 위도
longitude: 위치의 경도
"""
# 예보 그리드 엔드포인트를 먼저 가져옵니다.
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "이 위치에 대한 예보 데이터를 가져올 수 없습니다."
# points 응답에서 예보 URL을 가져옵니다.
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "상세 예보를 가져올 수 없습니다."
# 기간을 읽기 쉬운 예보로 포맷합니다.
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]: # 다음 5개 기간만 표시
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)
@mcp.tool(): FastMCP에서 도구로 등록하여 외부에서 호출 가능하게 합니다.get_alerts: 주어진 주 코드에 대한 현재 활성화된 날씨 경고를 가져옵니다.get_forecast: 주어진 위도와 경도에 대한 날씨 예보를 가져옵니다. 먼저 해당 위치의 예보 URL을 얻은 후, 예보 데이터를 가져와서 포맷합니다.if __name__ == "__main__":
# 서버 초기화 및 실행
mcp.run(transport='stdio')
if __name__ == "__main__":: 이 스크립트가 직접 실행될 때만 아래 코드를 실행합니다.mcp.run(transport='stdio'): 표준 입출력(standard input/output)을 통해 MCP 서버를 실행합니다. 이는 로컬 환경에서 테스트하거나 다른 애플리케이션과 통합할 때 유용합니다.이 코드는 FastMCP 프레임워크를 활용하여 NWS API로부터 날씨 경고와 예보를 가져오는 MCP 서버를 구축합니다. 비동기 HTTP 요청을 통해 효율적으로 데이터를 가져오고, 포맷팅하여 사용자에게 제공할 수 있습니다. 이러한 구조는 유지보수가 용이하며, 다른 도구나 애플리케이션과의 통합도 간편하게 할 수 있습니다.
Claude for Desktop에서 직접 구축한 weather.py MCP 서버를 테스트하는 방법을 단계별로 자세히 설명해드리겠습니다. 이 안내는 macOS 및 Windows 사용자를 대상으로 하며, Linux 사용자는 별도의 클라이언트 구축 튜토리얼을 참고하셔야 합니다.
uv 명령어 설치 확인uv는 Python 패키지 및 프로젝트 관리 도구입니다. 설치되어 있는지 확인하려면 터미널에서 다음 명령어를 실행하세요:
which uv # macOS/Linux
where uv # Windows
설치되어 있지 않다면 uv 공식 설치 가이드를 참고하여 설치하세요.
claude_desktop_config.json 파일 설정Claude for Desktop이 MCP 서버를 인식하려면 설정 파일에 서버 정보를 추가해야 합니다.
~/Library/Application Support/Claude/claude_desktop_config.jsonC:\Users\{사용자이름}\AppData\Roaming\Claude\claude_desktop_config.json해당 파일이 없다면 새로 생성하셔도 됩니다.
macOS:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
Windows:
code "%APPDATA%\Claude\claude_desktop_config.json"
위 명령어는 VS Code를 사용하는 경우입니다. 다른 텍스트 에디터를 사용하셔도 무방합니다.
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}
!!! You may need to put the full path to the uv executable in the command field. You can get this by running which uv on MacOS/Linux or where uv on Windows. !!!
"command": uv 실행 파일의 절대 경로를 입력합니다. 예를 들어, macOS에서는 /usr/local/bin/uv일 수 있습니다.
"args":
"--directory": weather.py 파일이 있는 폴더의 절대 경로를 지정합니다."run": uv 명령어로 스크립트를 실행할 때 사용하는 인자입니다."weather.py": 실행할 Python 스크립트의 파일명입니다.경로를 입력할 때는 반드시 절대 경로를 사용하시고, Windows에서는 역슬래시(
\) 대신 슬래시(/)를 사용하거나 역슬래시를 이중으로(\\) 입력하세요.
설정 파일을 저장한 후, Claude for Desktop을 완전히 종료하고 다시 실행하세요. 재시작 후에는 Claude가 새로 추가된 MCP 서버를 인식하게 됩니다.
Claude for Desktop이 실행되면 입력창 하단에 망치 아이콘이 나타납니다. 이 아이콘을 클릭하면 등록된 MCP 서버 목록이 표시되며, 여기서 weather 서버가 실행 중인지 확인할 수 있습니다.
이제 Claude에게 다음과 같은 질문을 해보세요:
Claude는 weather.py에 정의된 도구를 사용하여 해당 정보를 제공할 것입니다.
서버가 정상적으로 작동하지 않을 경우 다음 사항을 확인하세요:
경로 확인: 설정 파일에 입력한 경로가 정확한지 확인하세요.
서버 독립 실행 테스트: 터미널에서 직접 서버를 실행해보세요:
uv --directory /절대/경로/프로젝트/폴더 run weather.py
포트 충돌 확인: 기본적으로 MCP 서버는 포트 8001을 사용합니다. 해당 포트가 다른 프로세스에 의해 사용 중인지 확인하세요.
로그 확인:
~/Library/Logs/Claude/mcp*.log%APPDATA%\Claude\logs\mcp*.log자세한 문제 해결 방법은 공식 문서를 참고하세요.
더 자세한 설정 과정을 보고 싶으시다면 아래 영상을 참고하세요:
이 영상은 Windows 환경에서 Claude Desktop과 MCP 서버를 연결하는 방법을 설명합니다.
이제 Claude for Desktop에서 직접 구축한 weather.py MCP 서버를 테스트할 준비가 완료되었습니다. 추가로 궁금하신 점이 있으시면 언제든지 문의해주세요!
@server.tool 데코레이터 와 명확한 docstring 만으로도 LLM‑클라이언트가 도구를 똑똑하게 호출합니다.stdio 전송 + 클라이언트 설정 한 줄이면 로컬 서버가 곧바로 Cursor·Cloud Desktop 안에 장착됩니다.이 패턴을 그대로 확장하면, 예컨대 “PDF 요약” 함수나 “GitHub 이슈 생성” 함수도 동일한 구조로 손쉽게 추가할 수 있습니다.
다음 내용은 Node + TypeScript로 Weather MCP Server를 만드는 전 과정을 “설치 → 코딩 → 빌드 → 클라이언트 연결” 순서로 자세히 풀어쓴 가이드입니다. 핵심 흐름은 ① Node/패키지 준비 → ② src/index.ts에 서버·툴 구현 → ③ tsc로 컴파일·실행 → ④ Cursor · Claude Desktop 같은 MCP 클라이언트에 등록입니다. 이 과정에서 Model Context Protocol의 stdio 전송 방식과 @server.tool 패턴이 그대로 적용된다는 점이 Python 버전과 동일한 포인트입니다.
날씨 서버 만들기 설명
사용자가 날씨 서버를 만드는 방법을 쉽게 이해할 수 있게 간단하고 자세히 설명해주세요. TypeScript로 MCP를 사용하는 방법에 대한 긴 튜토리얼 내용을 제공하고, 이를 쉽게 설명해달라는 요청입니다.
그럼, 다음과 같이 단계별 가이드를 제공할 수 있습니다:
각 코드 조각을 하나씩 설명하고, 자주 발생하는 문제에 대해 안내할게요.
5초 동안 생각함
아래 안내는 **“날씨 MCP 서버(Weather MCP Server)”**를 완전히 처음부터 만들고, Claude for Desktop에서 테스트하기까지 전 과정을 쉬운 말로, 단계별로, 하지만 충분히 자세히 풀어쓴 것입니다.
(코드는 그대로 복사해도 되고, ❗로 표시한 “중요 포인트”만 잘 챙기면 절대 막히지 않아요.)
| 구분 | 왜 필요한가? | 확인/설치 방법 |
|---|---|---|
| Node.js ≥ 16 + npm | TypeScript 코드를 실행‧빌드하려면 필수 | node --version, npm --version |
| VS Code (추천) | 코드 작성·실행·디버깅이 편함 | https://code.visualstudio.com |
| (Mac) 터미널 / (Win) PowerShell | 명령어 입력 창 | 운영체제 기본 제공 |
| Claude for Desktop | MCP 서버를 호출해 볼 “호스트” | https://www.anthropic.com/claude |
❗ Node 16 미만이면 Node js.org에서 LTS(장기지원) 버전을 설치하세요.
# ① “weather”라는 새 폴더 만들고 이동
mkdir weather
cd weather
# ② npm 프로젝트 초기화 (package.json 자동 생성)
npm init -y
npm init -y는 “모든 질문에 기본값으로 yes”라는 뜻입니다.
# ① 런타임(실행 시) 의존성
npm install @modelcontextprotocol/sdk zod
# ② 개발(빌드용) 의존성
npm install -D typescript @types/node
| 패키지 | 역할 |
|---|---|
| @modelcontextprotocol/sdk | MCP 서버 만들 때 쓰는 핵심 SDK |
| zod | 매개변수(도구 입력값) 타입 검증 라이브러리 |
| typescript | TS → JS로 변환해 주는 컴파일러 |
| @types/node | Node API의 TypeScript용 타입 정의 |
# ① 소스코드 디렉터리
mkdir src
# ② 진입 파일
touch src/index.ts
package.json을 열어 아래처럼 “3곳”만 바꿔 주세요.
{
// 1) ES 모듈 방식 사용
"type": "module",
// 2) CLI로 실행할 때 'weather' 명령 등록 (선택)
"bin": {
"weather": "./build/index.js"
},
// 3) 빌드 스크립트 추가
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
// 빌드 결과물만 패키징
"files": ["build"]
}
❗ chmod 755는 Mac·Linux 전용(실행 권한 부여). 윈도우는 무시해도 됩니다.
프로젝트 루트(= weather 폴더)에 tsconfig.json 작성:
{
"compilerOptions": {
"target": "ES2022", // 컴파일 결과 JS 버전
"module": "Node16", // 모듈 시스템
"moduleResolution": "Node16",
"outDir": "./build", // JS 출력 폴더
"rootDir": "./src", // TS 소스 폴더
"strict": true, // 엄격 모드
"esModuleInterop": true, // CommonJS 호환
"skipLibCheck": true, // 빠른 빌드
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
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"; // 미국 기상청 공개 API
const USER_AGENT = "weather-app/1.0";
// "weather"라는 이름의 서버 만들기
const server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {}, // 이번 예제에서는 미사용
tools: {}, // 아래에서 도구 두 개 등록
},
});
// fetch 래퍼: 오류 처리 + 타입 보장
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": USER_AGENT,
Accept: "application/geo+json",
};
try {
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as T;
} catch (err) {
console.error("NWS 요청 오류:", err);
return null;
}
}
// 타입 정의 (필요한 필드만)
interface AlertFeature { /* …중략… */ }
interface ForecastPeriod { /* …중략… */ }
// 경보(Alerts) 텍스트 변환
function formatAlert(feature: AlertFeature): string {
const p = feature.properties;
return [
`Event: ${p.event ?? "Unknown"}`,
`Area: ${p.areaDesc ?? "Unknown"}`,
`Severity: ${p.severity ?? "Unknown"}`,
`Status: ${p.status ?? "Unknown"}`,
`Headline: ${p.headline ?? "No headline"}`,
"—".repeat(6),
].join("\n");
}
get-alertsserver.tool(
"get-alerts", // 툴 이름
"Get weather alerts for a state", // 설명
{
state: z.string().length(2) // 입력: 주(州) 2글자 코드
.describe("Two-letter state code (e.g. CA, NY)"),
},
async ({ state }) => { // 실제 동작
const code = state.toUpperCase();
const url = `${NWS_API_BASE}/alerts?area=${code}`;
const data = await makeNWSRequest<AlertsResponse>(url);
if (!data) return { content:[{type:"text",text:"데이터 실패"}] };
if (!data.features?.length)
return { content:[{type:"text",text:`${code} 경보 없음`}] };
const msg = `Active alerts for ${code}:\n\n` +
data.features.map(formatAlert).join("\n");
return { content:[{type:"text",text:msg}] };
}
);
get-forecastserver.tool(
"get-forecast",
"Get weather forecast for a location",
{
latitude: z.number().min(-90).max(90)
.describe("위도"),
longitude: z.number().min(-180).max(180)
.describe("경도"),
},
async ({ latitude, longitude }) => {
// 1단계: 위‧경도로 “그리드 포인트” 얻기
const ptUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const ptData = await makeNWSRequest<PointsResponse>(ptUrl);
const fcUrl = ptData?.properties?.forecast;
if (!fcUrl) return { content:[{type:"text",text:"위치 지원 안 됨"}] };
// 2단계: 예보 요청
const fcData = await makeNWSRequest<ForecastResponse>(fcUrl);
const periods = fcData?.properties?.periods ?? [];
if (!periods.length)
return { content:[{type:"text",text:"예보 없음"}] };
// 3단계: 보기 좋게 문자열 조립
const msg = `Forecast for ${latitude}, ${longitude}:\n\n` +
periods.map(p => [
`${p.name ?? "Unknown"}:`,
`Temp: ${p.temperature ?? "?"}°${p.temperatureUnit ?? "F"}`,
`Wind: ${p.windSpeed ?? "?"} ${p.windDirection ?? ""}`,
p.shortForecast ?? "",
"—".repeat(6),
].join("\n")).join("\n");
return { content:[{type:"text",text:msg}] };
}
);
async function main() {
const transport = new StdioServerTransport(); // 표준입출력 연결
await server.connect(transport);
console.error("✅ Weather MCP Server is running (stdio)");
}
main().catch(err => {
console.error("Fatal error:", err);
process.exit(1);
});
# ① TypeScript → JavaScript 변환
npm run build # 빌드 결과가 build/ 하위에 생성
# ② 서버 직접 실행(테스트용)
node build/index.js # "Weather MCP Server is running…" 메시지
❗ 빌드는 매번 코드 변경 후 꼭 실행해야 합니다. (안 하면 Claude가 옛 버전 JS를 실행)
~/Library/Application Support/Claude/claude_desktop_config.json 열기%APPDATA%\Claude\claude_desktop_config.json){
"mcpServers": {
"weather": {
"command": "node",
"args": [
"/절대/경로/weather/build/index.js"
]
}
}
}
node)get-alerts, get-forecast 두 도구가 보이면 성공!| 목표 | 채팅 입력 예 | 기대 결과 |
|---|---|---|
| 미국 캘리포니아 주 경보 | /tools/weather/get-alerts { "state": "CA" } | 캘리포니아의 현재 기상 경보 목록 |
| 샌프란시스코 예보 (위도 37.7749, 경도 -122.4194) | /tools/weather/get-forecast { "latitude": 37.7749, "longitude": -122.4194 } | 시간대별 예보 |
✅ 입력 값은 JSON이므로 문자열·숫자 구분에 주의하세요.
| 증상 | 원인 | 해결책 |
|---|---|---|
node: command not found | Node 미설치 또는 PATH 설정 오류 | Node 재설치(LTS) 후 터미널 재실행 |
Cannot find module '@modelcontextprotocol/sdk' | npm 패키지 설치 누락 | npm install 재실행 (루트에서) |
| Claude에서 툴이 안 보임 | ① 빌드 안 함 ② config 경로·파일명 오타 ③ JS 경로 상대‧띄어쓰기 문제 | ① npm run build② 경로 정확히 입력 ③ 파일 절대경로 사용 |
tsconfig.json → outDir 설정으로 배포 구조를 분리합니다. src/index.ts 안에서 요청 헬퍼 → 데이터 타입 → @server.tool 로 툴 두 개(get_alerts, get_forecast) 등록하면 핵심 로직 완성입니다.server.connect({ transport:"stdio" }) 한 줄로 Cursor·Claude Desktop 같은 MCP 클라이언트와 바로 통신할 수 있습니다. 이 패턴을 응용하면 TypeScript 프로젝트에서도 간단히 PDF 리소스 제공이나 사내 API 자동화 툴 등을 확장할 수 있습니다.