---
title: "用 Python SDK 写第一个 MCP Server"
wiki: mcp
category: "服务端开发"
slug: build-server-python
url: https://learnagent.wiki/mcp/cards/build-server-python
tags: ["MCP", "Python SDK", "FastMCP", "Server 开发", "uv"]
last_updated: 2026-04-11
reading_time: 12
---

> 如果你已经理解了 MCP 的三大原语，下一步最自然的问题就是：**自己怎么写一个 Server？**

# 用 Python SDK 写第一个 MCP Server

## 基础概念

如果你已经理解了 MCP 的三大原语，下一步最自然的问题就是：**自己怎么写一个 Server？**

官方给 Python 路线的答案很明确：优先用 **Python SDK + FastMCP**。它会帮你把协议层那些烦人的事先包掉，比如：

- 生命周期和握手
- 工具 schema 生成
- 资源和提示词注册
- stdio / HTTP 传输接线

也就是说，你的主要工作不再是“手搓 JSON-RPC”，而是把业务能力用 Python 函数表达出来。

这里有两个官方事实需要先说清：

1. MCP 官方 quickstart 明确要求 **Python 3.10+**
2. quickstart 明确要求 **Python MCP SDK 1.2.0 或更高**

我又查了 PyPI 当前元数据：**截至 2026-04-11，`mcp` 包最新版本是 `1.27.0`，要求 Python `>=3.10`。**

### 核心要素

| 要素 | 作用 |
|------|------|
| **FastMCP** | 官方推荐的高层接口，用装饰器注册 tool / resource / prompt，最适合第一版开发 |
| **uv** | 官方文档和 quickstart 都优先推荐的 Python 项目管理方式 |
| **`mcp[cli]`** | 安装 SDK 的同时带上 CLI 开发工具，方便 `mcp dev`、`mcp install` |
| **传输方式** | 本地调试最常见是 stdio；生产部署更推荐 Streamable HTTP |
| **调试链路** | 开发时优先 `uv run mcp dev server.py` + Inspector；接客户端时再 `mcp install` |

### 从 Python 函数到 MCP Server 的链路

```mermaid
graph LR
    A["写普通 Python 函数"] --> B["@mcp.tool / @mcp.resource / @mcp.prompt"]
    B --> C["FastMCP 生成 MCP 能力定义"]
    C --> D["mcp.run() 启动服务"]
    D --> E["用 Inspector 调试"]
    E --> F["装到 Claude Desktop / Claude Code / VS Code"]
```

### 开发时你真正要关心的层次

```mermaid
graph TD
    A["我想写一个 MCP Server"] --> B{"先选哪种接口？"}
    B -->|"大多数场景"| C["FastMCP 高层接口"]
    B -->|"要自己控制更底层协议细节"| D["low-level server"]
    C --> E["先把功能写成 Python 函数"]
    E --> F["用装饰器注册能力"]
    F --> G["mcp dev / Inspector 调试"]
```

对第一版服务器来说，**FastMCP 几乎总是对的默认选项**。

## 基础用法

### 第一步：创建项目

官方 quickstart 推荐先用 `uv` 初始化项目：

```bash
uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]" httpx
```

如果你只是做最小本地 demo，不一定非要叫 `weather`，但安装思路就是这样。

### 第二步：写一个最小 Server

下面这个例子保留了官方 quickstart 和 Python SDK README 的核心结构，但缩成最适合入门理解的版本：

```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Return a greeting resource."""
    return f"Hello, {name}!"


@mcp.prompt()
def review_prompt(topic: str) -> str:
    """Generate a simple review prompt."""
    return f"Please review the topic: {topic}"


if __name__ == "__main__":
    mcp.run()
```

这里最关键的不是代码长短，而是这三行装饰器分别在做什么：

- `@mcp.tool()`：注册“可执行动作”
- `@mcp.resource()`：注册“可读取数据”
- `@mcp.prompt()`：注册“可复用提示词模板”

### 第三步：本地开发调试

Python SDK README 里给的最快调试方式是：

```bash
uv run mcp dev server.py
```

如果你的 Server 依赖额外包，也可以直接在开发命令里临时带上：

```bash
uv run mcp dev server.py --with pandas --with numpy
```

这条命令背后做的事情可以简单理解成：

1. 启动你的 FastMCP Server
2. 自动接上 Inspector
3. 让你马上在浏览器里调 Tools / Resources / Prompts

这一点非常适合边写边试。

### 第四步：安装到 Claude Desktop

官方 README 里还提供了直接安装到 Claude Desktop 的方式：

```bash
uv run mcp install server.py
```

也支持：

```bash
uv run mcp install server.py --name "My Analytics Server"
uv run mcp install server.py -v API_KEY=abc123 -v DB_URL=postgres://...
uv run mcp install server.py -f .env
```

这比你手改 JSON 配置更省事，尤其在开发期非常舒服。

### 第五步：如果你要直接运行

Python SDK README 还给了 direct execution 的路线：

```bash
python server.py
```

或者：

```bash
uv run mcp run server.py
```

不过有个官方限制一定要记住：

> `uv run mcp run` 和 `uv run mcp dev` 只支持 **FastMCP**，不支持 low-level server 变体。

### 第六步：什么时候切到 HTTP

如果你只是本地桌面客户端调试，`stdio` 已经够了。但 Python SDK README 也明确写了：**生产部署更推荐 Streamable HTTP**，并建议：

- `stateless_http=True`
- `json_response=True`

一个最小示意：

```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "StatelessServer",
    stateless_http=True,
    json_response=True,
)


@mcp.tool()
def hello(name: str = "World") -> str:
    """Say hello."""
    return f"Hello, {name}!"


if __name__ == "__main__":
    mcp.run(transport="streamable-http")
```

## 同类工具对比

“用 Python 写第一个 MCP Server”也有不同写法：

| 维度 | FastMCP | low-level server | 直接手搓协议层 |
|------|---------|------------------|----------------|
| 上手速度 | **最快** | 中 | 最慢 |
| 适合人群 | 第一次写 MCP Server 的大多数人 | 需要更细协议控制的人 | 几乎只适合框架作者或特殊底层场景 |
| 能否自动生成 schema | 能，依赖类型注解和 docstring | 较少自动化 | 基本没有 |
| 调试工具支持 | 很好，`mcp dev` / `mcp run` 直接支持 | 受限 | 需要自己兜底 |
| 学习成本 | 低 | 中高 | 高 |

核心区别：

- **FastMCP**：先把功能做出来，最适合第一版。
- **low-level server**：当你需要更细的协议控制，再下沉。
- **手搓协议层**：除非你在做 SDK 或特殊基础设施，否则不值得。

## 常见误区

| 误区 | 准确理解 |
|------|----------|
| 以为写 MCP Server 就得手写 JSON-RPC | 对大多数 Python 开发者来说，不需要。FastMCP 已经把大部分协议细节包好了 |
| 以为只有 Tool 才值得先学 | Tool 最常用，但 Resource 和 Prompt 也是正式原语，第一版就应该知道怎么注册 |
| 以为 `mcp dev` 只是启动一下脚本 | 不止，它本质上是把开发中的 FastMCP Server 快速接入 Inspector 调试链路 |
| 以为安装 `mcp` 就够了，CLI 可以后面再说 | 如果你要用 `mcp dev`、`mcp install`，官方推荐直接装 `mcp[cli]` |
| 以为本地写通了 stdio，生产就照抄 | 生产部署官方更推荐 Streamable HTTP，而且配置重点会变 |
| 以为 README 里的所有接口都适用于 low-level server | 不是。官方明确说 `mcp run` 和 `mcp dev` 主要支持 FastMCP，不支持 low-level 变体 |

## 优劣势分析

| 优势 | 劣势 |
|------|------|
| **上手快**：类型注解 + docstring + 装饰器就能变成 MCP 能力 | **抽象层更高**：想完全掌控底层协议时，FastMCP 不如 low-level 直接 |
| **开发体验好**：`mcp dev`、Inspector、`mcp install` 都是现成链路 | **需要理解两层概念**：业务函数一层，MCP 原语一层，新手刚开始会混 |
| **Python 生态强**：接数据分析、脚本自动化、内部 API 都很顺手 | **部署形态会分叉**：本地 stdio 简单，到了远程 HTTP 就要考虑更多工程细节 |
| **从 demo 到真实服务过渡自然**：可以先 stdio，再逐步迁移到 Streamable HTTP | **官方接口仍在持续演进**：虽然稳定可用，但版本和最佳实践还在更新 |
| **文档和示例丰富**：quickstart、README、examples、testing 文档都比较齐 | **如果只看零散博客容易踩坑**：API 名称、版本、运行命令很容易看混 |

## 思考题

<details>
<summary>初级：为什么官方推荐你先用 FastMCP，而不是一上来就学 low-level server？</summary>

**参考答案：**

因为你第一次写 MCP Server，真正的目标通常不是研究协议栈，而是尽快把一个业务能力暴露出来。

FastMCP 让你把注意力放在“我有哪些工具、资源、提示词”上，而不是“JSON-RPC 消息怎么组织、生命周期事件怎么接、schema 怎么序列化”。对绝大多数业务开发者来说，这个分层是合理的。

等你真的遇到更细的协议控制需求，再下沉到 low-level server，会更划算。

</details>

<details>
<summary>中级：什么时候你应该优先用 `mcp dev server.py`，什么时候直接 `python server.py` 就够了？</summary>

**参考答案：**

如果你还在开发中，需要频繁验证：

- tool schema 对不对
- prompt 参数对不对
- resource 能不能读
- 调用结果长什么样

那 `mcp dev server.py` 更合适，因为它天然接 Inspector，调试效率高。

如果你已经很明确自己只是要把服务跑起来，或者你在做更定制的运行环境实验，那直接 `python server.py` 或 `uv run mcp run server.py` 会更接近真实执行方式。

简单记：

- **开发调试**：优先 `mcp dev`
- **直接运行 / 自定义部署**：再考虑 `python server.py` 或 `mcp run`

</details>

<details>
<summary>进阶：为什么本地 demo 能用 stdio 很舒服，但官方又建议生产更偏向 Streamable HTTP？</summary>

**参考答案：**

因为这两个传输方式解决的是不同场景。

`stdio` 最适合本地单机：配置简单、零网络、调试直观。你在 Claude Desktop、本地 IDE、Inspector 里开发时，它几乎是最省脑子的路线。

但一旦进到生产环境，问题就变成：

- 怎么做远程部署
- 怎么接鉴权
- 怎么扩缩容
- 怎么挂到已有 Web 基础设施上

这时 Streamable HTTP 的优势就出来了，它更贴近现代服务部署习惯。Python SDK README 也明确给了生产导向建议：优先 `stateless_http=True` 和 `json_response=True`。

</details>

## 参考资料

1. MCP 官方 quickstart：Server Developer Guide：<https://modelcontextprotocol.io/quickstart/server>（查询日期 2026-04-11）
2. MCP Python SDK 官方仓库：<https://github.com/modelcontextprotocol/python-sdk>（查询日期 2026-04-11）
3. MCP Python SDK 文档站：<https://modelcontextprotocol.github.io/python-sdk/>（查询日期 2026-04-11）
4. PyPI `mcp` 包信息：<https://pypi.org/project/mcp/>（查询日期 2026-04-11）
5. MCP Python SDK 安装文档：<https://github.com/modelcontextprotocol/python-sdk/blob/main/docs/installation.md>（查询日期 2026-04-11）
6. MCP Python SDK 测试文档：<https://github.com/modelcontextprotocol/python-sdk/blob/main/docs/testing.md>（查询日期 2026-04-11）

---
*Source: https://learnagent.wiki/mcp/cards/build-server-python*
*Markdown mirror of https://learnagent.wiki, served as text/markdown for LLM ingestion.*