MCP tool 是 MCP 服务器暴露供 client 调用的函数。当 LLM 决定需要采取行动 — 查找数据、运行计算、调用 API — 它会调用 tool。MCP 服务器执行 tool 并返回结果。
Tool 使用 @modelcontextprotocol/sdk 包定义。Agents SDK 处理 transport 与生命周期;无论使用 createMcpHandler 还是 McpAgent,tool 定义相同。
WebMCP 示例
在 McpServer 实例上用 server.tool() 注册 tool。每个 tool 有名称、description(LLM 用于决定何时调用)、用 Zod ↗ 定义的输入 schema 与 handler 函数。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({ name: "Math", version: "1.0.0" });
server.tool(
"add",
"Add two numbers together",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
}),
);
return server;
}import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({ name: "Math", version: "1.0.0" });
server.tool(
"add",
"Add two numbers together",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
}),
);
return server;
}Tool handler 接收已验证输入,必须返回带 content 数组的对象。每个 content 项有 type(通常为 "text")及对应数据。
Tool 结果以 content part 数组返回。最常见类型为 text,也可返回 image 与 embedded resource。
server.tool(
"lookup",
"Look up a user by ID",
{ userId: z.string() },
async ({ userId }) => {
const user = await db.getUser(userId);
if (!user) {
return {
isError: true,
content: [{ type: "text", text: `User ${userId} not found` }],
};
}
return {
content: [{ type: "text", text: JSON.stringify(user, null, 2) }],
};
},
);server.tool(
"lookup",
"Look up a user by ID",
{ userId: z.string() },
async ({ userId }) => {
const user = await db.getUser(userId);
if (!user) {
return {
isError: true,
content: [{ type: "text", text: `User ${userId} not found` }],
};
}
return {
content: [{ type: "text", text: JSON.stringify(user, null, 2) }],
};
},
);设置 isError: true 表示 tool 调用失败。LLM 收到 error 消息并决定如何继续。
description 参数至关重要 — LLM 读取它来决定是否及何时调用 tool。描述应:
- 具体说明 tool 做什么:「获取城市当前天气」优于「天气 tool」
- 清楚说明输入:「需要城市名字符串」帮助 LLM 正确格式化调用
- 诚实说明限制:「仅支持美国城市」防止 LLM 用不支持的输入调用
Tool 输入定义为 Zod schema,handler 运行前自动验证。用 Zod 的 .describe() 为 LLM 提供每个参数的上下文。
server.tool(
"search",
"Search for documents by query",
{
query: z.string().describe("The search query"),
limit: z
.number()
.min(1)
.max(100)
.default(10)
.describe("Maximum number of results to return"),
category: z
.enum(["docs", "blog", "api"])
.optional()
.describe("Filter by content category"),
},
async ({ query, limit, category }) => {
const results = await searchIndex(query, { limit, category });
return {
content: [{ type: "text", text: JSON.stringify(results) }],
};
},
);server.tool(
"search",
"Search for documents by query",
{
query: z.string().describe("The search query"),
limit: z
.number()
.min(1)
.max(100)
.default(10)
.describe("Maximum number of results to return"),
category: z
.enum(["docs", "blog", "api"])
.optional()
.describe("Filter by content category"),
},
async ({ query, limit, category }) => {
const results = await searchIndex(query, { limit, category });
return {
content: [{ type: "text", text: JSON.stringify(results) }],
};
},
);对无状态 MCP 服务器,在 factory 函数内定义 tool 并将 server 传给 createMcpHandler:
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({ name: "My Tools", version: "1.0.0" });
server.tool("ping", "Check if the server is alive", {}, async () => ({
content: [{ type: "text", text: "pong" }],
}));
return server;
}
export default {
fetch: (request, env, ctx) => {
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
};import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({ name: "My Tools", version: "1.0.0" });
server.tool("ping", "Check if the server is alive", {}, async () => ({
content: [{ type: "text", text: "pong" }],
}));
return server;
}
export default {
fetch: (request: Request, env: Env, ctx: ExecutionContext) => {
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
} satisfies ExportedHandler<Env>;对有状态 MCP 服务器,在 McpAgent 的 init() 方法中定义 tool。Tool 可通过 this 访问 agent 实例,即读写状态。
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({ name: "Stateful Tools", version: "1.0.0" });
async init() {
this.server.tool(
"incrementCounter",
"Increment and return a counter",
{},
async () => {
const count = (this.state?.count ?? 0) + 1;
this.setState({ count });
return {
content: [{ type: "text", text: `Counter: ${count}` }],
};
},
);
}
}import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({ name: "Stateful Tools", version: "1.0.0" });
async init() {
this.server.tool(
"incrementCounter",
"Increment and return a counter",
{},
async () => {
const count = (this.state?.count ?? 0) + 1;
this.setState({ count });
return {
content: [{ type: "text", text: `Counter: ${count}` }],
};
},
);
}
}