之前聊过 MCP 的概念,这次拿个实际例子跑一遍:让 Claude 能查天气。
大模型能力很强,但内容不实时。它自己可不会去调天气 API。MCP 就是用来填这个坑的——通过标准化接口,让模型能发现并调用外部工具。
这个例子来自官方 quickstart:搭一个 MCP 天气服务器,暴露 get-alerts 和 get-forecast 两个工具,然后连到 Claude for Desktop。
环境准备
先安装 uv,一个快速的 Python 包管理器:
curl -LsSf https://astral.sh/uv/install.sh | sh
输出应该类似:
downloading uv 0.6.9 aarch64-apple-darwin
no checksums to verify
installing to /Users/nicolas/.local/bin
uv
uvx
everything's installed!
然后把依赖装上:

编写服务端
创建 server.py,核心逻辑其实就是两个注解 @server.list_tools() 和 @server.call_tool()。前者告诉主机你可以用哪些工具,后者处理实际调用。
完整的服务端代码:
from typing import Any
import asyncio
import httpx
from mcp.server.models import InitializationOptions
import mcp.types as types
from mcp.server import NotificationOptions, Server
import mcp.server.stdio
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
server = Server("weather")
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
""" List available tools. Each tool specifies its arguments using JSON Schema validation. """
return [
types.Tool(
name=,
description=,
inputSchema={
: ,
: {
: {
: ,
: ,
},
},
: [],
},
),
types.Tool(
name=,
description=,
inputSchema={
: ,
: {
: {
: ,
: ,
},
: {
: ,
: ,
},
},
: [, ],
},
),
]
() -> [, ] | :
headers = {
: USER_AGENT,
:
}
:
response = client.get(url, headers=headers, timeout=)
response.raise_for_status()
response.json()
Exception:
() -> :
props = feature[]
(
)
() -> [types.TextContent | types.ImageContent | types.EmbeddedResource]:
arguments:
ValueError()
name == :
state = arguments.get()
state:
ValueError()
state = state.upper()
(state) != :
ValueError()
httpx.AsyncClient() client:
alerts_url =
alerts_data = make_nws_request(client, alerts_url)
alerts_data:
[types.TextContent(=, text=)]
features = alerts_data.get(, [])
features:
[types.TextContent(=, text=)]
formatted_alerts = [format_alert(feature) feature features[:]]
alerts_text = + .join(formatted_alerts)
[
types.TextContent(
=,
text=alerts_text
)
]
name == :
:
latitude = (arguments.get())
longitude = (arguments.get())
(TypeError, ValueError):
[types.TextContent(
=, text=
)]
(- <= latitude <= ) (- <= longitude <= ):
[types.TextContent(
=, text=
)]
httpx.AsyncClient() client:
lat_str =
lon_str =
points_url =
points_data = make_nws_request(client, points_url)
points_data:
[types.TextContent(=, text=)]
properties = points_data.get(, {})
forecast_url = properties.get()
forecast_url:
[types.TextContent(=, text=)]
forecast_data = make_nws_request(client, forecast_url)
forecast_data:
[types.TextContent(=, text=)]
periods = forecast_data.get(, {}).get(, [])
periods:
[types.TextContent(=, text=)]
formatted_forecast = []
period periods:
forecast_text = (
)
formatted_forecast.append(forecast_text)
forecast_text = + .join(formatted_forecast)
[types.TextContent(
=, text=forecast_text
)]
:
ValueError()
():
mcp.server.stdio.stdio_server() (read_stream, write_stream):
server.run(
read_stream, write_stream, InitializationOptions(
server_name=,
server_version=,
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
)
)
__name__ == :
asyncio.run(main())










