# 用官方 SDK 写一个 Python MCP 服务器

> 要用 Python 写 MCP 服务器，先用 uv 安装官方的 `mcp` 包，再创建一个 `MCPServer`，用 `@mcp.tool()` 把每个函数注册成工具。SDK 会根据函数的类型注解和文档字符串生成工具的参数结构。最后用 `mcp.run(transport="stdio")` 运行这个文件，Claude Desktop 这类宿主就能启动它并调用工具。

Mindket 编辑部 · 发布于 2026-11-04 · 更新于 2026-11-05 · https://mindket.com/cn/learn/mcp/build-mcp-server-python

## 要点

- 你会写出一个带两个工具的天气服务器：`get_alerts` 和 `get_forecast`，全部放在一个 Python 文件里。
- SDK 根据函数的类型注解和文档字符串生成每个工具的参数结构，不用手写 JSON Schema。
- stdio 服务器不能用 `print()`：标准输出上多出任何内容都会破坏协议，日志要写到 stderr。
- SDK 2.x 把 `FastMCP` 改名为 `MCPServer`，所以还在导入 `FastMCP` 的旧教程会和现在对不上。

## 开始之前

这一课做的是 MCP 官方[构建指南](https://modelcontextprotocol.io/docs/develop/build-server)里的天气服务器。多数 Python 教程都从它起步，第一个服务器该有的部分它都有：服务器对象、两个工具、会变成参数结构的文档字符串，还有 stdio 传输。

动手之前先避一个坑。Python SDK 的 2.0 版把 `FastMCP` 改名为 `MCPServer`，旧的 `mcp.server.fastmcp` 导入路径已经没有了。教程里要是还在用它，那是按 1.x 写的。现在 `pip install mcp` 装的是 2.x，下面的代码也是 2.x 的。

- Python 3.11 或更高。
- `uv`，指南用的 Python 包管理器。README 说直接用 `pip install "mcp[cli]"` 也行。
- 打过补丁的 Node.js，只在最后一步的 Inspector 里用到：22 系列要 22.23.4 或更高，24 系列要 24.22.1 或更高，或者 26.12.1 及以上。[第 3 课](https://mindket.com/cn/learn/mcp/mcp-inspector)会细讲。
- 要能上网。工具调用的是美国国家气象局的 API，它只覆盖美国境内的地点。
- Claude Desktop，想和写好的服务器对话的话。任何 MCP 宿主都可以。

**Python SDK v2，从第一个测试版说起**

1. **2026年6月30日** 第一个 v2 测试版。2.0.0b1 发布，完整支持 2026-07-28 版规范。
2. **2026年7月28日** 2.0.0 版。稳定的 v2 和新规范同一天发布，用 `MCPServer` 取代了 `FastMCP`。
3. **2026年8月24日** 2.1.0 版。`Client` 类可以接受 `StdioServerParameters`，提示词消息也能带图片和音频。
4. **2026年9月7日** 2.2.0 版。HTTP 客户端只在端点的同一来源内跟随重定向，闲置的 Streamable HTTP 会话半小时后过期。1.30.0 版也做了同样的改动。
5. **2026年10月2日** 2.3.0 版。以修复为主，另外加了三个新选项。

来源：[MCP Python SDK release v2.0.0b1](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.0.0b1) (2026-11-05 查阅)；[MCP Python SDK release v2.0.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.0.0) (2026-11-05 查阅)；[MCP Python SDK release v2.1.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.1.0) (2026-11-05 查阅)；[MCP Python SDK release v2.2.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.2.0) (2026-11-05 查阅)；[MCP Python SDK release v2.3.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.3.0) (2026-11-05 查阅)

## 七步写出服务器

指南把代码分成四段：导入、两个辅助函数、两个工具和运行入口。按这个顺序粘在一起，就是一个完整的 `weather.py`。

````python
from typing import Any

import httpx2
from mcp.server import MCPServer

# Initialize MCPServer
mcp = MCPServer("weather")

# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"


async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    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:
    """Format an alert feature into a readable string."""
    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")}
"""


@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."

    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:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        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)


if __name__ == "__main__":
    mcp.run(transport="stdio")

````

1. 安装 [uv](https://docs.astral.sh/uv/)。macOS 或 Linux 运行 `curl -LsSf https://astral.sh/uv/install.sh | sh`。Windows 运行 `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`。然后重启终端，让系统找到 `uv` 命令。
2. 用 `uv init weather` 创建项目，再用 `cd weather` 进入目录。
3. 创建并激活虚拟环境。先运行 `uv venv`，再运行 `source .venv/bin/activate`（macOS 和 Linux）或 `.venv\Scripts\activate`（Windows）。
4. 用 `uv add "mcp[cli]"` 安装 SDK。Windows 下指南写的是不带引号的 `uv add mcp[cli]`。
5. 新建 `weather.py`，把上面的代码粘进去。
6. 运行 `uv run weather.py`。它不会输出任何东西，只是停在那里，因为它在 stdin 上等宿主发消息。按 Ctrl+C 停止。
7. 用 `uv run mcp dev weather.py` 在 Inspector 里打开它，用两个字母的州代码（比如 `CA`）调用 `get_alerts`。

## 文件里每一部分在做什么

装饰器 `@mcp.tool()` 能把一个普通函数变成模型可以调用的工具。SDK 直接读这个函数本身：函数名成为工具名，文档字符串成为模型读到的说明，类型注解成为输入参数的结构。文档字符串是写给模型看的，因为读它的就是模型。

README 用 15 行演示了同样的思路：一个 `add` 工具加一个问候资源。觉得天气服务器太长，可以先从那里入手。多出来的篇幅大部分是调用天气 API 的辅助函数，和 MCP 本身无关。

这里的工具用了 `async def`，因为它们要等网络请求。

| 文件里的写法 | 它做什么 | 宿主看到什么 |
| --- | --- | --- |
| `mcp = MCPServer("weather")` | 创建服务器对象并给它起名 | 一个叫 weather 的服务器 |
| `get_alerts` 上的 `@mcp.tool()` | 把函数注册成工具 | 一个工具，有一个文本参数 `state` |
| `get_forecast` 上的 `@mcp.tool()` | 注册第二个工具 | 一个工具，有两个数字参数 `latitude` 和 `longitude` |
| `mcp.run(transport="stdio")` | 从 stdin 读请求，往 stdout 写回复 | 一个由宿主启动和关闭的本地进程 |

## 为什么 print() 会搞坏 stdio 服务器

走 stdio 时，标准输出承载的就是协议本身。服务器往那里写的每一行都必须是 JSON-RPC 消息，所以一个多余的 `print()` 就会弄乱数据流，宿主随即断开连接。指南的意思很明确：stdio 服务器里完全不要出现 `print()`。

请用标准库的 `logging` 模块，它写到 stderr，每个模块用一个 logger。走 HTTP 就没有这个限制，因为标准输出不会碰到 HTTP 响应。

````python
import logging

logger = logging.getLogger(__name__)

# Bad (stdio)
print("Processing request")

# Good (stdio)
logger.info("Processing request")  # writes to stderr

````

## 在 Claude Desktop 里运行

指南用 Claude for Desktop 来测试服务器。打开 `claude_desktop_config.json`，在 `mcpServers` 下加上这个服务器，保存文件，再重启应用。下面是指南里 macOS 和 Linux 的条目，它的 Windows 写法把路径写成双反斜杠，比如 `C:\ABSOLUTE\PATH\TO\PARENT\FOLDER\weather`。

项目文件夹的路径必须是绝对路径，`command` 字段里可能还得写 `uv` 的完整路径，运行 `which uv` 或 `where uv` 就能查到。之后要把 Claude Desktop 彻底退出再重新打开。第 4 课会把这个应用和另外两个应用放在一起演示。

````json
{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
        "run",
        "weather.py"
      ]
    }
  }
}

````

## MCP 联合创建者和 FastMCP 作者怎么说 Python SDK v2

David Soria Parra 是 Anthropic 的技术人员，也是 MCP 的联合创建者。他在 2026 年 7 月 1 日[在 X 上](https://x.com/dsp_/status/2072307451982958925)宣布，Python 和 TypeScript SDK 的第一批 v2 测试版已经发布，请开发者赶在 7 月 28 日无状态规范落地之前试用，并就好不好用给出反馈。那个日期之前的教程，大多是按 1.x 写的。

Jeremiah Lowin 是 Prefect 的创始人兼 CEO，也是 FastMCP 的作者。他在规范发布当天[在 X 上](https://x.com/jlowin/status/2082217726949445778)说，FastMCP v4 完全基于 v2 Python SDK 重写。

这个 FastMCP 是独立的包，先 `uv add fastmcp`，再 `from fastmcp import FastMCP`。它的[欢迎页](https://gofastmcp.com/getting-started/welcome)写着，官方 SDK 在 2024 年吸收的高层 API 正是由它首创。导入 `fastmcp` 的教程用的是这个独立包，导入 `mcp.server.fastmcp` 的则是 SDK 1.x 的代码。

被旧导入路径绊倒的人不少，所以 2.1.1 版让它直接指向[迁移指南](https://py.sdk.modelcontextprotocol.io/migration/)。第二天，也就是 8 月 26 日，维护者又把这条提示补进了 2.0.1，原因是很多人都碰上了这个错误。移植 1.x 的服务器时，把这份指南开在旁边。

> FastMCP v4 完全基于 v2 Python SDK 重新构建
>
> Jeremiah Lowin, Prefect 创始人兼 CEO，FastMCP 作者，引文为译文, [X](https://x.com/jlowin/status/2082217726949445778) (2026-11-05 查阅)

[![David Soria Parra（@dsp_）2026 年 7 月 1 日在 X 上发帖：新的无状态 MCP 规范将在 7 月 28 日发布，团队赶在那之前发布了 Typescript SDK v2.0.0 和 Python SDK v2.0.0 的第一批测试版，这会让构建 MCP 服务器和客户端容易得多](https://mindket.com/blog/shots/x-dsp-sdk-v2-betas.webp?v=030329044878)](https://x.com/dsp_/status/2072307451982958925)

David Soria Parra 在 X 上，2026 年 7 月 1 日

[![Jeremiah Lowin（@jlowin）2026 年 7 月 28 日在 X 上发帖：新规范发布了，FastMCP v4 测试版也一起来了。FastMCP v4 完全基于 v2 Python SDK 重新构建，你既能用上新协议的全部功能，又能保留熟悉的 FastMCP 写法](https://mindket.com/blog/shots/x-jlowin-fastmcp-v4-sdk-v2.webp?v=cfa8aba198d6)](https://x.com/jlowin/status/2082217726949445778)

Jeremiah Lowin 在 X 上，2026 年 7 月 28 日

> X 官方嵌入卡片的截图，拍摄于 2026 年 11 月 5 日，时间为 UTC。两条帖子都被 X 用“Show more”截断了。点击截图可到 X 上打开原帖。

## 常见错误

- 从 `mcp.server.fastmcp` 导入 `FastMCP`。这条路径在 2.x 里已经没有了，请用 `from mcp.server import MCPServer`。
- 在 stdio 服务器里调用 `print()`。宿主在数据流里读到乱码就会断开，请改用 `logging`。
- 在宿主的配置里写相对路径。宿主从自己的工作目录启动服务器，光写 `weather.py` 会失败，要写绝对路径。
- 改了宿主的配置却没有重启宿主。宿主只在启动时读取配置。
- 查询美国以外的预报。工具背后的天气 API 只覆盖美国境内。
- 轻信报错信息。辅助函数 `make_nws_request` 会吞掉所有异常并返回 `None`，所以任何网络问题看起来都是“Unable to fetch alerts”。调试时，请在那里把异常记进日志。

## 动手试试

把 README 里的那个资源加到 `weather.py`，重新运行 `uv run mcp dev weather.py`，到 Inspector 里找找它。资源是服务器能提供的第二类东西，和工具并列。

````python
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

````

- 它会出现在资源模板里，而不是资源里，因为它带一个参数。
- 用名字 World 读取它，确认回复是 `Hello, World!`。
- 故意加一个 `print()` 把服务器弄坏，看看 Inspector 会显示什么。

## 下一课：用 TypeScript 写同一个服务器

第 2 课用[官方 TypeScript SDK](https://mindket.com/cn/learn/mcp/build-mcp-server-typescript)写一个服务器，第 3 课再逐个标签页讲解 Inspector。如果你对协议本身还不熟，可以先看[什么是 MCP](https://mindket.com/cn/blog/what-is-mcp)，里面讲了整体架构。

> 本地运行的服务器能做你的用户账户能做的任何事。运行别人写的服务器之前，该检查什么，见[MCP 服务器安全吗？](https://mindket.com/cn/blog/are-mcp-servers-safe)。

## 自测一下

1. 在 Python SDK 2.x 里，用哪条导入来创建服务器？

   A. `from mcp.server.fastmcp import FastMCP`
   B. `from mcp.server import MCPServer`
   C. `from mcp import FastMCP`

   答案：B。 SDK 2.x 把 `FastMCP` 改名为 `MCPServer`，旧的 `mcp.server.fastmcp` 路径已经没有了。

2. 工具函数的哪一部分会变成模型读到的描述？

   A. 它的文档字符串
   B. 它的函数名
   C. 它的返回类型注解

   答案：A。 函数名成为工具名，类型注解成为输入结构，文档字符串就是描述，所以要为模型来写它。

3. stdio 服务器为什么绝对不能调用 `print()`？

   A. `print()` 比 `logging` 慢
   B. 宿主完全读不了 Python 的输出
   C. 标准输出传的是协议数据，多出来的一行会破坏这条数据流

   答案：C。 在 stdio 上，标准输出就是协议本身，所以日志要用 `logging`，它写到的是 stderr。

4. 天气服务器里，每个网络问题都显示成“Unable to fetch alerts”，这是为什么？

   A. 宿主把所有错误信息都改写了
   B. 天气 API 需要密钥，而你没有提供
   C. Python 3.11 会隐藏网络错误
   D. 辅助函数吞掉了所有异常，并返回 `None`

   答案：D。 `make_nws_request` 会捕获所有异常，所以调试时要在里面把异常记录下来。

## 常见问题

### 天气服务器能在 Python 3.10 上运行吗？

也许可以。构建指南和这个包在 PyPI 上的页面写的是 3.10 及以上，SDK 自己的文档却要求再高一个版本。几处说法不一致，所以这一课请用 3.11，3.10 只能算没有确认。

### Python SDK 从 1.x 到 2.x，除了改名还变了什么？

`@mcp.tool()`、`@mcp.resource()` 和 `@mcp.prompt()` 这几个装饰器用法不变。`host`、`port` 这类传输选项从构造函数移到了 `run()`，HTTP 客户端换成了 `httpx2`，WebSocket 传输被移除，新的 `Client` 类建在 `ClientSession` 之上。要照着旧教程做，就把版本固定成 `mcp>=1.28,<2`。

### 一定要用 uv 吗，pip 行不行？

pip 也行。README 给的备选写法是 `pip install "mcp[cli]"`，对应 `uv add "mcp[cli]"`。官方指南用 uv，是因为它也用 `uv run` 来运行文件，指南里的 Claude Desktop 配置同样调用了 `uv`。

### mcp[cli] 里的 cli 多装了什么？

多装了 `mcp` 命令：`mcp dev` 在 Inspector 里打开服务器，`mcp run` 运行服务器，`mcp install` 把它登记到 Claude Desktop。这个附加包会装上 `typer` 和 `python-dotenv`，部署上线的服务器可能用不到。

### 怎样让服务器走 HTTP 而不是 stdio？

运行 `uv run mcp run server.py --transport streamable-http`。README 接着用客户端连接 `http://localhost:8000/mcp`。stdio 适合由宿主在你的电脑上启动服务器，Streamable HTTP 才是用来部署的传输方式。

### 这个天气服务器需要 API 密钥吗？

指南的代码里没有密钥。它只给美国国家气象局的 API 发送一个 `User-Agent` 请求头，别的什么都不带。这个 API 只覆盖美国境内。

### 工具可以写成普通函数，不用 async 吗？

可以。按 SDK v2 的说明，普通的 `def` 工具、资源和提示词会在工作线程里运行。工具要等网络时用 `async def`，天气工具就是这样写的。

## 资料来源

- [MCP docs: Build an MCP server](https://modelcontextprotocol.io/docs/develop/build-server) (2026-11-04 查阅)
- [MCP Python SDK: README](https://github.com/modelcontextprotocol/python-sdk) (2026-11-04 查阅)
- [MCP Python SDK docs: Installation](https://py.sdk.modelcontextprotocol.io/get-started/installation/) (2026-11-04 查阅)
- [MCP Python SDK docs: First steps](https://py.sdk.modelcontextprotocol.io/get-started/first-steps/) (2026-11-04 查阅)
- [MCP Python SDK docs: Connect to a real host](https://py.sdk.modelcontextprotocol.io/get-started/real-host/) (2026-11-04 查阅)
- [MCP Python SDK docs: What's new in v2](https://py.sdk.modelcontextprotocol.io/whats-new/) (2026-11-04 查阅)
- [MCP docs: Connect to local MCP servers](https://modelcontextprotocol.io/docs/develop/connect-local-servers) (2026-11-04 查阅)
- [MCP Python SDK docs: Migration guide](https://py.sdk.modelcontextprotocol.io/migration/) (2026-11-05 查阅)
- [FastMCP: Welcome](https://gofastmcp.com/getting-started/welcome) (2026-11-05 查阅)
- [MCP Inspector: README](https://github.com/modelcontextprotocol/inspector) (2026-11-05 查阅)
- [MCP Python SDK release v2.0.0b1](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.0.0b1) (2026-11-05 查阅)
- [MCP Python SDK release v2.0.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.0.0) (2026-11-05 查阅)
- [MCP Python SDK release v2.0.1](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.0.1) (2026-11-05 查阅)
- [MCP Python SDK release v2.1.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.1.0) (2026-11-05 查阅)
- [MCP Python SDK release v2.1.1](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.1.1) (2026-11-05 查阅)
- [MCP Python SDK release v2.2.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.2.0) (2026-11-05 查阅)
- [MCP Python SDK release v2.3.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.3.0) (2026-11-05 查阅)
- [David Soria Parra on X: first betas of the Python and TypeScript SDK v2](https://x.com/dsp_/status/2072307451982958925) (2026-11-05 查阅)
- [Jeremiah Lowin on X: FastMCP v4 rebuilt on the v2 Python SDK](https://x.com/jlowin/status/2082217726949445778) (2026-11-05 查阅)

[浏览 MCP 服务器](https://mindket.com/cn/mcp-servers)
