news 2026/5/5 9:59:57

基于MCP协议构建AI工具:从自定义模板到天气查询服务器实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于MCP协议构建AI工具:从自定义模板到天气查询服务器实战

1. 项目概述:从零理解一个自定义MCP模板

最近在折腾AI应用开发,特别是想给Claude Desktop或者Cursor这类工具增加一些自定义能力,发现了一个绕不开的概念:MCP(Model 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:这是你的“主战场”。在这里,你需要定义你的服务器具体能做什么。主要是实现两个核心概念:

  1. 工具(Tools):AI可以主动调用的函数。比如“查询数据库”、“发送邮件”、“重启服务”。每个工具需要定义名称、描述、输入参数(JSON Schema)和执行函数。
  2. 资源(Resources):AI可以读取的静态或动态内容。比如“当前系统日志文件”、“某个API的文档”、“数据库的Schema定义”。资源有唯一的URI,AI可以通过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.tstools/目录下。

3. 核心细节解析与实操要点

3.1 工具(Tools)的定义:从接口到实现

工具是MCP服务器与AI交互最活跃的部分。在模板的server.ts中,你会看到类似下面的结构来定义一个工具:

// 这是一个示例工具:获取当前时间 const tools: Record<string, 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}` } ] }; } } };

关键点解析:

  1. name:这是工具的标识符,在MCP协议中必须是唯一的。通常使用蛇形命名(snake_case),如search_web,execute_shell
  2. description这是最重要的部分之一。AI(如Claude)完全依赖这个描述来理解工具的用途、何时调用它以及如何调用。描述必须清晰、准确,最好能说明输入参数的意义和输出结果的格式。一个模糊的描述会导致AI无法有效利用你的工具。
  3. inputSchema:使用JSON Schema定义输入参数。这相当于给AI提供了一个强类型的“函数签名”。定义越精确,AI调用时传入的参数就越准确。务必为每个属性提供description,这能极大提升AI的理解能力。
  4. 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: Record<string, Resource> = { projectReadme: { uri: "file:///project/README.md", name: "项目自述文件", description: "本项目的详细说明文档。", mimeType: "text/markdown" } }; // 动态资源模板示例:系统状态报告 const resourceTemplates: Record<string, 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) }] }; } } };

关键点解析:

  1. uri:资源的唯一标识符,类似于URL。AI客户端通过这个URI来请求资源内容。可以使用自定义协议(如project://,system://)来组织资源。
  2. mimeType:声明资源的内容类型,帮助AI客户端正确解析(如text/plain,text/markdown,application/json)。
  3. 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 环境准备与项目初始化

首先,你需要确保本地环境就绪:

  1. 安装Node.js:版本建议在18.x或以上。可以在终端运行node --version检查。
  2. 获取模板:最直接的方式是使用Git克隆(如果原作者提供了仓库),或者你可能需要根据模板的说明手动创建文件结构。假设我们通过某种方式获得了这个模板项目。
  3. 安装依赖:进入项目目录,运行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请求的库,比如axios

npm install axios npm install --save-dev @types/axios

现在,修改src/server.ts

import { 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 };

代码要点解析:

  1. 环境变量:使用dotenv安全地加载API密钥。
  2. 输入验证:在工具执行开始就检查city参数的有效性。
  3. API调用:使用axios发起HTTP请求,并设置了超时和中文语言参数。
  4. 错误处理:这是最关键的部分。我们捕获了所有可能的错误(网络错误、API错误、无效输入),并生成了对人类和AI都友好的错误信息。通过返回{ isError: true }的内容,我们明确告知AI客户端这次调用失败了,而不是返回一个看似成功但内容错误的结果。
  5. 结果格式化:将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); });

接下来,我们需要编译和运行。

  1. 编译TypeScript:运行npm run build。这会将src/下的.ts文件编译成.js文件,输出到dist/目录(具体输出目录看tsconfig.json配置)。
  2. 直接运行(开发模式):更常用的方式是使用npm run dev。这个命令通常会使用tsxnodemon来监视文件变化并实时重启,非常适合开发。

服务器运行后,它会静静地等待来自标准输入(stdin)的连接。它自己不会主动做任何事情。要测试它,你需要一个MCP客户端。

4.4 连接与测试:使用Claude Desktop

目前,最流行的MCP客户端是Claude Desktop应用。

  1. 配置Claude Desktop:你需要编辑Claude Desktop的配置文件。在Mac上,它通常位于~/Library/Application Support/Claude/claude_desktop_config.json。在Windows上,位于%APPDATA%\Claude\claude_desktop_config.json
  2. 添加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操作(网络请求、文件读写、数据库查询)都是异步的,避免阻塞整个服务器的事件循环。
  • 超时设置:对于网络请求或长时间运行的操作,务必设置超时。就像我们在天气查询示例中使用axiostimeout配置一样。一个未处理的长时间挂起的操作会导致MCP客户端也一直等待。
  • 优雅重启与重连:在server.tsindex.ts中监听SIGTERMSIGINT信号,在进程退出前进行必要的清理工作。对于生产环境,可以考虑使用进程管理器(如PM2)来保证服务器的持续运行和故障后自动重启。

5.4 调试与日志记录

开发MCP服务器时,调试可能有点棘手,因为它通过stdio与客户端通信。

  • 使用控制台错误输出console.error()输出的内容会打印到stderr,不会干扰MCP协议通信(协议使用stdin/stdout)。这是你输出调试信息、日志和错误详情的主要渠道。
  • 结构化日志:考虑使用像winstonpino这样的日志库,将日志输出到文件,并区分不同级别(info, debug, error)。
  • 模拟客户端测试:你可以编写一个简单的Node.js脚本,模拟MCP客户端向你的服务器进程发送JSON-RPC请求,这能帮助你独立于Claude Desktop进行单元测试和集成测试。

6. 常见问题与排查技巧实录

在实际开发和部署中,你肯定会遇到各种问题。下面是一些常见坑点及其解决方案。

6.1 服务器启动失败或立即退出

  • 症状:运行npm startnode dist/index.js后进程立刻退出,或者Claude Desktop配置后无法连接。
  • 排查步骤
    1. 检查依赖:确保npm install已成功执行,没有错误。
    2. 检查环境变量:如果代码依赖环境变量(如API密钥),确保它们已正确设置。可以在启动命令前直接设置,如OPENWEATHER_API_KEY=xxx node dist/index.js测试。
    3. 检查文件路径:确认Claude Desktop配置中args指向的JavaScript文件路径绝对正确,并且该文件已由npm run build成功生成。
    4. 查看错误日志:仔细阅读终端或日志文件中打印的错误信息。最常见的错误是模块导入失败(路径错误)、TypeScript类型错误(在编译时)或运行时变量未定义。
    5. 简化测试:暂时注释掉server.ts中所有工具和资源的复杂逻辑,只保留一个最简单的“echo”工具(直接返回输入参数),看服务器是否能正常启动并与客户端通信。这可以隔离是业务逻辑问题还是基础通信问题。

6.2 Claude无法识别或调用工具

  • 症状:在Claude中提及工具相关的功能,但Claude没有反应,或者说“我没有这个能力”。
  • 排查步骤
    1. 确认服务器已连接:Claude Desktop通常会在连接MCP服务器时在界面有细微提示(如一个小图标)。最可靠的确认方法是查看Claude Desktop的日志文件(位置因系统而异)。
    2. 检查工具定义:确保在ListToolsRequest的处理程序中,返回的tools数组包含了你的工具,且namedescription正确。一个拼写错误就可能导致识别失败。
    3. 审查工具描述:AI严重依赖描述。确保描述清晰、无歧义,并准确描述了工具的用途和参数。可以尝试让另一个人阅读描述,看是否能准确理解工具是做什么的。
    4. 重启Claude Desktop:修改服务器配置或代码后,必须完全重启Claude Desktop(不仅仅是关闭窗口,而是从任务管理器/活动监视器中彻底退出再重启),配置和连接才会刷新。
    5. 检查MCP协议版本:确保你使用的@modelcontextprotocol/sdk版本与Claude Desktop支持的MCP协议版本兼容。通常使用最新稳定版SDK即可。

6.3 工具调用超时或返回错误

  • 症状:Claude尝试调用工具,但一直转圈然后失败,或者返回一个笼统的错误。
  • 排查步骤
    1. 查看服务器日志:这是最重要的调试手段。所有console.error和未捕获的异常都会输出到stderr。在运行服务器的终端里查看输出,或者在Claude Desktop的日志中查找来自你服务器的输出。
    2. 检查网络和外部依赖:如果你的工具需要调用外部API(如天气查询),首先确保你的服务器本身可以访问互联网,并且API服务本身是正常的。可以在服务器代码之外用curlPostman测试一下API。
    3. 添加超时和错误处理:如我们在示例中所做,为所有外部调用添加超时,并用详细的try...catch包裹核心逻辑,返回友好的错误信息。
    4. 验证输入参数:在工具的execute函数开头,打印接收到的参数,确保AI传递的参数与你期望的格式一致。有时AI可能会传递多余或格式不符的参数。

6.4 性能问题与资源泄漏

  • 症状:服务器运行一段时间后响应变慢,或者内存占用持续增长。
  • 排查步骤
    1. 检查异步操作:确保没有意外的同步阻塞操作(如同步文件读写fs.readFileSync)在工具中被频繁调用。
    2. 使用连接池:如果工具需要频繁访问数据库或外部服务,务必使用连接池,而不是为每次调用创建新连接。
    3. 监控内存:使用Node.js内置的process.memoryUsage()node --inspect进行性能剖析,查找内存泄漏点。常见泄漏源是缓存未设置上限,或者在全局变量中累积数据。
    4. 工具逻辑优化:对于计算密集型的工具,考虑是否有可能进行算法优化或引入缓存(对于相同输入返回相同输出的工具)。

6.5 安全风险防范

  • 命令注入:如果你的工具涉及执行系统命令(如child_process.exec),必须对用户输入(来自AI)进行严格的过滤和转义。永远不要直接将用户输入拼接成命令字符串。使用参数化数组形式(如execFile(‘ls’, [‘-la’, userInputDir]))比字符串拼接安全得多。
  • 路径遍历:对于文件操作工具,必须将用户提供的路径参数解析为绝对路径,并检查其是否在预先设定的安全目录范围内。使用path.resolve()path.relative()进行规范化检查。
  • 信息泄露:确保错误信息不会泄露敏感数据(如API密钥、服务器内部路径)。在生产环境中,返回给客户端的错误信息应该是通用的,详细的错误日志应记录在服务器端。
  • 速率限制:考虑为你的工具添加简单的速率限制,防止被恶意或错误的频繁调用拖垮服务器或触发外部API的限制。

基于bsmi021/custom-mcp-template进行开发,最大的优势是让你避开了协议层和项目基建的坑,能快速进入创造价值的阶段。从定义一个简单的查询工具开始,逐步扩展到复杂的自动化工作流,你会逐渐体会到为AI赋予“行动力”的乐趣和强大之处。记住,好的MCP工具设计,本质上是为人机协作设计一个清晰、安全、高效的接口。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 9:58:56

终极Cookie导出指南:3步安全获取cookies.txt,数据永不外传

终极Cookie导出指南&#xff1a;3步安全获取cookies.txt&#xff0c;数据永不外传 【免费下载链接】Get-cookies.txt-LOCALLY Get cookies.txt, NEVER send information outside. 项目地址: https://gitcode.com/gh_mirrors/ge/Get-cookies.txt-LOCALLY 在当今数据安全意…

作者头像 李华
网站建设 2026/5/5 9:55:53

AI Agent技能开发实战:从SBTI趣味测试看纯Prompt工程与模块化设计

1. 项目概述&#xff1a;当AI Agent遇上沙雕人格测试 最近在捣鼓AI Agent生态&#xff0c;发现一个特别有意思的玩意儿——SBTI赛博人格测试Skill。这可不是什么严肃的心理学量表&#xff0c;而是把网上爆火的那个“沙雕人格测试”给搬到了AI Agent里。简单说&#xff0c;就是让…

作者头像 李华
网站建设 2026/5/5 9:51:48

DouyinLiveRecorder:40+平台直播录制神器,轻松保存每一场精彩直播

DouyinLiveRecorder&#xff1a;40平台直播录制神器&#xff0c;轻松保存每一场精彩直播 【免费下载链接】DouyinLiveRecorder 可循环值守和多人录制的直播录制软件&#xff0c;支持抖音、TikTok、Youtube、快手、虎牙、斗鱼、B站、小红书、pandatv、sooplive、flextv、popkont…

作者头像 李华
网站建设 2026/5/5 9:46:03

创意总监技能树构建:从执行到战略的四大核心能力与实战路径

1. 项目概述&#xff1a;创意总监技能树的构建与实战价值在创意行业摸爬滚打十几年&#xff0c;从设计师到美术指导&#xff0c;再到独立带团队的创意总监&#xff0c;我越来越深刻地意识到&#xff0c;这个职位远不止是“有想法”那么简单。很多人&#xff0c;包括一些刚入行的…

作者头像 李华