MCP Server 架构设计与工具注册
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read422 words

MCP Server 架构设计与工具注册

本章进入自建 MCP Server 的实战。掌握这一章,你就能为任何内部系统、私有 API 或特殊场景构建定制化的 AI 工具。

何时值得自建 MCP Server

graph TD A[需要新工具能力] --> B{有现成 Server?} B -- 有,功能满足 --> C[直接使用,无需自建] B -- 有,但功能不满足 --> D{修改成本高吗?} D -- 低,可 fork --> E[Fork 后修改] D -- 高 --> F[自建] B -- 没有 --> F F --> G{工具数量?} G -- 1-3个简单工具 --> H["FastMCP
(5分钟上手)"] G -- 多工具,有状态 --> I["官方 Python SDK
(完整控制)"] G -- TypeScript 生态 --> J["官方 TypeScript SDK"] style H fill:#27AE60,color:#fff style I fill:#4A90D9,color:#fff style J fill:#4A90D9,color:#fff

MCP Server 的生命周期

sequenceDiagram participant H as Host (Claude Desktop) participant S as MCP Server H->>S: 启动进程(stdio 模式) S->>H: 就绪信号 H->>S: initialize(协议版本协商) S-->>H: 确认,返回 Server 元数据 H->>S: tools/list(获取工具列表) S-->>H: 工具定义列表 loop 会话期间 H->>S: tools/call(调用工具) S-->>H: 工具执行结果 end H->>S: 关闭进程

Server 在整个会话期间保持运行,tools/list 只在启动时调用一次。这意味着你可以在 Server 初始化时做一些代价较高的操作(如建立数据库连接池、加载配置)。

工具注册的四个要素

每个 MCP 工具由四个核心要素定义:

from mcp import types
types.Tool(
# 1. 工具名:snake_case,动词开头,描述操作
name="search_products",
# 2. 工具描述:面向 LLM,说明什么时候用、返回什么
description="""
搜索商品目录。
适用场景:用户询问"有没有XXX商品"或"帮我找XXX类型的产品"。
返回:匹配的商品列表(名称、价格、库存、SKU)。
限制:一次最多返回50条,按相关性排序。
""",
# 3. 输入 Schema:JSON Schema 格式,精确定义参数
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词(支持商品名、品牌、类别)"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "other"],
"description": "按类别筛选,不填则搜索所有类别"
},
"max_price": {
"type": "number",
"description": "最高价格过滤(人民币)"
},
"in_stock_only": {
"type": "boolean",
"default": True,
"description": "是否只返回有库存的商品"
}
},
# 4. 必填字段声明
"required": ["query"]
}
)

工具命名规范

类型 命名模式 示例
查询操作 get_ / list_ / search_ get_order, list_products, search_customers
创建操作 create_ create_ticket, create_report
更新操作 update_ update_status, update_inventory
删除操作 archive_ / soft_delete_ archive_record(避免用 delete_,更安全)
执行动作 send_ / run_ / process_ send_notification, run_analysis
检查状态 check_ / verify_ check_availability, verify_payment

Schema 设计要点

使用 enum 限制有效值

"status": {
"type": "string",
"enum": ["pending", "processing", "shipped", "delivered", "cancelled"],
# LLM 只能选这几个值,不会猜测
}

用 description 给 LLM 提示

"date": {
"type": "string",
"description": "日期,ISO 8601 格式(YYYY-MM-DD),如 2026-03-22。不要用相对日期如'今天'"
}

设置合理的默认值

"limit": {
"type": "integer",
"default": 10,
"minimum": 1,
"maximum": 100,
"description": "返回条数,默认10,最大100"
}

工具粒度设计

太细粒度(一个操作一个工具):

get_order_basic_info      ← 需要多次调用
get_order_line_items
get_order_shipping_info
get_order_payment_info

LLM 每次查询订单需要 4 次工具调用,效率低,且容易出错。

太粗粒度(一个工具包含所有操作):

manage_orders(action="...", ...)  ← action 参数太复杂

LLM 很难正确构造 action 参数。

合适的粒度

get_order_details(order_id)     ← 一次调用获取完整订单
list_orders(filters...)         ← 列表查询(分开)
update_order_status(...)        ← 写操作(分开,有副作用)

一个完整的 Server 骨架

# server.py - MCP Server 骨架
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
# 初始化 Server,名字会在 Host 端显示
app = Server("my-custom-server")
# 工具列表(在这里集中管理所有工具定义)
TOOLS = [
types.Tool(
name="example_tool",
description="示例工具",
inputSchema={
"type": "object",
"properties": {
"input": {"type": "string"}
},
"required": ["input"]
}
)
]
@app.list_tools()
async def list_tools() -> list[types.Tool]:
"""返回所有可用工具的列表"""
return TOOLS
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
"""处理工具调用请求"""
if name == "example_tool":
result = f"处理结果: {arguments['input']}"
return [types.TextContent(type="text", text=result)]
# 未知工具
raise ValueError(f"Unknown tool: {name}")
async def main():
"""以 stdio 模式运行 Server"""
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())

本节执行清单


下一节:用 Python SDK 实现 MCP Server