构建 MCP 服务器:第 4 部分 — 创建工具
- AIGC
- 2天前
- 16热度
- 0评论

这是我们构建 MCP 服务器的四部分教程的最后一部分。在第一部分中,我们使用基本资源创建了第一个 MCP 服务器。第二部分添加了资源模板并改进了代码组织。在第三部分中,我们添加了提示符并进一步完善了服务器结构。现在,我们将通过添加工具来完成服务器的搭建。
什么是 MCP 工具?
工具是 LLM 可以调用来执行操作或检索动态信息的可执行函数。与只读的资源和构建 LLM 交互的提示不同,工具允许 LLM 主动执行诸如计算值、进行 API 调用或修改数据等操作。
为什么要使用工具?
工具使 LLM 能够与系统交互并执行操作。以下是一些实际示例:
文件操作
name: "write-file"
arguments: {
path: "/logs/report.txt",
content: "Daily summary..."
}
用户:
将此报告保存到文件”
人工智能:
我将使用写入文件工具...文件已成功保存。
API 交互
name: "fetch-weather"
arguments: {
location: "San Francisco",
units: "celsius"
}
用户:
旧金山的天气怎么样?
人工智能:
让我查一下……根据天气 API,气温为 18°C,晴朗。
数据处理
name: "analyze-data"
arguments: {
dataset: "sales_2024_q1",
operation: "summary_stats"
}
用户:
计算第一季度销售额的汇总统计数据
人工智能:
正在运行分析…平均销售额为 342,中位数为 342,…
添加工具
为我们的新工具创建一个新文件,并添加一个“创建消息”工具:
// src/tools.ts
// Allowed values
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;
const tones = ['formal', 'casual', 'playful'] as const;
// tool definitions
export const tools = {
'create-message': {
name: 'create-message',
description: 'Generate a custom message with various options',
inputSchema: {
type: 'object',
properties: {
messageType: {
type: 'string',
enum: messageTypes,
description: 'Type of message to generate',
},
recipient: {
type: 'string',
description: 'Name of the person to address',
},
tone: {
type: 'string',
enum: tones,
description: 'Tone of the message',
},
},
required: ['messageType', 'recipient'],
},
},
};
到目前为止,我们所添加的只是我们工具的描述,它将允许使用该工具的模型了解它的作用以及它期望什么样的信息。
现在让我们添加实际的处理程序:
// src/tools.ts
// Allowed values
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;
const tones = ['formal', 'casual', 'playful'] as const;
// tool definitions
export const tools = {
// ... existing defs
};
type CreateMessageArgs = {
messageType: typeof messageTypes[number];
recipient: string;
tone?: typeof tones[number];
};
// Simple templates for the various message combinations
const messageFns = {
greeting: {
formal: (recipient: string) =>
`Dear ${recipient}, I hope this message finds you well`,
playful: (recipient: string) => `Hey hey ${recipient}! 🎉 What's shakin'?`,
casual: (recipient: string) => `Hi ${recipient}! How are you?`,
},
farewell: {
formal: (recipient: string) =>
`Best regards, ${recipient}. Until we meet again.`,
playful: (recipient: string) =>
`Catch you later, ${recipient}! 👋 Stay awesome!`,
casual: (recipient: string) => `Goodbye ${recipient}, take care!`,
},
"thank-you": {
formal: (recipient: string) =>
`Dear ${recipient}, I sincerely appreciate your assistance.`,
playful: (recipient: string) =>
`You're the absolute best, ${recipient}! 🌟 Thanks a million!`,
casual: (recipient: string) =>
`Thanks so much, ${recipient}! Really appreciate it!`,
},
};
const createMessage = (args: CreateMessageArgs) => {
if (!args.messageType) throw new Error("Must provide a message type.");
if (!args.recipient) throw new Error("Must provide a recipient.");
const { messageType, recipient } = args;
const tone = args.tone || "casual";
if (!messageTypes.includes(messageType)) {
throw new Error(
`Message type must be one of the following: ${messageTypes.join(", ")}`,
);
}
if (!tones.includes(tone)) {
throw new Error(
`If tone is provided, it must be one of the following: ${
tones.join(", ")
}`,
);
}
const message = messageFns[messageType][tone](recipient);
return {
content: [
{
type: "text",
text: message,
},
],
};
};
export const toolHandlers = {
"create-message": createMessage,
};
现在让我们更新我们的处理程序:
// src/handlers.ts
import {
CallToolRequestSchema, // <-- Add this
GetPromptRequestSchema,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListToolsRequestSchema, // <-- and this
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { resourceHandlers, resources } from "./resources.js";
import {
getResourceTemplate,
resourceTemplates,
} from "./resource-templates.js";
import { promptHandlers, prompts } from "./prompts.js";
import { toolHandlers, tools } from "./tools.js"; // <-- import our tools
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
export const setupHandlers = (server: Server): void => {
// List available resources when clients request them
// ... previously created handlers here
// tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: Object.values(tools),
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
type ToolHandlerKey = keyof typeof toolHandlers;
const { name, arguments: params } = request.params ?? {};
const handler = toolHandlers[name as ToolHandlerKey];
if (!handler) throw new Error("Tool not found");
type HandlerParams = Parameters<typeof handler>;
return handler(...[params] as HandlerParams);
});
};
最后,更新服务器初始化:
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { setupHandlers } from "./handlers.js";
const server = new Server(
{
name: "hello-mcp",
version: "1.0.0",
},
{
capabilities: {
resources: {},
prompts: {},
tools: {}, // <-- Add tools capability
},
},
);
setupHandlers(server);
// Start server using stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.info(
'{"jsonrpc": "2.0", "method": "log", "params": { "message": "Server running..." }}',
);
理解代码
以下是我们目前所做的工作。
工具结构
- 工具通过 inputSchema 定义其接口
- 处理程序实现实际功能
- 返回格式符合 MCP 规范
错误处理
- 验证必需参数
- 具体错误消息
- 类型安全的处理程序访问
使用检查器进行测试
记住首先构建输出:
npm run build
然后启动检查器:
npx @modelcontextprotocol/inspector node build/index.js
测试工具:
- 点击“工具”选项卡
- 找到“创建消息”
- 尝试不同的组合:
{
"messageType": "thank-you",
"recipient": "Alice",
"tone": "playful"
}
使用 Claude Desktop 进行测试
尝试以下示例
注意:您可能会遇到授权使用工具的请求:
基本信息:
用户:
为 Bob 创建问候消息
克劳德:
我会使用消息工具……“嗨,鲍勃!你好吗?”
风格化消息:
用户:
向爱丽丝致以俏皮的谢意
克劳德:
使用消息工具……“你真是太棒了,爱丽丝!🌟 非常感谢!”
不同的消息类型:
用户:
您可以创建哪些类型的消息?
克劳德:
我可以帮助您使用 create-message 函数创建不同类型的消息。您可以生成:
1. 问候
2. 告别
3. 感谢信息对于每种类型,您可以指定收件人,并可选择将语气设置为正式、随意或俏皮。您想让我演示如何创建特定类型的消息吗?
您的结果可能与上述不同,特别是如果您将此 MCP 与 Claude 以外的工具或 Sonnet 3.5 以外的模型一起使用
总结
恭喜!您现在已经构建了一个完整的 MCP 服务器,其中包含:
- 静态和基于模板的资源
- 可自定义的提示
- 动态工具
- 组织良好、类型安全的代码
您已学习了如何:
- 构建 MCP 服务器
- 实现不同的 MCP 功能
- 有效地组织代码
- 优雅地处理错误
- 使用检查器进行测试
- 与 Claude Desktop 集成
从这里,您可以:
- 添加更复杂的工具
- 与外部 API 集成
- 实现文件操作
- 添加数据库连接
- 创建您自己的自定义功能
请记住,MCP 是一个不断发展的协议,因此请关注官方文档以了解新功能和最佳实践。