A TypeScript scaffold for building Model Context Protocol (MCP) tools that let AI models call external services like sending emails or notifications.
这是一个用于开发 Model Context Protocol (MCP) 工具的脚手架项目,基于 Bun 运行时和 Express 框架构建。本项目提供了基础架构和示例工具,帮助开发者快速开始构建自己的 MCP 工具。
Model Context Protocol (MCP) 是一种允许 AI 模型调用外部工具和服务的协议。通过 MCP,AI 可以执行各种操作,如发送邮件、推送通知、查询数据库等,从而扩展其能力范围。
# 克隆脚手架仓库 git clone cd # 安装依赖 bun install
# 开发模式,支持热重载 bun dev # 生产模式 bun start
服务器将在指定端口上启动(默认为 3000)。
本脚手架的主要目的是帮助开发者快速开始构建自己的 MCP 工具。以下是开发自定义 MCP 工具的完整指南。
一个 MCP 工具通常包含以下组件:
在 src/tools/
目录下创建一个新文件,例如 src/tools/my-tool.ts
:
import { z } from "zod"; import { formatToolResponse } from "../utils/response"; /** * 为工具参数定义 schema */ export const myToolSchema = z.object({ // 定义您的工具需要的参数 param1: z.string().min(1, "参数1不能为空"), param2: z.number().optional(), // 添加更多参数... }); /** * 可选:为包含上下文信息的参数定义接口 */ interface MyToolArgs extends z.infer { apiKey?: string; // 如果您的工具需要 API 密钥 // 其他上下文信息... } /** * 实现工具功能 */ export async function myTool({ param1, param2, apiKey }: MyToolArgs) { try { console.log(`执行工具,参数1: ${param1}`); // 实现您的工具逻辑 // 例如:调用外部 API、处理数据、执行计算等 const result = await someOperation(param1, param2); // 返回成功响应 return formatToolResponse(`操作成功: ${result}`); } catch (error) { console.error("工具执行错误:", error); return formatToolResponse( `执行失败: ${error instanceof Error ? error.message : String(error)}`, true // 标记为错误 ); } } // 辅助函数示例 async function someOperation(param1: string, param2?: number) { // 实现具体操作... return "操作结果"; }
修改 src/services/tools-registry.ts
文件,导入并注册您的新工具:
// 添加导入 import { myTool, myToolSchema } from "../tools/my-tool"; export function registerTools(server: McpServer, req: Request): void { const { sendKey, scKey } = extractApiKeys(req); // 其他工具注册... // 注册您的自定义工具 server.tool( "my-tool", // 工具名称(AI 将用此名称调用工具) "这是我的工具描述", // 工具描述(帮助 AI 理解工具用途) myToolSchema.shape, // 参数 schema async (args: z.infer) => myTool({ ...args, apiKey: sendKey // 或其他上下文信息 }) ); }
// 良好的参数验证示例 export const userToolSchema = z.object({ userId: z.string().uuid("用户ID必须是有效的UUID"), action: z.enum(["create", "update", "delete"], { errorMap: () => ({ message: "操作必须是 create、update 或 delete" }) }), data: z.record(z.string(), z.any()).optional(), limit: z.number().int().positive().optional(), });
try { // 操作代码... } catch (error) { if (error instanceof NetworkError) { console.error("网络错误:", error); return formatToolResponse("连接服务失败,请检查网络", true); } else if (error instanceof ValidationError) { console.error("验证错误:", error); return formatToolResponse(`输入数据无效: ${error.message}`, true); } else { console.error("未知错误:", error); return formatToolResponse("发生未预期的错误", true); } }
console.info(`开始执行工具 ${toolName},参数:`, sanitizeParams(args)); const startTime = Date.now(); // 工具逻辑... const duration = Date.now() - startTime; console.info(`工具 ${toolName} 执行完成,耗时: ${duration}ms`);
// 成功响应示例 return { content: [ { type: "text", text: "操作成功" }, { type: "json", json: { id: result.id, status: result.status, timestamp: new Date().toISOString() } } ], isError: false }; // 错误响应示例 return { content: [{ type: "text", text: `操作失败: ${errorMessage}` }], isError: true };
设计工具时考虑它们如何协同工作,一个工具的输出可以作为另一个工具的输入。
通过请求对象传递上下文信息(如API密钥、用户信息等)。
对于长时间运行的操作,考虑使用流式响应提供进度更新。
对频繁请求的数据实现缓存,提高性能并减少外部API调用。
对外部API调用实现超时和重试逻辑,提高可靠性。
async function callWithRetry(fn, maxRetries = 3, delay = 1000) { let lastError; for (let i = 0; i setTimeout(resolve, delay)); delay *= 2; // 指数退避 } } throw lastError; }
本脚手架包含三个示例工具,您可以参考它们的实现来开发自己的工具:
一个简单的回显工具,接收消息并返回相同的消息。
// src/tools/echo.ts import { z } from "zod"; import { formatToolResponse } from "../utils/response"; export const echoSchema = z.object({ message: z.string(), }); export async function echo(args: z.infer) { return formatToolResponse(`Tool echo: ${args.message}`); }
展示如何调用外部API发送电子邮件的示例。
// src/tools/send-email.ts (简化版) import { z } from "zod"; import { formatToolResponse } from "../utils/response"; export const emailSchema = z.object({ to: z.string().email("必须是有效的电子邮件地址"), subject: z.string().min(1, "主题不能为空"), body: z.string().min(1, "邮件正文不能为空"), }); interface SendEmailArgs extends z.infer { apiKey: string; } export async function sendEmail({ to, subject, body, apiKey }: SendEmailArgs) { // 实现电子邮件发送逻辑... return formatToolResponse("电子邮件发送成功"); }
展示如何处理外部API响应和错误情况的示例。
// src/tools/wechat-push.ts (简化版) import { z } from "zod"; import { formatToolResponse } from "../utils/response"; export const wechatPushSchema = z.object({ title: z.string(), description: z.string(), }); interface WechatPushArgs extends z.infer { apiKey: string; } export async function sendWechatNotification({ title, description, apiKey }: WechatPushArgs) { try { // 调用外部API发送通知... // 处理响应... if (success) { return formatToolResponse("微信通知发送成功"); } else { return formatToolResponse(`发送失败: ${errorMessage}`, true); } } catch (error) { return formatToolResponse(`发送错误: ${error.message}`, true); } }
使用 cURL 或 Postman 向 /mcp
端点发送请求来测试您的工具:
curl -X POST http://localhost:3000/mcp \ -H "Content-Type: application/json" \ -H "mcp-send-key: sk_your_key" \ -H "mcp-sc-key: SC_your_key" \ -d '{ "jsonrpc": "2.0", "method": "tool", "params": { "tool": "my-tool", "arguments": { "param1": "测试值", "param2": 123 } }, "id": "test-1" }'
我们欢迎您为这个MCP工具开发脚手架做出贡献!请参考以下步骤:
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)Discover shared experiences
Shared threads will appear here, showcasing real-world applications and insights from the community. Check back soon for updates!