GuidesMCP
构建你的第一个 MCP Server
用 Python 做一个最小 MCP weather server,暴露 get_alerts / get_forecast,并接入 Claude for Desktop 验证。
基于 Model Context Protocol Docs 整理 · 官方资料 ↗
MCP Server 不是 Agent。它做的事情更简单:把一个程序里的能力,以统一协议暴露给 Claude Desktop、IDE 或其他 MCP Client。
Host 负责承载对话体验,Client 负责和 Server 通信,Server 则提供具体能力。对初次接触来说,最常见的能力就是 Tool:一个有名字、有参数说明、能返回结果的函数。
这篇直接做一个真实最小 Server:按 MCP 官方教程搭建 weather server,暴露 get_alerts / get_forecast,并让宿主真正看见这些工具。
MCP Server 到底是什么
可以把关系理解成四层:Host、Client、Server、Tool。
Host 是用户直接面对的应用,比如 Claude for Desktop。Client 在 Host 内部,负责按 MCP 协议去连接 Server。Server 是你写的那个程序;它不负责“自己思考”,而是暴露能力。Tool 则是这些能力里最直观的一种:模型看到工具描述后,可以在需要时请求调用。
所以你写 MCP Server,本质不是在训练一个新模型,而是在给现有客户端提供一套标准接口。天气查询、内部 API、数据库只读查询,都可以先做成 Tool。
先把项目跑起来
官方 Python 示例用 uv 管理环境。你需要 Python 3.10+、已安装的 uv,以及后面通过 mcp[cli] 装上的 Python MCP SDK。
- Python 3.10+
- 已安装 uv
- 熟悉基础 Python
- 若用 Claude for Desktop 验证:macOS / Windows(官方注明 Linux 桌面端尚不可用,可改做 Client 教程验证)
官方 Python 环境初始化
uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]"
touch weather.py写第一个 Tool
当前官方文档使用 from mcp.server import MCPServer。先创建 server 实例,再准备请求美国国家气象局(NWS)的辅助函数。STDIO 场景下用 httpx2 发请求,失败时返回 None,而不是把异常直接打到 stdout。
from typing import Any
from mcp.server import MCPServer
import httpx2
mcp = MCPServer("weather")
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
async def make_nws_request(url: str) -> dict[str, Any] | None:
headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
async with httpx2.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')}\n"
f"Area: {props.get('areaDesc', 'Unknown')}\n"
f"Severity: {props.get('severity', 'Unknown')}\n"
f"Description: {props.get('description', 'No description available')}"
)真正的工具用 @mcp.tool() 定义。函数名、类型提示和 docstring 都会进入工具 schema;模型正是靠这些信息判断“什么时候该调用、该传什么参数”。
@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."
return "\n---\n".join(format_alert(f) for f in data["features"])
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location."""
points = await make_nws_request(f"{NWS_API_BASE}/points/{latitude},{longitude}")
if not points:
return "Unable to fetch forecast data for this location."
forecast = await make_nws_request(points["properties"]["forecast"])
if not forecast:
return "Unable to fetch detailed forecast."
periods = forecast["properties"]["periods"][:5]
return "\n---\n".join(
f"{p['name']}: {p['temperature']}{p['temperatureUnit']} / {p['detailedForecast']}"
for p in periods
)启动 Server
STDIO server 绝不能把日志 print 到 stdout,否则会破坏 JSON-RPC 通信。需要日志时写到 stderr。入口调用 mcp.run(transport="stdio")。
if __name__ == "__main__":
mcp.run(transport="stdio")
# 启动:
# uv run weather.py让 Claude Desktop / Client 看见它
把 server 写进 Claude for Desktop 的配置。关键是 command / args 指向你的绝对路径,并用 uv run 启动 weather.py。
claude_desktop_config.json 示例(路径换成你的绝对路径)
{
"mcpServers": {
"weather": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"run",
"weather.py"
]
}
}
}改完配置后重启 Claude for Desktop。如果在 Linux 上没有桌面端,可以改做官方的 Build an MCP client 来验证同一个 server。
怎么确认连接成功
连接成功时,你应该能在宿主里看到 weather 这个 server,并且对话中可以触发 get_alerts / get_forecast。工具返回的是与美国州天气相关的文本;这依赖 NWS 网络可用性。
- uv run weather.py 能启动,且没有因为 stdout 日志崩溃
- Claude for Desktop 能识别 weather server
- 对话中可触发 get_alerts / get_forecast
- 工具返回可读的天气相关文本
为什么模型知道该什么时候调用 Tool
模型并不是“猜到你写了一个 Python 函数”。它看到的是工具 schema:名字、参数类型、以及 docstring 里的说明。
所以描述写清楚非常重要。get_alerts 明确要求两字母州代码,模型才更可能在用户问 California alerts 时传入 CA。描述含糊、参数命名随便,调用质量会明显下降。
最容易出错的地方
宿主看不到 server
确认配置文件路径、绝对目录、JSON 合法,并重启 Claude for Desktop。
server 一启动就异常
检查是否有 print / 日志写到 stdout;改为 logging 到 stderr。
Linux 无法用 Claude for Desktop
按官方说明改做 Build an MCP client 来验证 server。
接下来可以继续做什么
对照官方完整 weather.py 补全细节,或者把 Tool 换成你自己的内部 API。下一步更自然的扩展,是做 MCP Client,理解对端如何发现工具、发起调用,并把结果回传给模型。
- 阅读 GitHub:quickstart-resources/weather-server-python
- 继续做「构建可以连接 MCP Server 的 Client」
官方资料
Model Context Protocol Docs
Build an MCP server ↗