MCP
Model Context Protocol (MCP) 是 Anthropic 推出的協定,讓 AI 助理可以呼叫外部工具。透過 MCP,我們可以擴展 Claude 的能力,讓它存取資料或執行特定功能。
MCP 支援兩種傳輸方式:
- stdio:伺服器作為子程序,透過標準輸入輸出溝通
- SSE:伺服器獨立運行,透過 HTTP 溝通
本文介紹 stdio 方式,它比較簡單適合入門。
專案
建立一個簡單的天氣查詢 MCP 伺服器:
- 提供 5 個城市的預設天氣資料
- 定義
get_weather 工具讓 Claude 呼叫 - 當使用者詢問天氣時,Claude 自動使用這個工具
環境準備
# 建立專案
mkdir weather-mcp-server
cd weather-mcp-server
# 初始化並安裝套件
uv init --python 3.11
uv add mcp
結構
weather-mcp-server/
├── .mcp.json # Claude Code 設定
├── src/
│ └── weather_server/
│ ├── __init__.py
│ ├── weather_data.py # 天氣資料
│ └── stdio_server.py # MCP 伺服器
└── pyproject.toml
步驟 1:定義天氣資料
檔案:src/weather_server/weather_data.py
from typing import TypedDict
class WeatherData(TypedDict):
temperature: float
condition: str
humidity: int
WEATHER_DATA: dict[str, WeatherData] = {
"New York": {"temperature": 22.0, "condition": "Sunny", "humidity": 60},
"London": {"temperature": 12.0, "condition": "Rainy", "humidity": 85},
"Tokyo": {"temperature": 24.0, "condition": "Sunny", "humidity": 65},
"Sydney": {"temperature": 18.0, "condition": "Cloudy", "humidity": 70},
"Paris": {"temperature": 15.0, "condition": "Foggy", "humidity": 80},
}
def get_weather(city: str) -> WeatherData | None:
for city_name, data in WEATHER_DATA.items():
if city_name.lower() == city.lower():
return data
return None
def get_supported_cities() -> list[str]:
return list(WEATHER_DATA.keys())
步驟 2:實作 MCP 伺服器
檔案:src/weather_server/stdio_server.py
import asyncio
import logging
from typing import Any
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from .weather_data import get_weather, get_supported_cities
# 設定日誌
logging.basicConfig(
level=logging.INFO,
handlers=[
logging.StreamHandler(), # stderr
logging.FileHandler('/tmp/weather-mcp-server.log')
]
)
logger = logging.getLogger("weather-server")
app = Server("weather-server")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""定義可用的工具"""
return [
Tool(
name="get_weather",
description=f"查詢城市天氣。支援:{', '.join(get_supported_cities())}",
inputSchema={
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名稱"}
},
"required": ["city"],
},
)
]
@app.call_tool()
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
"""處理工具呼叫"""
if name != "get_weather":
raise ValueError(f"未知的工具:{name}")
city = arguments.get("city")
if not city:
raise ValueError("缺少參數:city")
logger.info(f"查詢:{city}")
weather = get_weather(city)
if weather is None:
supported = get_supported_cities()
text = f"沒有 '{city}' 的資料。支援:{', '.join(supported)}"
else:
text = (
f"{city} 的天氣:\n"
f"溫度:{weather['temperature']}°C\n"
f"狀況:{weather['condition']}\n"
f"濕度:{weather['humidity']}%"
)
return [TextContent(type="text", text=text)]
async def main():
logger.info("啟動天氣 MCP 伺服器")
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
步驟 3:設定 Claude Code
檔案:.mcp.json
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["run", "python", "-m", "weather_server.stdio_server"],
"env": {"PYTHONPATH": "src"}
}
}
}
關鍵概念
為什麼要用 -m 參數?
因為程式碼使用相對引入(from .weather_data import ...),必須作為模組執行。
# 錯誤
python src/weather_server/stdio_server.py
# 正確
PYTHONPATH=src python -m weather_server.stdio_server
stdio 傳輸運作原理
Claude Code 啟動時:
1. 讀取 .mcp.json
2. 執行指定指令(建立子程序)
3. 透過 stdin/stdout 溝通(JSON-RPC)
4. 日誌輸出到 stderr(被捕捉但不顯示)
為什麼需要檔案日誌?
因為 stderr 被 Claude Code 捕捉後不會顯示,所以同時寫到檔案。監看日誌:
tail -f /tmp/weather-mcp-server.log
測試
手動測試伺服器
PYTHONPATH=src uv run python -m weather_server.stdio_server
應該看到啟動訊息,按 Ctrl+C 停止。
在 Claude Code 中測試
- 重新啟動 Claude Code
- 執行
/mcp 確認 weather 伺服器已連接 - 詢問 Claude:"東京現在的天氣如何?"
- 另開終端機監看日誌:
tail -f /tmp/weather-mcp-server.log
Tool 定義說明
Tool(
name="get_weather", # 工具名稱
description="...", # 告訴 Claude 這個工具的用途
inputSchema={ # JSON Schema 定義參數
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
)
Claude 會根據 description 和 inputSchema,在適當時機自動呼叫工具。
常見問題
伺服器連接失敗?
確認 .mcp.json 有設定:
- 使用
-m 參數 - 設定
PYTHONPATH=src
如何看日誌?
tail -f /tmp/weather-mcp-server.log
修改程式碼後如何重新載入?
重新啟動 Claude Code 即可。
結語
完整程式碼:https://github.com/checko/hellomcpserver