1. 项目概述从零理解一个自定义MCP模板最近在折腾AI应用开发特别是想给Claude Desktop或者Cursor这类工具增加一些自定义能力发现了一个绕不开的概念MCPModel Context Protocol。简单来说MCP就是一套标准协议它能让你的AI助手比如Claude安全、可控地访问和使用你电脑上的工具、数据和服务。这就像给你的AI装上了一双“手”和“眼睛”让它不再只是空谈而是能真正帮你做事。而bsmi021/custom-mcp-template这个项目就是一个专门为想要快速上手MCP服务器开发的开发者准备的“脚手架”或“启动模板”。如果你和我一样厌倦了从零开始配置环境、搭建项目结构、处理各种样板代码那么这个模板就是为你量身定做的。它预设了开发一个MCP服务器所需的基础框架、依赖配置和最佳实践让你能跳过繁琐的初始化步骤直接聚焦于核心业务逻辑的开发。无论是想做一个连接内部数据库的查询工具还是一个调用特定API的天气插件甚至是控制智能家居的开关都可以从这个模板出发。这个模板的价值在于它把MCP开发中那些通用、重复且容易出错的部分给标准化了。你不用再纠结项目目录该怎么组织package.json里该装哪些依赖或者MCP协议的基本消息循环该怎么写。它提供了一个清晰、可扩展的起点尤其适合那些对Node.js/TypeScript有一定了解但对MCP协议细节还不甚熟悉的开发者。接下来我会带你深入拆解这个模板看看它到底包含了什么以及如何基于它快速构建出你自己的AI工具。2. 核心架构与设计思路拆解2.1 MCP协议的核心思想与模板的定位要理解这个模板的设计首先得搞懂MCP协议在解决什么问题。在没有MCP之前如果你想让你本地的AI助手例如通过Claude Desktop操作你电脑上的某个应用或数据通常需要非常复杂的集成或者干脆做不到。MCP定义了一套标准的JSON-RPC over STDIO标准输入输出的通信协议。这意味着一个MCP服务器你将要开发的东西就是一个独立的进程它通过读取标准输入stdin接收来自AI客户端的请求并通过标准输出stdout返回响应。bsmi021/custom-mcp-template的定位非常明确降低MCP服务器的开发门槛。它不是一个功能完整的服务器而是一个“半成品”或者说“蓝图”。它帮你处理了协议通信的底层细节、工具Tools和资源Resources的基本定义框架、错误处理机制以及开发环境配置。这样你只需要关心“我的服务器要提供什么工具”和“这些工具具体怎么实现”这两个核心问题。2.2 模板项目的目录结构与职责分析克隆或下载这个模板后你会看到一个典型的Node.js/TypeScript项目结构。我们来逐一分析每个关键部分的作用custom-mcp-template/ ├── package.json # 项目依赖和脚本定义 ├── tsconfig.json # TypeScript编译配置 ├── src/ │ ├── index.ts # 服务器主入口文件协议通信核心 │ ├── server.ts # 业务逻辑核心定义工具和资源 │ └── types.ts # 类型定义确保代码安全 ├── tools/ # 可选复杂工具的实现可以放在这里 ├── resources/ # 可选资源管理相关代码 └── README.md # 项目说明和快速启动指南package.json这是项目的基石。模板通常会预置几个关键依赖modelcontextprotocol/sdk这是Anthropic官方提供的MCP SDK封装了协议细节是开发MCP服务器的必需品。typescript,tsx,types/node用于TypeScript开发和执行。脚本部分定义了dev开发模式通常用tsx watch实现热重载、build编译为JavaScript和start运行编译后的产物。src/index.ts这是服务器的“大脑”或“总控中心”。它的核心工作是建立一个MCP连接并将src/server.ts中定义的工具和资源挂载上去。它处理来自stdin的原始数据流解析成MCP请求分发给对应的处理函数再将处理结果封装成MCP响应写回stdout。对于初学者来说这个文件通常不需要大改理解其流程即可。src/server.ts这是你的“主战场”。在这里你需要定义你的服务器具体能做什么。主要是实现两个核心概念工具ToolsAI可以主动调用的函数。比如“查询数据库”、“发送邮件”、“重启服务”。每个工具需要定义名称、描述、输入参数JSON Schema和执行函数。资源ResourcesAI可以读取的静态或动态内容。比如“当前系统日志文件”、“某个API的文档”、“数据库的Schema定义”。资源有唯一的URIAI可以通过URI来获取其内容。模板的server.ts里通常会提供一两个示例工具和资源让你明白基本的写法。src/types.ts良好的类型定义是TypeScript项目的优势。这里会定义工具参数、资源内容等接口帮助你在编码时获得智能提示和类型检查减少运行时错误。2.3 设计模式与扩展性考量这个模板通常采用了一种“关注点分离”的设计。协议通信 (index.ts) 和业务逻辑 (server.ts) 是分开的。这样做的好处是可维护性当MCP协议未来有更新时可能只需要修改index.ts中的通信层业务逻辑影响较小。可测试性你可以单独为server.ts中的工具函数编写单元测试而不需要启动完整的MCP服务器进程。可扩展性当工具越来越多时你可以将工具分类放到src/tools/目录下的不同文件中然后在server.ts中统一导入和注册。模板的扩展性就体现在这种目录结构上。它没有把所有的代码都堆在一个文件里而是预留了合理的组织方式。当你从开发一个简单工具到构建一个拥有数十个工具的复杂MCP服务器时这个基础结构依然能保持清晰。注意虽然模板提供了便利但你必须清楚每个文件的作用。盲目修改index.ts中的协议处理逻辑可能会导致服务器无法与客户端正常通信。在绝大多数情况下你的开发工作应集中在server.ts和tools/目录下。3. 核心细节解析与实操要点3.1 工具Tools的定义从接口到实现工具是MCP服务器与AI交互最活跃的部分。在模板的server.ts中你会看到类似下面的结构来定义一个工具// 这是一个示例工具获取当前时间 const tools: Recordstring, Tool { getCurrentTime: { name: get_current_time, description: 获取当前的系统时间并可以按指定时区格式化。, inputSchema: { type: object, properties: { timezone: { type: string, description: 可选的时区字符串例如 Asia/Shanghai 或 America/New_York。默认为系统本地时区。, default: local }, format: { type: string, description: 时间格式遵循ISO 8601标准或自定义格式。, default: iso } } }, execute: async (params: any) { // 这里是工具的执行逻辑 const { timezone, format } params; let now new Date(); // ... 根据时区和格式处理时间 ... return { content: [ { type: text, text: 当前时间是${formattedTime} } ] }; } } };关键点解析name这是工具的标识符在MCP协议中必须是唯一的。通常使用蛇形命名snake_case如search_web,execute_shell。description这是最重要的部分之一。AI如Claude完全依赖这个描述来理解工具的用途、何时调用它以及如何调用。描述必须清晰、准确最好能说明输入参数的意义和输出结果的格式。一个模糊的描述会导致AI无法有效利用你的工具。inputSchema使用JSON Schema定义输入参数。这相当于给AI提供了一个强类型的“函数签名”。定义越精确AI调用时传入的参数就越准确。务必为每个属性提供description这能极大提升AI的理解能力。execute工具的实际执行函数。它接收解析后的参数对象并返回一个固定格式的结果。返回的content是一个数组目前主要支持{type: text, text: ...}。未来可能会支持更复杂的内容类型如图片。实操心得描述即文档花时间打磨工具的description和参数的description这比写代码注释更重要。你可以想象成在给一个“外星程序员”写API文档要事无巨细。错误处理在execute函数内部一定要用try...catch包裹核心逻辑并抛出或返回结构化的错误信息。MCP SDK通常会将未捕获的异常转换成一个通用的错误响应但这不利于AI理解和处理。更好的做法是返回一个包含错误信息的text内容。副作用与安全工具可以执行任何Node.js能做的操作读文件、发网络请求、执行命令。务必谨慎在工具实现中要对输入参数进行严格的验证和清理防止命令注入、路径遍历等安全风险。例如如果一个工具是“读取文件”必须将参数限制在某个安全目录内。3.2 资源Resources的暴露让AI拥有“记忆”资源是AI可以读取的上下文信息。它可以是静态的如一份项目README也可以是动态生成的如当前的CPU使用率报表。在server.ts中资源通常通过一个resources对象和一个resourceTemplates对象来定义。// 静态资源示例项目文档 const resources: Recordstring, Resource { projectReadme: { uri: file:///project/README.md, name: 项目自述文件, description: 本项目的详细说明文档。, mimeType: text/markdown } }; // 动态资源模板示例系统状态报告 const resourceTemplates: Recordstring, ResourceTemplate { systemStats: { uriTemplate: system://stats/{type}, name: 系统状态报告, description: 获取不同类型的系统状态信息。, mimeType: application/json, // 当AI请求匹配此模板的URI时会调用此函数来生成资源内容 render: async (uri: string, params: { type: string }) { const { type } params; let stats; if (type cpu) { stats await getCpuUsage(); } else if (type memory) { stats await getMemoryUsage(); } else { throw new Error(未知的资源类型: ${type}); } return { contents: [{ uri: uri, mimeType: application/json, text: JSON.stringify(stats, null, 2) }] }; } } };关键点解析uri资源的唯一标识符类似于URL。AI客户端通过这个URI来请求资源内容。可以使用自定义协议如project://,system://来组织资源。mimeType声明资源的内容类型帮助AI客户端正确解析如text/plain,text/markdown,application/json。resourceTemplates用于定义一类动态资源。uriTemplate支持类似/stats/{type}的参数化路径。render函数负责根据URI和解析出的参数动态生成内容。资源与工具的区别工具是“动词”AI调用它来做某事。资源是“名词”AI读取它来了解某事。 资源非常适合用来为AI提供背景知识。例如你可以创建一个资源其内容是“本项目的数据库Schema定义”。当AI在回答关于数据库查询的问题时它可以先读取这个资源来获取上下文然后再调用相应的查询工具这样回答的准确性会大大提高。3.3 类型安全与配置管理模板中的types.ts文件虽然小但作用关键。它定义了工具参数、资源内容等数据的TypeScript接口。// 示例定义工具参数类型 export interface GetTimeParams { timezone?: string; format?: iso | human | timestamp; } // 在 server.ts 中使用 execute: async (params: GetTimeParams) { // 现在 params 有类型提示了 const { timezone local, format iso } params; // ... }这样做的好处是你在server.ts中编写execute函数时可以获得完整的参数类型提示和自动补全避免拼写错误和类型不匹配的问题。同时它也作为一份活的文档让项目协作者一目了然地知道每个工具需要什么。配置管理一个实用的MCP服务器通常需要一些配置比如API密钥、数据库连接字符串、安全允许的目录等。模板可能没有直接给出方案但最佳实践是使用环境变量或配置文件如.env文件配合dotenv库来管理这些敏感或可变的配置。然后在server.ts中读取这些配置。切记绝对不要将密钥硬编码在源代码中4. 从模板到实战构建一个天气查询MCP服务器现在让我们利用bsmi021/custom-mcp-template来实际构建一个简单的、可用的MCP服务器一个天气查询工具。我们将实现一个工具让AI可以询问任何城市的当前天气。4.1 环境准备与项目初始化首先你需要确保本地环境就绪安装Node.js版本建议在18.x或以上。可以在终端运行node --version检查。获取模板最直接的方式是使用Git克隆如果原作者提供了仓库或者你可能需要根据模板的说明手动创建文件结构。假设我们通过某种方式获得了这个模板项目。安装依赖进入项目目录运行npm install。这会安装package.json中列出的所有依赖包。4.2 定义天气查询工具我们的核心工作集中在修改src/server.ts文件。我们将移除或注释掉模板自带的示例添加我们自己的工具。第一步规划工具功能工具名get_weather描述查询指定城市的当前天气情况。输入参数city城市名字符串必需。执行逻辑调用一个免费的天气API例如 OpenWeatherMap获取数据并格式化成易读的文本返回。第二步实现工具代码首先我们需要一个天气API的密钥。去 OpenWeatherMap 官网免费注册一个账号获取你的API Key。然后在项目根目录创建.env文件确保该文件在.gitignore中以免泄露密钥OPENWEATHER_API_KEY你的_api_key_在这里接着安装用于发送HTTP请求的库比如axiosnpm install axios npm install --save-dev types/axios现在修改src/server.tsimport { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import axios from axios; import * as dotenv from dotenv; // 加载环境变量 dotenv.config(); const API_KEY process.env.OPENWEATHER_API_KEY; if (!API_KEY) { console.error(错误未找到 OPENWEATHER_API_KEY 环境变量。请在 .env 文件中配置。); process.exit(1); } // 创建Server实例 const server new Server( { name: weather-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, resources: {}, }, } ); // 定义工具 server.setRequestHandler(ListToolsRequestSchema, async () { return { tools: [ { name: get_weather, description: 获取指定城市的当前天气信息包括温度、湿度、天气状况和风速。, inputSchema: { type: object, properties: { city: { type: string, description: 要查询天气的城市名称例如 Beijing 或 New York。支持中文和英文城市名。, }, units: { type: string, description: 温度单位。metric 表示摄氏度imperial 表示华氏度。默认为 metric。, enum: [metric, imperial], default: metric, } }, required: [city], additionalProperties: false, }, }, ]; }; }); // 处理工具调用请求 server.setRequestHandler(CallToolRequestSchema, async (request) { const { name, arguments: args } request.params; if (name get_weather) { const { city, units metric } args as { city: string; units?: string }; // 输入验证 if (!city || typeof city ! string || city.trim().length 0) { throw new Error(城市名称不能为空。); } try { // 调用OpenWeatherMap API const url https://api.openweathermap.org/data/2.5/weather; const response await axios.get(url, { params: { q: city, appid: API_KEY, units: units, lang: zh_cn // 获取中文天气描述 }, timeout: 10000, // 10秒超时 }); const data response.data; const temp data.main.temp; const humidity data.main.humidity; const description data.weather[0].description; const windSpeed data.wind.speed; const cityName data.name; const tempUnit units metric ? °C : °F; const windUnit units metric ? 米/秒 : 英里/小时; const weatherText 城市${cityName} 温度${temp} ${tempUnit} 湿度${humidity}% 天气状况${description} 风速${windSpeed} ${windUnit}; return { content: [ { type: text, text: weatherText, }, ], }; } catch (error: any) { // 更友好的错误处理 let errorMessage 查询天气失败; if (error.response) { // API返回了错误状态码 if (error.response.status 404) { errorMessage 未找到城市 ${city}请检查城市名是否正确。; } else if (error.response.status 401) { errorMessage API密钥无效或过期请检查配置。; } else { errorMessage API错误 (状态码: ${error.response.status})。; } } else if (error.request) { // 请求已发出但没有收到响应 errorMessage 网络请求超时或失败请检查网络连接。; } else { // 请求配置出错 errorMessage 请求配置错误: ${error.message}; } // 返回错误信息给AI而不是抛出异常 return { content: [ { type: text, text: errorMessage, }, ], isError: true, // 标记这是一个错误响应 }; } } // 如果工具名不匹配返回未知工具错误 throw new Error(未知的工具: ${name}); }); // 资源相关处理本例暂不定义资源保持为空 server.setRequestHandler(ListResourcesRequestSchema, async () ({ resources: [] })); server.setRequestHandler(ReadResourceRequestSchema, async () { throw new Error(未实现); }); // 启动服务器这部分通常在 index.ts但模板可能简化到 server.ts // 实际模板中启动逻辑可能在 index.ts这里我们假设需要导出server实例 export { server };代码要点解析环境变量使用dotenv安全地加载API密钥。输入验证在工具执行开始就检查city参数的有效性。API调用使用axios发起HTTP请求并设置了超时和中文语言参数。错误处理这是最关键的部分。我们捕获了所有可能的错误网络错误、API错误、无效输入并生成了对人类和AI都友好的错误信息。通过返回{ isError: true }的内容我们明确告知AI客户端这次调用失败了而不是返回一个看似成功但内容错误的结果。结果格式化将JSON格式的API响应转换成了清晰的多行文本便于AI读取和转述给用户。4.3 配置与运行服务器现在我们需要确保服务器能正确启动。查看模板的src/index.ts如果存在它应该已经包含了建立Stdio传输和启动服务器的代码。通常它看起来像这样import { server } from ./server.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Weather MCP Server 已启动并等待连接...); } main().catch((error) { console.error(服务器启动失败:, error); process.exit(1); });接下来我们需要编译和运行。编译TypeScript运行npm run build。这会将src/下的.ts文件编译成.js文件输出到dist/目录具体输出目录看tsconfig.json配置。直接运行开发模式更常用的方式是使用npm run dev。这个命令通常会使用tsx或nodemon来监视文件变化并实时重启非常适合开发。服务器运行后它会静静地等待来自标准输入stdin的连接。它自己不会主动做任何事情。要测试它你需要一个MCP客户端。4.4 连接与测试使用Claude Desktop目前最流行的MCP客户端是Claude Desktop应用。配置Claude Desktop你需要编辑Claude Desktop的配置文件。在Mac上它通常位于~/Library/Application Support/Claude/claude_desktop_config.json。在Windows上位于%APPDATA%\Claude\claude_desktop_config.json。添加MCP服务器配置在配置文件中添加如下内容如果文件不存在就创建{ mcpServers: { weather-server: { command: node, args: [ /ABSOLUTE/PATH/TO/YOUR/PROJECT/dist/index.js ], env: { OPENWEATHER_API_KEY: 你的_api_key_在这里 } } } }重要args中的路径必须是绝对路径指向你编译后的入口文件例如index.js。你也可以在env里直接设置环境变量这样就不用在项目里放.env文件了。 3.重启Claude Desktop保存配置文件并完全重启Claude Desktop应用。 4.测试在Claude的聊天窗口中你现在可以直接说“帮我查一下北京的天气。” Claude应该会识别出你配置的MCP服务器中的get_weather工具并调用它。你会看到Claude的回复中包含了从你的服务器获取的真实天气数据。5. 进阶开发与最佳实践5.1 工具设计的艺术让AI更好用设计一个容易被AI正确理解和调用的工具需要一些技巧命名要直观工具名应该像动词短语如calculate_distance,search_github_issues。避免使用缩写或过于技术性的术语。描述要具体且包含上下文除了说明功能最好能说明“在什么情况下使用这个工具”。例如“当用户询问关于项目待办事项时可以使用此工具从Jira获取最新的任务列表。”参数设计要合理提供合理的默认值减少AI必须提供的参数数量。使用enum枚举类型限制选项避免AI自由发挥导致无效输入。对于复杂参数考虑使用嵌套的JSON Schema对象。处理模糊输入AI的理解可能不精确。例如用户说“上海天气”AI可能会调用get_weather(“上海”)。但如果用户说“那个东方明珠所在城市的天气”AI可能就无法准确提取“上海”这个参数。这时更好的设计是让工具本身具有一定的容错性或模糊匹配能力例如调用一个地理编码API将“东方明珠”解析为“上海”但这会增加工具复杂度。一个折中方案是在工具描述中明确要求“请提供明确的城市名称”。5.2 状态管理与会话上下文MCP协议本身是无状态的每个工具调用都是独立的。但有些场景需要状态。例如一个“购物车”工具需要记住用户之前添加的商品。实现思路你可以在服务器内部维护一个简单的内存存储如一个Map以某种会话ID可以从请求的metadata中获取如果客户端提供了的话为键来存储状态。但请注意Claude Desktop等客户端目前对会话上下文的支持可能有限且服务器进程重启后状态会丢失。更健壮的方式对于需要持久化状态的复杂工具建议将状态存储在外部如数据库、文件并通过一个“资源”来暴露当前状态或者设计需要显式“开始会话”、“提交会话”的工具组合。5.3 性能优化与错误恢复异步与非阻塞所有工具的执行函数都必须是async的。确保其中所有的I/O操作网络请求、文件读写、数据库查询都是异步的避免阻塞整个服务器的事件循环。超时设置对于网络请求或长时间运行的操作务必设置超时。就像我们在天气查询示例中使用axios的timeout配置一样。一个未处理的长时间挂起的操作会导致MCP客户端也一直等待。优雅重启与重连在server.ts或index.ts中监听SIGTERM和SIGINT信号在进程退出前进行必要的清理工作。对于生产环境可以考虑使用进程管理器如PM2来保证服务器的持续运行和故障后自动重启。5.4 调试与日志记录开发MCP服务器时调试可能有点棘手因为它通过stdio与客户端通信。使用控制台错误输出console.error()输出的内容会打印到stderr不会干扰MCP协议通信协议使用stdin/stdout。这是你输出调试信息、日志和错误详情的主要渠道。结构化日志考虑使用像winston或pino这样的日志库将日志输出到文件并区分不同级别info, debug, error。模拟客户端测试你可以编写一个简单的Node.js脚本模拟MCP客户端向你的服务器进程发送JSON-RPC请求这能帮助你独立于Claude Desktop进行单元测试和集成测试。6. 常见问题与排查技巧实录在实际开发和部署中你肯定会遇到各种问题。下面是一些常见坑点及其解决方案。6.1 服务器启动失败或立即退出症状运行npm start或node dist/index.js后进程立刻退出或者Claude Desktop配置后无法连接。排查步骤检查依赖确保npm install已成功执行没有错误。检查环境变量如果代码依赖环境变量如API密钥确保它们已正确设置。可以在启动命令前直接设置如OPENWEATHER_API_KEYxxx node dist/index.js测试。检查文件路径确认Claude Desktop配置中args指向的JavaScript文件路径绝对正确并且该文件已由npm run build成功生成。查看错误日志仔细阅读终端或日志文件中打印的错误信息。最常见的错误是模块导入失败路径错误、TypeScript类型错误在编译时或运行时变量未定义。简化测试暂时注释掉server.ts中所有工具和资源的复杂逻辑只保留一个最简单的“echo”工具直接返回输入参数看服务器是否能正常启动并与客户端通信。这可以隔离是业务逻辑问题还是基础通信问题。6.2 Claude无法识别或调用工具症状在Claude中提及工具相关的功能但Claude没有反应或者说“我没有这个能力”。排查步骤确认服务器已连接Claude Desktop通常会在连接MCP服务器时在界面有细微提示如一个小图标。最可靠的确认方法是查看Claude Desktop的日志文件位置因系统而异。检查工具定义确保在ListToolsRequest的处理程序中返回的tools数组包含了你的工具且name和description正确。一个拼写错误就可能导致识别失败。审查工具描述AI严重依赖描述。确保描述清晰、无歧义并准确描述了工具的用途和参数。可以尝试让另一个人阅读描述看是否能准确理解工具是做什么的。重启Claude Desktop修改服务器配置或代码后必须完全重启Claude Desktop不仅仅是关闭窗口而是从任务管理器/活动监视器中彻底退出再重启配置和连接才会刷新。检查MCP协议版本确保你使用的modelcontextprotocol/sdk版本与Claude Desktop支持的MCP协议版本兼容。通常使用最新稳定版SDK即可。6.3 工具调用超时或返回错误症状Claude尝试调用工具但一直转圈然后失败或者返回一个笼统的错误。排查步骤查看服务器日志这是最重要的调试手段。所有console.error和未捕获的异常都会输出到stderr。在运行服务器的终端里查看输出或者在Claude Desktop的日志中查找来自你服务器的输出。检查网络和外部依赖如果你的工具需要调用外部API如天气查询首先确保你的服务器本身可以访问互联网并且API服务本身是正常的。可以在服务器代码之外用curl或Postman测试一下API。添加超时和错误处理如我们在示例中所做为所有外部调用添加超时并用详细的try...catch包裹核心逻辑返回友好的错误信息。验证输入参数在工具的execute函数开头打印接收到的参数确保AI传递的参数与你期望的格式一致。有时AI可能会传递多余或格式不符的参数。6.4 性能问题与资源泄漏症状服务器运行一段时间后响应变慢或者内存占用持续增长。排查步骤检查异步操作确保没有意外的同步阻塞操作如同步文件读写fs.readFileSync在工具中被频繁调用。使用连接池如果工具需要频繁访问数据库或外部服务务必使用连接池而不是为每次调用创建新连接。监控内存使用Node.js内置的process.memoryUsage()或node --inspect进行性能剖析查找内存泄漏点。常见泄漏源是缓存未设置上限或者在全局变量中累积数据。工具逻辑优化对于计算密集型的工具考虑是否有可能进行算法优化或引入缓存对于相同输入返回相同输出的工具。6.5 安全风险防范命令注入如果你的工具涉及执行系统命令如child_process.exec必须对用户输入来自AI进行严格的过滤和转义。永远不要直接将用户输入拼接成命令字符串。使用参数化数组形式如execFile(‘ls’, [‘-la’, userInputDir])比字符串拼接安全得多。路径遍历对于文件操作工具必须将用户提供的路径参数解析为绝对路径并检查其是否在预先设定的安全目录范围内。使用path.resolve()和path.relative()进行规范化检查。信息泄露确保错误信息不会泄露敏感数据如API密钥、服务器内部路径。在生产环境中返回给客户端的错误信息应该是通用的详细的错误日志应记录在服务器端。速率限制考虑为你的工具添加简单的速率限制防止被恶意或错误的频繁调用拖垮服务器或触发外部API的限制。基于bsmi021/custom-mcp-template进行开发最大的优势是让你避开了协议层和项目基建的坑能快速进入创造价值的阶段。从定义一个简单的查询工具开始逐步扩展到复杂的自动化工作流你会逐渐体会到为AI赋予“行动力”的乐趣和强大之处。记住好的MCP工具设计本质上是为人机协作设计一个清晰、安全、高效的接口。