MCP 协议中的工具定义与注册全流程指南
传统 RAG 让大模型只能“读”到检索结果;而 Model Context Protocol(MCP) 为 LLM 赋予了“行动力”。借助 Tool,开发者可以让模型调用本地脚本、企业 API,甚至直接下单或提交 PR。本文聚焦「工具」这一核心能力,演示 如何从 0 定义、注册并安全暴露 MCP 工具,并给出 Python 与 TypeScript 双生态的最佳实践。
一、工具在 MCP 架构中的定位
- Resources:只读数据
- Tools:可产生副作用的动作
- 客户端先经
tools/list探索,再用tools/call执行 - 服务器需负责参数校验、结果格式化与安全控制
二、工具定义的三个核心元素
- 唯一名称(name):采用
snake_case或kebab-case,防止冲突 - 人类可读描述(description):让模型知道何时应调用
- 输入 / 输出 Schema:用 JSON Schema 精确定义必填字段、类型及范围;规范 2025-06 版新增
outputSchema
三、Python SDK:用装饰器 5 行代码注册工具
# server.py
from mcp.server.fastmcp import FastMCP
app = FastMCP(name="CalculatorServer")
@app.tool(
description="两个数字求和",
annotations={
"title": "Add Numbers",
"readOnlyHint": True,
"idempotentHint": True
}
)
def add(a: int, b: int) -> int:
"""返回 a + b 的结果"""
return a + b
运行服务器:
uv run server.py
四、TypeScript SDK:显式请求处理器
// server.ts
import { Server, ListToolsRequestSchema, CallToolRequestSchema } from "@mcp/server-ts";
const server = new Server({ name: "ExampleTS", version: "1.0.0" });
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "calculate_sum",
description: "Add two numbers",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}
]
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "calculate_sum") {
const { a, b } = req.params.arguments;
return { content: [{ type: "text", text: String(a + b) }] };
}
throw new Error("Tool not found");
});
启动:
node server.ts
五、最佳实践清单
- 原子化:一个工具只做一件事,便于组合
- 严格校验:用
pydantic/TypeBox限制参数,防注入 - 进度 & 取消:长任务调用
ctx.report_progress()并支持取消 - 安全注解:
readOnlyHint、destructiveHint提醒潜在风险 - 动态热更新:通过
notifications/tools/list_changed刷新客户端工具列表
六、常见错误与排查
- 工具未显示 → 检查服务器路径是否绝对、重启 Host
- 400 参数错误 → 对照
inputSchema校对字段名与类型 - 模型不使用工具 → 增加描述或示例,在系统 Prompt 中显式引导
掌握以上流程,即可让任何函数或接口化身 LLM 的“外挂”,为 Agentic AI 打造查-思-改闭环。