Tutorial-Codebase-Knowledge 解析 MCP Python SDK(二):用 `FastMCP` 构建 MCP 服务器的极简之道

原创2026-09-22 17:47:261,731 阅读
文章标签:AI 应用大模型AI Agent代码智能体

Tutorial-Codebase-Knowledge 解析 MCP Python SDK(二):用 FastMCP 构建 MCP 服务器的极简之道

FastMCP 是 MCP Python SDK 提供的高层服务器框架,它把 MCP 协议中连接管理、消息编解码、能力协商等底层细节封装成"一键操作",让开发者只需用普通 Python 函数加装饰器就能产出完整可用的 MCP 服务器。本文以 docs/MCP Python SDK/02_fastmcp_server___fastmcp__.md 为核心脉络,从最小服务器、@server.tool() 装饰器到 ToolManager 内部调度机制逐层展开,读完你将掌握 FastMCP 的创建、运行与扩展方法,并理解一次 callTool 请求从客户端到你的 Python 函数的完整调用链。

FastMCP 是什么

在 MCP 协议层面,构建一个服务器需要处理连接、按协议解释消息、管理能力(capabilities)声明等大量琐碎工作。以"回声服务器"为例——仅仅为了让 AI 助手把你输入的内容原样返回,就需要写一大堆底层代码。

FastMCP 正是为解决这一问题而生的高层抽象。原文档用了一个非常形象的比喻:

  • 底层 MCP 协议的各个部件就像厨房里单个的工具:锅、平底锅、刀、打蛋器。你当然可以用它们各自完成烹饪,但必须清楚每件工具的使用时机与方法。
  • FastMCP 则是一台"多功能厨房料理机(kitchen multi-cooker)":它把常见功能打包成易用的整体。你只需提供"食材"(你的 Python 函数与数据),按下简单"按钮"(@tool、@resource、@prompt 等特殊标记),FastMCP 就替你完成复杂"烹饪过程"(管理底层 MCP 细节)。

使用 FastMCP 的核心收益:

优势 说明
简单(Simplicity) 隐藏了大量复杂的 MCP 协议细节
开发者友好(Developer-Friendly) 使用熟悉的 Python 概念:函数与装饰器
样板代码少(Less Boilerplate) 大幅减少重复性的初始化与接线代码
内置功能(Built-in Features) 提供设置管理、能力自动生成(自动告知客户端你的服务器能做什么)、通用任务处理等开箱即用的能力

从本项目教程系列的整体结构看,FastMCP 对应的是 docs/MCP Python SDK/index.md 中定义的 Abstraction 3,它建立在会话(Abstraction 1)与传输(Abstraction 2)之上,并统一管理资源(Abstraction 4)、工具(Abstraction 5)与提示(Abstraction 6)。

你的第一个 FastMCP 服务器:最小骨架

让我们从创建一个 FastMCP 服务器所需的绝对最小代码开始。

文件:my_simple_server.py

# 1. 导入 FastMCP 类
from mcp.server.fastmcp import FastMCP

# 2. 创建 FastMCP 服务器实例
#    给它一个客户端可能看到的名称。
#    可选地提供总体说明(instructions)。
server = FastMCP(
    name="MySimpleServer",
    instructions="This is a very simple example server."
)

# 3. 添加标准 Python 主入口块,
#    使脚本被直接执行时运行服务器。
if __name__ == "__main__":
    print(f"Starting {server.name}...")
    # 告诉 FastMCP 开始监听连接
    server.run()
    print(f"{server.name} finished.") # 通常只在停止(Ctrl+C)后看到

逐行解读:

  1. from mcp.server.fastmcp import FastMCP:从 SDK 中导入核心的 FastMCP 类。
  2. server = FastMCP(...):创建我们的"多功能料理机"对象。
    • name="MySimpleServer":服务器的人类可读名称,客户端可能展示该名称。
    • instructions="...":对服务器用途的总体描述,客户端可用它理解服务器是做什么的。
  3. if __name__ == "__main__"::标准 Python 模式,只有直接执行脚本时(例如 python my_simple_server.py 或 mcp run my_simple_server.py)才会运行块内代码。
  4. server.run():真正启动服务器的命令,让 FastMCP 开始监听传入连接并处理 MCP 消息。默认使用 "stdio" 传输(从终端读写),这正是 docs/MCP Python SDK/01_cli___mcp__command_.md 第 1 章讨论过的传输方式。

将上述代码保存为 my_simple_server.py 后,用第 1 章学到的命令 mcp run my_simple_server.py 即可启动它。此时它还没有任何具体能力,但已经是一个功能完整的 MCP 服务器。

提示:mcp run 会在你的脚本中查找名为 mcp、server 或 app 的服务器对象;也可以显式指定对象,如 my_simple_server.py:server,随后调用该对象的 .run() 方法。

用装饰器添加功能:料理机的"按钮"

我们的"料理机"(FastMCP)已经通电运行,但还没有任何"烹饪程序"。如何添加类似"echo"这样的功能?答案是装饰器(decorators)。

Python 装饰器是以 @ 开头的特殊标记,放在函数定义上方,用于修改或增强函数。FastMCP 使用 @server.tool()、@server.resource()、@server.prompt() 等装饰器,把 Python 函数轻松注册为客户端可用的能力。

下面用 @server.tool() 装饰器添加一个 "echo" 工具。

文件:echo_server.py

from mcp.server.fastmcp import FastMCP

# 1. 创建服务器实例
server = FastMCP(name="EchoServer")

# 2. 使用 @server.tool 装饰器定义工具
@server.tool(name="echo", description="Repeats the input message back.")
def echo(message: str) -> str:
  """
  这个函数现在被注册为名为 'echo' 的工具。
  'message: str' 告诉 FastMCP 该工具期望一个名为 'message' 的字符串参数。
  '-> str' 告诉 FastMCP 该工具将返回一个字符串。
  """
  print(f"Tool 'echo' called with message: {message}") # 服务端日志
  # 3. 函数逻辑直接实现工具行为
  return f"You said: {message}"

# 4. 标准运行块
if __name__ == "__main__":
    print(f"Starting {server.name}...")
    server.run() # 开始监听
    print(f"{server.name} finished.")

解读:

  1. server = FastMCP(...):与之前相同,创建服务器对象。
  2. @server.tool(...):魔法所在!
    • 将 server 对象的 tool() 方法用作装饰器。
    • name="echo":显式告诉 FastMCP 客户端应以 echo 名称调用此工具。如果省略,则默认使用函数名(echo)。
    • description="...":给客户端的有用描述。
  3. def echo(message: str) -> str::标准 Python 函数。
    • message: str:类型注解。告诉 FastMCP(以及其他工具)此函数期望一个名为 message 的字符串参数。FastMCP 利用该信息自动校验客户端输入并生成文档。
    • -> str:表示函数将返回字符串,FastMCP 据此了解期望的输出类型。
    • 函数体包含工具的具体逻辑。
  4. server.run():启动服务器。得益于装饰器,服务器现在已"知道" echo 工具。

现在运行 mcp run echo_server.py,服务器启动后即可响应 echo 工具的请求。客户端发送 callTool 请求,名称 echo、参数 {"message": "Hello!"},FastMCP 会自动执行你的 echo 函数并把结果 "You said: Hello!" 返回给客户端。

原文档将 @server.resource() 与 @server.prompt() 的详细讲解留给后续章节:

FastMCP 底层工作原理(简化视角)

用起来很简单,但 FastMCP 内部到底做了什么?

  1. 初始化(Initialization):创建 FastMCP() 时,它内部建立了工具、资源、提示的管理器(如 _tool_manager、_resource_manager、_prompt_manager)。
  2. 注册(Registration):Python 遇到 echo 函数上方的 @server.tool(...) 时,调用 server.tool() 方法。该方法把你的 echo 函数及其信息(名称、描述、类型注解推导出的参数类型)注册到内部的 _tool_manager。
  3. 运行(Running):调用 server.run() 时,FastMCP 启动底层 MCP 服务器机制,监听传入连接(如 stdio 或 web 协议)。
  4. 处理请求(Handling Requests):当客户端连接并发送 MCP 消息,如 {"method": "callTool", "params": {"name": "echo", "arguments": {"message": "Test"}}}:
    • 底层服务器收到原始消息;
    • FastMCP 核心逻辑接管,识别出这是针对名为 echo 的 callTool 请求;
    • 向 _tool_manager 询问是否知道名为 echo 的工具;
    • _tool_manager 找到已注册的 echo 函数;
    • FastMCP 从请求中提取 arguments({"message": "Test"});
    • 依据函数签名(message: str)校验这些参数;
    • 调用你的真实 Python echo 函数,把 "Test" 作为 message 参数传入;
    • 函数执行并返回 "You said: Test";
    • FastMCP 把返回值打包成合法的 MCP callTool 响应消息,经底层机制发回客户端。

时序图:

sequenceDiagram
    participant Client
    participant FastMCP_Server as FastMCP (echo_server.py)
    participant ToolManager as _tool_manager
    participant EchoFunction as echo()

    Client->>+FastMCP_Server: Send MCP Request: callTool(name="echo", args={"message": "Test"})
    FastMCP_Server->>+ToolManager: Find tool named "echo"
    ToolManager-->>-FastMCP_Server: Return registered 'echo' function info
    FastMCP_Server->>+EchoFunction: Call echo(message="Test")
    EchoFunction-->>-FastMCP_Server: Return "You said: Test"
    FastMCP_Server->>-Client: Send MCP Response: result="You said: Test"

结合源码看实现(简略)

你不必理解每一行,但了解关键代码发生的位置会很有帮助。原文档给出了 server/fastmcp/server.py 中 FastMCP 的三个核心片段的简化版本(路径对应 MCP Python SDK 上游源码结构,当前教程仓库中对应的讲解见 docs/MCP Python SDK/index.md 的架构图)。

构造与内部管理器:FastMCP.__init__

# (...) imports (...)
from .tools import ToolManager
from .resources import ResourceManager
from .prompts import PromptManager

class FastMCP:
    def __init__(
        self, name: str | None = None, instructions: str | None = None, **settings: Any
    ):
        # 存储 debug 模式、日志级别等设置
        self.settings = Settings(**settings)

        # 创建底层低层 MCP 服务器
        self._mcp_server = MCPServer(
            name=name or "FastMCP",
            instructions=instructions,
            # ... 其他底层设置 ...
        )
        # 创建管理器以跟踪已注册的条目
        self._tool_manager = ToolManager(
            warn_on_duplicate_tools=self.settings.warn_on_duplicate_tools
        )
        self._resource_manager = ResourceManager(
            # ...
        )
        self._prompt_manager = PromptManager(
            # ...
        )

        # 将 MCP 请求(如 'callTool')连接到 FastMCP 方法
        self._setup_handlers()
        # (...)

可见 FastMCP 创建了若干辅助对象(_tool_manager 等)来组织你注册的工具、资源与提示。注意 ToolManager 构造时传入了 warn_on_duplicate_tools 设置——当工具重名时是否给出警告,这是 settings 驱动的行为之一。

注册入口:FastMCP.tool 装饰器

# (...) imports (...)
from mcp.types import AnyFunction # 代表任意 Python 函数

class FastMCP:
    # (...) other methods (...)

    def tool(
        self, name: str | None = None, description: str | None = None
    ) -> Callable[[AnyFunction], AnyFunction]:
        """Decorator to register a tool."""
        # (...) error checking (...)

        # 这是真正作用于 'echo' 函数的函数
        def decorator(fn: AnyFunction) -> AnyFunction:
            # 让工具管理器记住这个函数 'fn',
            # 将其与给定名称和描述关联。
            # 它还会检查 'fn' 以推导其参数(如 'message: str')
            self.add_tool(fn, name=name, description=description)
            return fn # 原样返回函数

        return decorator # 返回 'decorator' 函数

    def add_tool(
        self,
        fn: AnyFunction,
        name: str | None = None,
        description: str | None = None,
    ) -> None:
        """Add a tool to the server."""
        # 将函数及其信息传给 ToolManager
        self._tool_manager.add_tool(fn, name=name, description=description)

这展示了 @server.tool() 装饰器最终调用 self._tool_manager.add_tool() 注册函数的完整链路。注册阶段会在 ToolManager.add_tool 中通过 Tool.from_function 对函数做内省(introspection):提取函数名、描述、参数与返回类型,据此构建描述期望输入参数的 schema(类似一份迷你表单定义),然后封装成内部 Tool 对象存入以名称为键的字典。

请求分发:FastMCP.call_tool 处理器

# (...) imports (...)

class FastMCP:
    # (...) other methods (...)

    async def call_tool(
        self, name: str, arguments: dict[str, Any]
    ) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
        """Call a tool by name with arguments."""
        # 获取一个 'Context' 对象(后续章节详解!)
        context = self.get_context()
        # 让 ToolManager 查找并执行工具
        # ToolManager 负责找到你的 'echo' 函数、
        # 校验参数并调用它。
        result = await self._tool_manager.call_tool(name, arguments, context=context)
        # 把函数返回值(如 "You said: Test")
        # 转换为 MCP 响应所需的格式。
        converted_result = _convert_to_content(result)
        return converted_result

    def _setup_handlers(self) -> None:
        """Set up core MCP protocol handlers."""
        # 这行把底层 'callTool' 消息
        # 连接到上面所示的 'self.call_tool' 方法
        self._mcp_server.call_tool()(self.call_tool)
        # (...) listTools、readResource 等其他处理器 ...

这展示了传入的 callTool 消息如何被路由到 call_tool 方法,再由其借助 _tool_manager 运行你注册的函数。值得注意的两点:

  • call_tool 首先调用 self.get_context() 创建请求级 Context 对象,随后随调用一并传给 ToolManager——这是后续 docs/MCP Python SDK/06_fastmcp_context___context__.md 中 ctx.info()、ctx.report_progress()、ctx.read_resource() 等功能注入的入口;
  • 返回值经 _convert_to_content 统一转换为 TextContent | ImageContent | EmbeddedResource 序列,这正是 MCP 协议 callTool 响应所要求的 content 结构。

与工具一章的呼应

关于 ToolManager 如何查找工具、校验参数并调用函数(Tool.run),原文档在 docs/MCP Python SDK/04_fastmcp_tools___tool____toolmanager__.md 中给出了更细的代码片段,包括 ToolManager.add_tool(Tool.from_function 内省 + 去重 + 存入 self._tools 字典)与 ToolManager.call_tool(get_tool 查找,未知工具抛出 ToolError,随后 await tool.run(arguments, context=context))。其参数校验依据的 FuncMetadata 正是在注册阶段从类型注解生成。

运行与调试:结合 CLI 的完整工作流

FastMCP 服务器通常与第 1 章介绍的 mcp CLI 配合使用:

# 直接运行服务器(stdio 传输)
mcp run echo_server.py

# 开发调试:启动服务器并附带 MCP Inspector(Web 调试界面)
mcp dev echo_server.py

# 注册到 Claude Desktop 等客户端应用
mcp install echo_server.py --name "My Echo Tool"
  • mcp dev 会额外启动基于 npx 的 MCP Inspector,让你在浏览器中实时查看收发消息、测试工具调用——调试 FastMCP 服务器的首选方式。
  • mcp install 会向客户端配置文件(如 Claude 的 claude_desktop_config.json)写入 uv run --with mcp mcp run /path/to/echo_server.py 这样的启动命令,本质上是自动化"告诉客户端如何找到并执行你的自定义服务器"的过程。

结论

至此你已看到 FastMCP 相比直接处理底层协议,构建 MCP 服务器要简单得多。就像多功能料理机一样,它提供便捷的"按钮"(@server.tool() 等装饰器),用标准 Python 函数为服务器添加功能(如工具),并替你处理接收请求、调用代码、发送响应的全部底层复杂性。

你已学会:

  • 创建基础的 FastMCP 服务器实例(name、instructions 与 settings 参数);
  • 定义执行任务的 Python 函数,并用 @server.tool() 装饰器将其注册为客户端可调用的工具;
  • 通过类型注解(message: str、-> str)驱动参数校验与自动文档生成;
  • 理解 FastMCP 利用内部管理器(_tool_manager、_resource_manager、_prompt_manager)处理工具调用请求的基本流程,以及 FastMCP.__init__ → @server.tool → add_tool → ToolManager.add_tool → call_tool → _convert_to_content 的完整调用链。

虽然 echo 工具很简单,但 FastMCP 为构建更复杂、更强大的 AI Agent 与工具提供了坚实基础。接下来可以沿着教程序列继续探索其他"按钮":先用 @server.resource() 提供数据与文件(docs/MCP Python SDK/03_fastmcp_resources___resource____resourcemanager__.md),再用 @server.prompt() 管理可复用的 LLM 消息模板(docs/MCP Python SDK/05_fastmcp_prompts___prompt____promptmanager__.md),最后用 Context 让工具与资源在请求期间具备日志、进度与跨能力访问(docs/MCP Python SDK/06_fastmcp_context___context__.md)。

登录后查看全文
Tutorial-Codebase-Knowledge