3.1 MCPServer 的构造与生命周期


3.1 MCPServer 的构造与生命周期

本节摘要:服务端主线开始。本节先把 MCPServer 这个高层容器的构造与生命周期讲透。MCPServer("名称") 创建的不只是一个对象,而是一个能力容器——它把工具管理器、资源管理器、提示词管理器组织起来,等待你注册函数,也等待被传输层驱动。我们会讲清它的构造参数、它内部管理的三大子管理器、以及「定义能力」与「真正运行」两个阶段的分离。理解这个容器,你才能理解后续每一章往里「放东西」时到底放到了哪。

一、构造:一个能力容器的诞生

最简构造只需要一个参数——服务端名字:

from mcp.server import MCPServer mcp = MCPServer("Demo")

这一行做了什么?它在内存里建起了一个空的能力容器,内部已经组织好三大子管理器:

MCPServer("Demo") 内部结构 ┌─────────────────────────────────────────┐ │ MCPServer(能力容器) │ │ ├── 工具管理器(ToolManager) │ ← @mcp.tool 往这放 │ ├── 资源管理器(ResourceManager) │ ← @mcp.resource 往这放 │ ├── 提示词管理器(PromptManager) │ ← @mcp.prompt 往这放 │ └── (内部状态:能力声明、版本等) │ └─────────────────────────────────────────┘

此时这个容器还是空的——没注册任何工具/资源/提示词,也没有被传输驱动。它只是一个「等待被填充、等待被运行」的容器。

二、构造参数详解

MCPServer 的构造参数,常用的有这几个:

参数 类型 作用 必填
名称 字符串 服务端名字,出现在能力声明与日志里
依赖/配置 (可选) 注入额外配置,如自定义中间件
提示 (可选) 控制指令提示等高级选项

第 1 章我们只用了名字。后续章节会逐步用到其他参数——比如第 12 章会讲如何注入自定义中间件。本节记住一点:构造期只决定「这个容器长什么样」,不决定「它现在在跑什么」

💡 技巧:服务端名字虽然只是个标识,但起得好能省很多调试麻烦。建议起一个能体现用途的名字,如 "database-tools""file-resources",而不是 "server""mcp" 这种无信息量的名字。这个名字会出现在客户端的能力声明、检查器界面、日志里。

三、两大阶段的分离

理解 MCPServer 的关键,是认清它的生命有两个独立阶段:

阶段一:定义能力(注册期) ┌─────────────────────────────────────────┐ │ mcp = MCPServer("Demo") │ │ @mcp.tool() │ │ def add(...): ... │ ← 往容器里放能力 │ @mcp.resource(...) │ │ def greeting(...): ... │ └─────────────────────────────────────────┘ ↑ 此时尚未运行,只是定义 阶段二:真正运行(运行期) ┌─────────────────────────────────────────┐ │ mcp.run() # 跑成 stdio 服务 │ ← 被传输驱动 │ # 或 │ │ mcp.run("streamable-http") │ ← 跑成 HTTP 服务 └─────────────────────────────────────────┘ ↑ 现在才真正开始接受客户端连接

这两个阶段的分离,是 SDK 设计的一个关键。它带来三个好处:

好处 说明
可测试 阶段一定义的容器,可在不启动传输的情况下用内存客户端测(第 1.3 节)
可组合 同一个容器定义,可分别用不同传输跑(stdio/HTTP),业务代码不变
可检查 阶段一就能用检查器看能力清单,不用真启动

四、注册期:三大装饰器的统一哲学

阶段一(注册期)里,你用三个装饰器往容器放能力:

mcp = MCPServer("Demo") @mcp.tool() # 放进工具管理器 def add(a: int, b: int) -> int: ... @mcp.resource("greeting://{name}") # 放进资源管理器 def greeting(name: str) -> str: ... @mcp.prompt() # 放进提示词管理器 def summarize(text: str) -> str: ...

三个装饰器背后是同一套哲学:函数即元数据来源。它们都从函数身上读名字、描述、参数,差别只在「读出来当什么」:

装饰器 函数名当 文档字符串当 类型注解当
@mcp.tool() 工具名 工具描述(给模型) 输入 Schema(给模型)
@mcp.resource(uri) 处理器名(内部) 资源描述 URI 参数(从 URI 占位符)
@mcp.prompt() 提示词名 提示词描述 扁平字符串参数列表

注意资源稍微特别——它的参数既来自 URI 占位符({name}),也来自函数签名,SDK 会把两者对齐。第 5 章详讲。

五、运行期:被传输驱动

阶段二(运行期)才让容器真正工作。最常见的两种启动方式:

# 方式一:跑成 stdio 服务(本地默认) mcp.run() # 方式二:跑成流式 HTTP 服务(生产) mcp.run("streamable-http")

run() 这个方法会阻塞(进入事件循环),开始监听传输、接受客户端连接、分发请求。在第 8 章会详讲每种传输的具体配置,这里先建立直觉:run() 之前是「定义」,run() 之后是「运行」

⚠️ 注意:内存连接(第 1.3 节的 Client(mcp))是这条规律的例外——它不调用 mcp.run(),而是直接把容器对象喂给客户端,在进程内驱动。这正是它适合测试的原因:跳过传输、跳过运行期启动,直接验证阶段一定义的能力

六、能力声明:从注册推断

这里先剧透一点第 3.3 节的内容,帮你建立整体感。MCPServer 在客户端连接时,会根据已注册的能力,自动推断并声明 capabilities:

你注册了 服务端自动声明
任意 @mcp.tool() tools 能力
任意 @mcp.resource() resources 能力
任意 @mcp.prompt() prompts 能力
一个补全处理器 completions 能力
都没注册 (对应能力不声明)

这就是第 1.3 节看到的 server_capabilities 字典的来源——它不是你写的,是 MCPServer 从你注册的东西推断出来的。这条「注册即声明」的规则贯穿整个 SDK,第 3.3 节会专门讲。

本节要点回顾

  1. MCPServer("名称") 创建的是一个能力容器,内部组织工具/资源/提示词三大管理器。
  2. 构造期只决定容器长什么样,常用的必填参数只有名字。
  3. 生命分两阶段:注册期(定义能力)与运行期(被传输驱动),两者分离。
  4. 两阶段分离带来三个好处:可测试、可组合、可检查。
  5. 三个装饰器背后是同一套哲学:函数即元数据来源,差别只在「读出来当什么」。
  6. mcp.run() 是阶段二的入口,之前是定义,之后是运行;内存连接是这条规律的例外。
  7. 能力声明从注册推断,注册什么就声明什么——这是第 3.3 节的主题。

容器清楚了,下一节我们逐个展开三个装饰器,看它们如何从函数读出元数据。


作者与出处
原作者: 灏天文库
来源:modelcontextprotocol
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U