MCP 입문 - 미리 구축된 MCP 서버(날씨 서버) 설정하기

GoGoComputer·2025년 5월 3일
post-thumbnail

아래 설명은 영상에서 다룬 MCP(Model Context Protocol) 날씨 서버 예제를 처음부터 끝까지 따라 하면서, 왜 이런 순서로 작업하는지까지 친절하게 풀어쓴 가이드입니다. 각 단계는 “무엇을 왜 하는지”→“어떻게 하는지” 순서로 설명해, 처음 접하셔도 맥락이 자연스럽게 잡히도록 구성했습니다.


MCP란 무엇인가?

  • MCP의 목적
    MCP는 LLM(대형 언어 모델)이 외부 데이터·도구와 통신할 때 쓰는 표준 인터페이스입니다. USB‑C가 노트북과 주변기기를 한 케이블로 연결해 주듯, MCP는 AI와 여러 데이터 소스를 한 규격으로 이어 줍니다.
  • 서버·클라이언트 구조
    MCP 서버는 “날씨 정보”처럼 모델이 필요로 하는 기능을 제공하고, MCP 클라이언트(Cursor · Claude Desktop 등)는 그 기능을 호출합니다.

로컬 vs 원격 MCP 서버

구분특징사용 시점
로컬(Local)내 PC에서 실행, 네트워크 지연 거의 없음개발·테스트, 보안이 민감할 때
원격(Remote)클라우드에 배포, 인터넷으로 접근여러 팀원이 동시에 쓰거나, 24시간 가동해야 할 때

이 튜토리얼은 로컬 서버를 설치·실행하는 과정을 다룹니다.


1단계 ― 작업 폴더 만들기

mkdir MCP-servers
cd MCP-servers
  • 여러 MCP 서버를 한곳에 모아 관리하기 위함입니다. “폴더 하나 = 서버 하나” 식으로 두면 나중에 업데이트·삭제가 쉽습니다.

2단계 ― Quickstart 리포지토리 클론

git clone https://github.com/modelcontextprotocol/quickstart-resources.git
cd quickstart-resources/weather-server-typescript
  • Quickstart 리포에는 Python 버전TypeScript(Node.js) 버전이 모두 들어 있습니다. 여기서는 Node.js 예제를 사용합니다.

3단계 ― Node.js & npm 준비

  1. 버전 확인

    node -v   # 예: v20.x
    npm  -v   # 예: 10.x

    두 명령이 정상 출력되면 설치 완료입니다.

  2. 없다면 설치
    https://nodejs.org/에서 LTS 버전을 내려받아 기본 옵션으로 설치하면 됩니다.


4단계 ― 의존성 설치와 흔한 오류 해결

npm install

❗ 자주 만나는 ERESOLVE 오류

  • 원인: package-lock.json이 내 Node/npm 버전과 맞지 않아 의존성 충돌이 발생.

  • 해결

    rm package-lock.json
    npm install --legacy-peer-deps   # 필요 시

    Stack Overflow와 npm 변경 로그에서도 동일한 해법을 권장합니다.

: --legacy-peer-deps 플래그는 일시적 방편입니다. 최종적으로는 해당 패키지를 최신 버전으로 올려 충돌을 제거하는 편이 좋습니다.


5단계 ― 빌드

npm run build
  • package.jsonscripts 항목에 정의된 build 스크립트가 TypeScript를 JS로 트랜스파일해 build/ 폴더에 index.js를 생성합니다.

6단계 ― 서버 실행

node build/index.js
  • 기본 포트(보통 3333)로 기동하며, /weather?lat=…&lon=… 형태의 요청을 받으면 api.weather.gov로부터 예보를 가져와 가공한 뒤 JSON으로 돌려줍니다.

7단계 ― Cursor·Claude Desktop에 등록

  1. Cursor

    • File → Settings → MCP Servers → Add Local Server
    • 실행 중인 http://localhost:3333 입력 후 테스트.
  2. Claude Desktop

    • 좌측 패널의 Tools → Add MCP Server 버튼.
    • 동일 주소 입력.

두 클라이언트는 자동으로 서버의 스키마를 읽어 기능 목록을 표시합니다.


8단계 ― 보안 체크리스트

위험완화 방법
공급망(서드파티) 공격사용 전 코드 리뷰·패키지 서명 확인
악성 npm 패키지Trust Score 높은 패키지만 사용, npm audit 주기적 실행
원격 서버 노출방화벽·인증 추가, HTTPS 적용

공격 사례로는 ua-parser-js 변조 사건이 유명합니다. GitHub도 서명 강화로 대응 중입니다.


정리

  1. 디렉터리 생성 → 리포지토리 클론 → 의존성 설치 → 빌드 → 실행 순서를 따르면 로컬 MCP 날씨 서버가 바로 기동됩니다.
  2. npm 충돌이 나면 package-lock.json 삭제 후 재설치를 먼저 시도하세요.
  3. 서버가 뜨면 Cursor·Claude Desktop 같은 MCP 클라이언트에 주소만 넣어주면 곧바로 “날씨 기능”을 사용할 수 있습니다.
  4. 끝으로, 오픈소스 코드는 편리하지만 항상 보안 검증을 거쳐야 합니다.

이제 로컬 서버 설정은 마쳤습니다. 다음 단계로 Python 버전이나 직접 만든 MCP 서버를 추가해 보면 MCP의 진가를 더 깊이 체험하실 수 있습니다.


1. MCP 서버와 Cursor가 대화하는 방식

1.1 MCP가 하는 일

  • MCP는 AI 모델과 외부 데이터·도구를 연결하기 위한 공개 표준으로, “USB‑C 같은 범용 커넥터”를 지향합니다.
  • 서버(Weather MCP 등)는 **특정 기능(날씨 문의, 데이터베이스 조회 등)**을 추상화한 “툴”을 노출하고, 클라이언트(Cursor, Claude 등)가 이 툴을 호출합니다.

1.2 두 가지 Transport

Transport특징쓰임새
stdio서버가 표준 입력/출력(stdin/stdout)으로 JSON 메시지를 주고받음. 로컬 PC에서 가장 간단Weather MCP처럼 PC에서 실행할 때 주로 사용
SSEHTTP + Server‑Sent Events. 서버가 이벤트를 push하고, 클라이언트는 HTTP POST로 요청원격 클라우드 MCP, 실시간 알림 등

2. MCP 서버 등록 위치

범위파일 경로언제 쓰나
글로벌~/.cursor/mcp.json모든 프로젝트에서 공통으로 쓰는 서버 (예: 사내 날씨 MCP)
프로젝트별<프로젝트>/.cursor/mcp.json해당 프로젝트 전용 툴 (예: 특정 DB 조회 MCP)

Cursor 설정 UI(⌘/Ctrl + Shift + J → MCP 탭)에서도 동일 JSON을 편집할 수 있습니다.


3. mcp.json 작성법

{
  "servers": {
    "weather": {
      "command": "node /absolute/path/to/weather-server-typescript/build/index.js",
      "transport": "stdio",
      "env": {
        // 예시: "API_KEY": "abcdef123456"
      }
    }
  }
}
  • name(weather): 클라이언트가 툴을 부를 때 식별자.
  • command: 서버를 어떻게 실행할지를 쉘 명령으로 명시. Node 버전이든 Python이든 상관없으며, 빌드 후 생성된 JS/py 파일을 지정.
  • env: API 키처럼 민감 정보를 전달할 때 사용. 소스에 키를 하드코딩하지 않아도 된다.

Node와 Python 서버 모두 “쉘로 실행할 수 있는 명령만 다를 뿐 구성 방식은 완전히 동일”합니다.


4. 도구 호출(툴 콜) 흐름과 보안

  1. Cursor가 질문을 분석해 Weather 툴이 필요하다고 판단하면, MCP 메시지로 toolCall을 준비한다.
  2. 기본 모드: 사용자에게 “이 툴을 latitude=…, longitude=…로 호출할까요?”라는 승인 팝업을 띄워 RCE 위험을 차단.
  3. YOLO 모드: 설정에서 Enable YOLO를 켜면 팝업 없이 자동 실행 → 빠르지만 기업 환경에선 권장되지 않는다.

RCE(Remote Code Execution) 위험: 오픈소스 MCP 서버가 악성 코드일 경우, 내 PC에서 그대로 실행되므로 공급망 공격(Supply‑Chain Attack)에 노출된다. 신뢰할 수 없는 저장소는 삼가고, 컨테이너 격리를 고려한다.


5. Weather MCP를 예로 든 실전 절차

단계명령비고
(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툴 콜 정상 동작

6. 문서 버전 차이 & 문제 해결

  • Cursor는 2025년 3월 이후 MCP API를 크게 개편하면서 문서가 바뀌었으므로, 예전 강좌의 JSON 필드 이름이 다를 수 있습니다. 최신 설명서는 docs.cursor.com/context/model-context-protocol 페이지를 참조하세요.
  • SSE Transport가 “세션 ID not found” 오류를 내면, Cursor 0.50.x에 있던 버그이므로 0.51 이상으로 업데이트하세요.

7. 다음 단계

  1. Python 버전 Weather MCP도 똑같이 mcp.json에 등록해 보세요. Node → Python으로 바꿀 때는 command만 바뀝니다.
  2. 컨테이너 격리: Docker로 MCP 서버를 띄우면 의존성 충돌·보안 문제를 크게 줄일 수 있습니다.
  3. 원격(Multi‑tenant) MCP가 GA 되면, transport:"sse"와 HTTPS 엔드포인트를 사용해 클라우드 서버도 붙일 수 있습니다.

1. MCP 서버 없이 Cursor가 보이는 기본 반응

  • Cursor IDE의 기본 “웹 검색” 에이전트는 실시간 API에 직접 연결되지 않으면 최신 기상 데이터를 줄 수 없습니다.
  • 예컨대 “샌프란시스코 날씨 알려줘”라고 묻으면, 버전에 따라 “실시간 데이터를 볼 수 없다”는 답을 돌려주거나 웹 스크래핑 결과만 요약해 줍니다.

2. MCP 서버를 쓰면 달라지는 점

2.1 Weather MCP Server 개요

  • Anthropic Quickstart 예제는 get‑forecast, get‑alerts 두 가지 도구를 노출합니다.
  • TypeScript 버전은 npm run buildbuild/index.js로 실행할 수 있습니다.

2.2 Cursor가 MCP 도구를 호출하는 흐름

  1. 사용자가 질문 → 에이전트가 좌표 추출(예: 샌프란시스코 위도/경도).
  2. toolCall 메시지를 준비해 MCP 서버에 전달(stdio 전송).
  3. 결과 JSON을 받아 자연어 답변에 삽입.
  4. 기본 설정에선 사용자 승인 팝업이 떠서 한 번 더 확인.

3. mcp.json 작성 — 단 2 줄로 끝내기

{
  "servers": {
    "weather": {
      "command": "node /Users/park/MCP-servers/quickstart-resources/weather-server-typescript/build/index.js",
      "transport": "stdio"
    }
  }
}
  • command: Node든 Python이든 실행 가능한 쉘 명령이면 OK.
  • transport: 로컬 실행은 stdio, 원격 서버는 sse를 주로 사용. (
  • env: API 키가 필요할 땐 "env":{"API_KEY":"…"}" 식으로 추가.

파일 위치

  • 전역 사용: ~/.cursor/mcp.json → 모든 프로젝트에서 사용.
  • 프로젝트 한정: <proj>/.cursor/mcp.json → 해당 폴더에서만 사용.

4. 실제 경로 찾기 & 오류 대처

  1. 빌드 후 생성된 JS 파일 위치 확인

    cd ~/MCP-servers/quickstart-resources/weather-server-typescript
    npm run build           # build/index.js 생성
    pwd                     # 절대 경로 복사
  2. 경로가 틀리면 Cursor MCP 탭에 “client close” 오류가 표시됩니다. 제대로 된 경로로 수정 후 저장하면 즉시 “valid” 상태로 바뀝니다.


5. 동작 테스트

  1. Cursor 재시작(또는 ⌘/Ctrl + Shift + P → “Reload Window”).
  2. 채팅창에 “샌프란시스코 지금 날씨?” 입력.
  3. 승인 팝업에서 Allow 클릭 → get‑forecast, 이어서 get‑alerts 호출 로그가 보임.
  4. 결과: 현재 기온·풍속·위험 경보가 자연어로 나타남.

6. 보안 & YOLO 모드

  • 기본 모드: 매 툴 호출마다 승인 필요 → 공급망 공격 방어.
  • YOLO 모드: 설정→MCP→“YOLO mode” 켜면 팝업 생략, 자동 실행(속도 ↑ / 위험 ↑).
  • 조직 환경에서는 컨테이너(Docker)로 격리하거나, Git 서명·SBOM 검사를 통해 신뢰성을 확보하세요.

7. 자주 묻는 문제 해결

증상원인해결
“client close” 오류command 경로 오타절대 경로 재확인
서버가 즉시 종료Node 버전 ↔ 패키지 충돌package-lock.json 삭제 후 npm install
팝업이 안 뜸YOLO 모드 ON설정에서 OFF

8. 다음 단계

  1. Python 버전 Weather MCP도 같은 방법으로 등록해 차이점 체험.
  2. SSE Transport 실습: 원격 클라우드에 배포 후 transport:"sse"로 연결.
  3. 커스텀 MCP 서버 작성 시 defineTool({name:"my‑sql‑query", ...}) 패턴으로 새 도구 추가.

이제 Cursor에서 실시간 외부 데이터 활용까지 완벽히 연결했습니다. 궁금한 점이 있으면 언제든 물어보세요!


1. 왜 Cloud Desktop에도 MCP 서버를 등록해야 할까?

  • Cloud Desktop은 MCP 클라이언트를 내장하고 있어, 외부 서버(Weather MCP 등)를 “툴” 형태로 호출할 수 있습니다.
  • MCP는 JSON‑RPC 2.0 메시지를 stdio 또는 sse 전송 방식으로 주고받아, IDE나 채팅 애플리케이션이 언어·플랫폼과 무관하게 같은 서버를 재사용하게 해 줍니다.
  • 이를 통해 Cursor에서 만든 워크플로를 그대로 Cloud(Claude)에서도 활용할 수 있으며, 회사·학습 환경 간 전환이 쉬워집니다.

2. 설정 파일 위치와 구조

2.1 파일 찾기

  1. 메뉴 바 Claude ▸ SettingsDeveloper 탭으로 이동합니다.
  2. Edit Config 버튼을 누르면, 여러 설정 파일 목록이 뜨고 그중 cloud-desktop-config.json(이름은 버전에 따라 다를 수 있음)을 엽니다.

2.2 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": {}
    }
  }
}
  • weather: 서버 식별자, 자유롭게 지정 가능.
  • command: MCP 서버를 구동할 절대 경로 포함 쉘 명령. Node·Python·Java 모두 가능.
  • transport: 로컬 실행은 대개 stdio; 원격 호스팅 시 sse를 사용합니다.

꿀팁 — 터미널에서 pwd로 절대 경로를 복사해 붙여 넣으면 오타를 줄일 수 있습니다. 오류 시 Cloud Settings에 “client close” 메시지가 표시됩니다.


3. 앱 재시작 및 도구 확인

  1. JSON을 저장한 뒤 Cloud Desktop을 완전히 종료(Quit) 하고 다시 실행합니다.

  2. 좌측 사이드바에 🔨 아이콘이 생기며, get-forecast·get-alerts 두 도구가 녹색 불로 표시되면 연결 성공입니다.

  3. 채팅창에서 “샌프란시스코 지금 날씨?” 같은 질문을 입력하면,

    • Cloud가 내부적으로 좌표를 추출한 뒤
    • get‑forecastget‑alerts 순서로 MCP 툴을 호출하고
    • 결과 JSON을 자연어로 요약해 답을 돌려줍니다.

4. 도구 승인 팝업과 YOLO 모드

  • 기본 모드: 호출마다 “Allow once / Always allow” 팝업이 떠서 원격코드실행(RCE) 위험을 줄입니다.
  • YOLO 모드(Cursor와 동일): 설정에서 활성화하면 승인 절차가 생략돼 빠르지만, 회사·보안 환경에서는 권장되지 않습니다.

5. MCP 서버 삭제·비활성화 실습

  1. Settings ▸ Developer ▸ MCP Servers 목록에서 휴지통 아이콘을 눌러 weather 항목을 제거합니다.
  2. 앱을 다시 시작하면 🔨 아이콘이 사라지고, 같은 날씨 질문을 던져도 Cloud는 “실시간 데이터를 가져올 수 없다”고 답합니다. 이는 툴이 더는 등록되지 않았기 때문입니다.

6. 자주 발생하는 문제와 해결책

증상원인해결
“client close” 또는 “failed to spawn”command 경로 오타·권한 없음절대 경로 확인, chmod +x 필요 시 적용
승인 팝업이 계속 나타남YOLO 모드 OFFSettings에서 Enable YOLO 체크
서버 즉시 종료Node 버전 ↔ 패키지 충돌package-lock.json 삭제 후 npm install 재실행

7. 한눈에 보는 전체 흐름

  1. Weather MCP 컴파일build/index.js 확보.
  2. cloud-desktop-config.json 열고 mcpServers에 서버 정보 추가.
  3. Cloud Desktop 재시작 → 툴바에 🔨 표시 확인.
  4. 질문 입력 → 툴 승인 팝업 → 날씨 결과 반환.
  5. 필요 시 설정에서 YOLO 모드로 승인 생략.
  6. 불필요할 때는 서버 삭제 후 재시작으로 완전 제거.

이렇게 하면 Cursor와 동일한 MCP 워크플로를 Cloud Desktop에서도 그대로 활용할 수 있습니다. 추가로 궁금한 점이나 오류가 발생하면 알려 주세요!


물론입니다! 아래는 Anthropic의 Model Context Protocol(MCP)을 사용하여 Python으로 날씨 정보를 제공하는 MCP 서버를 처음부터 끝까지 구축하는 실습 가이드입니다. 이 가이드는 프로젝트 설정부터 클라이언트와의 연동까지 단계별로 설명합니다.


🧠 1. MCP란?

**Model Context Protocol (MCP)**는 AI 모델, 특히 대형 언어 모델(LLM)이 외부 데이터 소스 및 도구와 상호 작용할 수 있도록 표준화된 인터페이스를 제공하는 개방형 프로토콜입니다. 이 프로토콜은 LLM이 외부 정보를 실시간으로 활용하여 보다 정확하고 유용한 응답을 생성할 수 있도록 지원합니다.


🛠️ 2. 개발 환경 설정

공식문서: https://modelcontextprotocol.io/quickstart/server#what-we%E2%80%99ll-be-building

2.1 uv 설치 확인

먼저, uv가 설치되어 있는지 확인합니다:

uv --version

버전 정보가 표시되지 않으면, 아래 명령어로 설치합니다:

curl -LsSf https://astral.sh/uv/install.sh | sh
exec $SHELL  # 터미널 재시작 또는 셸 재실행

2.2 프로젝트 초기화

uv init weather
cd weather
uv venv
source .venv/bin/activate  # Windows는 .venv\Scripts\activate

2.3 의존성 설치

uv add "mcp[cli]" httpx

📄 3. weather.py 작성

물론입니다! 아래는 weather.py에 포함된 각 코드의 역할과 그 이유를 자세히 설명한 것입니다. 이 코드는 FastMCP 프레임워크를 사용하여 미국 국립 기상청(NWS) API로부터 날씨 경고와 예보를 가져오는 서버를 구축합니다.


1. 패키지 임포트 및 서버 초기화

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_BASEUSER_AGENT: NWS API의 기본 URL과 사용자 에이전트를 정의하여 API 요청 시 식별 정보를 제공합니다.

2. 보조 함수 정의

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로부터 받은 경고 데이터를 사람이 읽기 쉬운 형식의 문자열로 변환합니다.

3. 도구 함수 구현

@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을 얻은 후, 예보 데이터를 가져와서 포맷합니다.

4. 서버 실행

if __name__ == "__main__":
    # 서버 초기화 및 실행
    mcp.run(transport='stdio')

설명:

  • if __name__ == "__main__":: 이 스크립트가 직접 실행될 때만 아래 코드를 실행합니다.
  • mcp.run(transport='stdio'): 표준 입출력(standard input/output)을 통해 MCP 서버를 실행합니다. 이는 로컬 환경에서 테스트하거나 다른 애플리케이션과 통합할 때 유용합니다.

요약

이 코드는 FastMCP 프레임워크를 활용하여 NWS API로부터 날씨 경고와 예보를 가져오는 MCP 서버를 구축합니다. 비동기 HTTP 요청을 통해 효율적으로 데이터를 가져오고, 포맷팅하여 사용자에게 제공할 수 있습니다. 이러한 구조는 유지보수가 용이하며, 다른 도구나 애플리케이션과의 통합도 간편하게 할 수 있습니다.

🚀 4. 서버 실행 및 테스트

Claude for Desktop에서 직접 구축한 weather.py MCP 서버를 테스트하는 방법을 단계별로 자세히 설명해드리겠습니다. 이 안내는 macOS 및 Windows 사용자를 대상으로 하며, Linux 사용자는 별도의 클라이언트 구축 튜토리얼을 참고하셔야 합니다.


🧰 1단계: 사전 준비

✅ Claude for Desktop 설치 확인

  • Claude for Desktop이 설치되어 있는지 확인하세요. 설치되어 있지 않다면 공식 다운로드 페이지에서 운영 체제에 맞는 버전을 다운로드하여 설치하세요.
  • 이미 설치되어 있다면 최신 버전으로 업데이트되어 있는지 확인하세요.

uv 명령어 설치 확인

  • uv는 Python 패키지 및 프로젝트 관리 도구입니다. 설치되어 있는지 확인하려면 터미널에서 다음 명령어를 실행하세요:

    which uv  # macOS/Linux
    where uv  # Windows
  • 설치되어 있지 않다면 uv 공식 설치 가이드를 참고하여 설치하세요.


🛠️ 2단계: claude_desktop_config.json 파일 설정

Claude for Desktop이 MCP 서버를 인식하려면 설정 파일에 서버 정보를 추가해야 합니다.

📁 설정 파일 위치

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: C:\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에서는 역슬래시(\) 대신 슬래시(/)를 사용하거나 역슬래시를 이중으로(\\) 입력하세요.


🔄 3단계: Claude for Desktop 재시작

설정 파일을 저장한 후, Claude for Desktop을 완전히 종료하고 다시 실행하세요. 재시작 후에는 Claude가 새로 추가된 MCP 서버를 인식하게 됩니다.


🔍 4단계: MCP 서버 연결 확인

Claude for Desktop이 실행되면 입력창 하단에 망치 아이콘이 나타납니다. 이 아이콘을 클릭하면 등록된 MCP 서버 목록이 표시되며, 여기서 weather 서버가 실행 중인지 확인할 수 있습니다.


🌤️ 5단계: 서버 테스트

이제 Claude에게 다음과 같은 질문을 해보세요:

  • "What are the active weather alerts in CA?"
  • "What's the forecast for latitude 37.7749, longitude -122.4194?"

Claude는 weather.py에 정의된 도구를 사용하여 해당 정보를 제공할 것입니다.


🧪 6단계: 문제 해결

서버가 정상적으로 작동하지 않을 경우 다음 사항을 확인하세요:

  1. 경로 확인: 설정 파일에 입력한 경로가 정확한지 확인하세요.

  2. 서버 독립 실행 테스트: 터미널에서 직접 서버를 실행해보세요:

    uv --directory /절대/경로/프로젝트/폴더 run weather.py
  3. 포트 충돌 확인: 기본적으로 MCP 서버는 포트 8001을 사용합니다. 해당 포트가 다른 프로세스에 의해 사용 중인지 확인하세요.

  4. 로그 확인:

    • macOS: ~/Library/Logs/Claude/mcp*.log
    • Windows: %APPDATA%\Claude\logs\mcp*.log

자세한 문제 해결 방법은 공식 문서를 참고하세요.


📺 추가 자료

더 자세한 설정 과정을 보고 싶으시다면 아래 영상을 참고하세요:

Easy connect Claude AI desktop to MCP servers (For Windows)

이 영상은 Windows 환경에서 Claude Desktop과 MCP 서버를 연결하는 방법을 설명합니다.


이제 Claude for Desktop에서 직접 구축한 weather.py MCP 서버를 테스트할 준비가 완료되었습니다. 추가로 궁금하신 점이 있으시면 언제든지 문의해주세요!

6. 마무리

  • 헬퍼 함수 는 “API 요청 → 데이터 가공”을 캡슐화해 코드를 깔끔히 유지합니다.
  • @server.tool 데코레이터 와 명확한 docstring 만으로도 LLM‑클라이언트가 도구를 똑똑하게 호출합니다.
  • stdio 전송 + 클라이언트 설정 한 줄이면 로컬 서버가 곧바로 Cursor·Cloud Desktop 안에 장착됩니다.

이 패턴을 그대로 확장하면, 예컨대 “PDF 요약” 함수나 “GitHub 이슈 생성” 함수도 동일한 구조로 손쉽게 추가할 수 있습니다.

다음 내용은 Node + TypeScriptWeather MCP Server를 만드는 전 과정을 “설치 → 코딩 → 빌드 → 클라이언트 연결” 순서로 자세히 풀어쓴 가이드입니다. 핵심 흐름은 ① Node/패키지 준비 → ② src/index.ts에 서버·툴 구현 → ③ tsc로 컴파일·실행 → ④ Cursor · Claude Desktop 같은 MCP 클라이언트에 등록입니다. 이 과정에서 Model Context Protocolstdio 전송 방식@server.tool 패턴이 그대로 적용된다는 점이 Python 버전과 동일한 포인트입니다.


날씨 서버 만들기 설명

사용자가 날씨 서버를 만드는 방법을 쉽게 이해할 수 있게 간단하고 자세히 설명해주세요. TypeScript로 MCP를 사용하는 방법에 대한 긴 튜토리얼 내용을 제공하고, 이를 쉽게 설명해달라는 요청입니다.

그럼, 다음과 같이 단계별 가이드를 제공할 수 있습니다:

  • 준비 사항: 필요한 도구와 설치 방법
  • 설정: 프로젝트 초기 설정
  • 서버 구현: 핵심 코드 작성 및 설명
  • 실행 및 테스트: 실행 방법과 테스트 팁

각 코드 조각을 하나씩 설명하고, 자주 발생하는 문제에 대해 안내할게요.

5초 동안 생각함

아래 안내는 **“날씨 MCP 서버(Weather MCP Server)”**를 완전히 처음부터 만들고, Claude for Desktop에서 테스트하기까지 전 과정을 쉬운 말로, 단계별로, 하지만 충분히 자세히 풀어쓴 것입니다.
(코드는 그대로 복사해도 되고, ❗로 표시한 “중요 포인트”만 잘 챙기면 절대 막히지 않아요.)


0. 준비 사항 한눈에 보기

구분왜 필요한가?확인/설치 방법
Node.js ≥ 16 + npmTypeScript 코드를 실행‧빌드하려면 필수node --version, npm --version
VS Code (추천)코드 작성·실행·디버깅이 편함https://code.visualstudio.com
(Mac) 터미널 / (Win) PowerShell명령어 입력 창운영체제 기본 제공
Claude for DesktopMCP 서버를 호출해 볼 “호스트”https://www.anthropic.com/claude

❗ Node 16 미만이면 Node js.org에서 LTS(장기지원) 버전을 설치하세요.


1. 새 프로젝트 폴더 만들기

# ① “weather”라는 새 폴더 만들고 이동
mkdir weather
cd weather

# ② npm 프로젝트 초기화 (package.json 자동 생성)
npm init -y

npm init -y는 “모든 질문에 기본값으로 yes”라는 뜻입니다.


2. 의존 패키지 설치

# ① 런타임(실행 시) 의존성
npm install @modelcontextprotocol/sdk zod

# ② 개발(빌드용) 의존성
npm install -D typescript @types/node
패키지역할
@modelcontextprotocol/sdkMCP 서버 만들 때 쓰는 핵심 SDK
zod매개변수(도구 입력값) 타입 검증 라이브러리
typescriptTS → JS로 변환해 주는 컴파일러
@types/nodeNode API의 TypeScript용 타입 정의

3. 기본 디렉터리 & 파일 구조

# ① 소스코드 디렉터리
mkdir src

# ② 진입 파일
touch src/index.ts

4. package.json 손보기

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 전용(실행 권한 부여). 윈도우는 무시해도 됩니다.


5. TypeScript 컴파일러 옵션(tsconfig.json)

프로젝트 루트(= 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"]
}

6. 코드 작성 — src/index.ts

6‑1. 패키지 임포트 & 서버 인스턴스 생성

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: {},     // 아래에서 도구 두 개 등록
  },
});

6‑2. 공통 요청 함수 & 데이터 포맷터

// 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");
}

6‑3. MCP 툴 1 : get-alerts

server.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}] };
  }
);

6‑4. MCP 툴 2 : get-forecast

server.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}] };
  }
);

6‑5. 서버 실행(메인 함수)

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);
});

7. 빌드 & 실행

# ① TypeScript → JavaScript 변환
npm run build          # 빌드 결과가 build/ 하위에 생성

# ② 서버 직접 실행(테스트용)
node build/index.js    # "Weather MCP Server is running…" 메시지

❗ 빌드는 매번 코드 변경 후 꼭 실행해야 합니다. (안 하면 Claude가 옛 버전 JS를 실행)


8. Claude for Desktop에 연결

  1. Claude 앱 종료 → 다시 실행(갱신)
  2. ~/Library/Application Support/Claude/claude_desktop_config.json 열기
    (Mac 기준, Windows는 %APPDATA%\Claude\claude_desktop_config.json)
  3. 아래처럼 설정 추가 후 저장:
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": [
        "/절대/경로/weather/build/index.js"
      ]
    }
  }
}
  • "command": 실행할 프로그램 (node)
  • "args": 첫 번째 인자로 JS 파일 절대경로
  1. Claude 재시작 → 채팅 입력창 옆 망치(🛠️) 아이콘 클릭 →
    get-alerts, get-forecast 두 도구가 보이면 성공!

9. 실전 테스트 예시

목표채팅 입력 예기대 결과
미국 캘리포니아 주 경보/tools/weather/get-alerts { "state": "CA" }캘리포니아의 현재 기상 경보 목록
샌프란시스코 예보
(위도 37.7749, 경도 -122.4194)
/tools/weather/get-forecast { "latitude": 37.7749, "longitude": -122.4194 }시간대별 예보

✅ 입력 값은 JSON이므로 문자열·숫자 구분에 주의하세요.


10. 자주 막히는 오류 TOP 3

증상원인해결책
node: command not foundNode 미설치 또는 PATH 설정 오류Node 재설치(LTS) 후 터미널 재실행
Cannot find module '@modelcontextprotocol/sdk'npm 패키지 설치 누락npm install 재실행 (루트에서)
Claude에서 툴이 안 보임① 빌드 안 함
② config 경로·파일명 오타
③ JS 경로 상대‧띄어쓰기 문제
npm run build
② 경로 정확히 입력
③ 파일 절대경로 사용

📝 마무리

  • 여기까지 따라오면 **“로컬에서 돌고, Claude(=클라이언트)가 STDIO로 붙는 MCP 서버”**를 성공적으로 구축한 것입니다.
  • 앞으로는 도구(tool)를 더 추가하거나, HTTP SSE 트랜스포트로 바꿔 원격 서버로 배포해 보는 연습을 하면 됩니다.

정리

  1. Node 환경에 MCP SDK와 TypeScript를 설치하고, tsconfig.json → outDir 설정으로 배포 구조를 분리합니다.
  2. src/index.ts 안에서 요청 헬퍼 → 데이터 타입 → @server.tool 로 툴 두 개(get_alertsget_forecast) 등록하면 핵심 로직 완성입니다.
  3. server.connect({ transport:"stdio" }) 한 줄로 Cursor·Claude Desktop 같은 MCP 클라이언트와 바로 통신할 수 있습니다.
  4. 빌드 후 클라이언트 설정 파일에 실행 명령만 적어 주면 LLM이 주도적으로 도구를 호출해 날씨 정보·경보를 가져옵니다.

이 패턴을 응용하면 TypeScript 프로젝트에서도 간단히 PDF 리소스 제공이나 사내 API 자동화 툴 등을 확장할 수 있습니다.

profile
IT를 좋아합니다.

0개의 댓글