MCP Server 开发踩坑:我用 3 行代码解决了 TypeScript 工具函数无法被 LLM 识别的问题 MCP Server 开发踩坑我用 3 行代码解决了 TypeScript 工具函数无法被 LLM 识别的问题搞了一下午终于搞明白为什么我的 MCP Server 工具函数死活不被 Claude 识别。坦白讲MCP 协议本身的文档写得挺清楚但 TypeScript SDK 在工具注册这个环节藏了一个不大不小的坑。记录一下免得后面的人继续踩。问题工具注册了但 LLM 说没有可用工具上周接到一个需求要把内部的一些运维脚本封装成 MCP Server让同事们通过 Claude 或 Cursor 直接调用。按照官方文档定义了工具、写好了 handler、跑起了stdio传输层。本地测试一切正常——mcp-server启动没问题日志也显示工具注册成功。但一连接到 Claude Desktop问题来了Claude 明确说没有可用的工具。我反复检查server.setRequestHandler(ListToolsRequestSchema, ...)写了server.setRequestHandler(CallToolRequestSchema, ...)写了工具列表里明明有我的函数就是识别不了。排查问题出在 zod schema 的describe上一开始我怀疑是传输层的问题换了stdio和sse两种模式结果一样。然后怀疑是 Claude Desktop 的缓存重启了三次还是一样。直到我打开 MCP Inspector 仔细对比官方示例和我的代码终于发现了差别。问题出在z.object()的字段描述上。我的代码是这样的简化后constExecCommandSchemaz.object({command:z.string(),// 没有 describetimeout:z.number(),// 没有 describe});server.setRequestHandler(ListToolsRequestSchema,async(){return{tools:[{name:exec_command,description:执行 shell 命令,inputSchema:zodToJsonSchema(ExecCommandSchema),},],};});看起来没问题但 MCP 协议要求inputSchema必须能被 LLM 理解每个字段的用途和约束。如果字段没有describeClaude 拿到的是一个裸的 JSON Schema它不知道command该填什么、timeout的单位是什么。LLM 对模糊的工具描述会直接拒绝调用而不是瞎猜。解决3 行代码搞定修复方案简单到离谱——给每个字段加上describeconstExecCommandSchemaz.object({command:z.string().describe(要执行的 shell 命令例如 ls -la),timeout:z.number().describe(命令超时时间单位秒默认 30),});就加了这两行.describe()Claude 立刻就能识别并调用工具了。说到底MCP 不是给你人看的是给 LLM 看的。LLM 依赖字段描述来理解工具的语义没有描述就等于白注册。进阶让工具描述更LLM 友好解决了基础问题后我又测试了几轮总结出让 LLM 更好识别的几个技巧1. 描述里带示例不要写目标路径要写目标路径例如 /var/log/nginxpath:z.string().describe(目标文件路径例如 /var/log/nginx/access.log),LLM 看到示例就知道该填什么格式大幅降低幻觉概率。2. 枚举值用z.enum()而不是裸字符串// 不好level:z.string().describe(日志级别可选 debug/info/warn/error),// 更好level:z.enum([debug,info,warn,error]).describe(日志级别),z.enum()会自动生成 JSON Schema 的enum字段LLM 看到这个会严格从列表里选而不是自由发挥。3. 可选字段明确标记retry:z.number().optional().describe(失败重试次数默认不重试),optional()会让 schema 生成required: []的语义LLM 知道这不是必填项不会硬编一个值进去。踩坑记录坑 1以为 zod 的description和describe是一回事zod 本身有z.string({ description: ... })的写法但这个是给 zod 内部用的不会体现在生成的 JSON Schema 里。必须用.describe()才能正确输出到inputSchema。坑 2Claude 的缓存比想象中持久调试的时候改了代码Claude Desktop 还是报没有工具。后来发现在 Claude 设置里点刷新 MCP 工具才生效单纯重启客户端不够。坑 3工具名不要用驼峰用下划线Claude 对execCommand和exec_command的理解没有本质区别但下划线更符合工具命名的惯例。而且 MCP Inspector 里显示也更整齐。坑 4description 字段不要空工具级别的description如果留空Claude 会直接忽略这个工具。哪怕你觉得工具名已经很清楚了也要写一句描述比如执行远程服务器命令并返回输出。写在最后MCP 协议的初衷很好——让 LLM 能安全、结构化地调用外部工具。但协议规范和实际 SDK 实现之间总有一些约定俗成的细节文档不会明说。这次踩坑给我的经验是给 LLM 用的接口描述比实现更重要。你写的代码能跑通只是第一步让 LLM 能正确理解并调用才是 MCP Server 真正可用的标准。如果你也在开发 MCP Server建议直接用 MCP Inspector 做一轮完整测试看看 LLM 拿到的 schema 到底长什么样。很多时候问题不在代码逻辑而在 LLM 看不到你想让它看到的东西。有问题欢迎评论区交流。

相关新闻

最新新闻

链路图断崖式失踪?5招手撕C#底层探针,彻底打通SkyWalking与国产数据库的“任督二脉”!

链路图断崖式失踪?5招手撕C#底层探针,彻底打通SkyWalking与国产数据库的“任督二脉”!

🔥关注墨瑾轩,带你探索编程的奥秘!🚀 🔥超萌技术攻略,轻松晋级编程高手🚀 🔥技术宝库已备好,就等你来挖掘🚀 🔥订阅墨瑾轩,智趣学习不…

2026/8/29 7:26:20
京东商品比价系统实战:Python+Django爬虫与结构化存储

京东商品比价系统实战:Python+Django爬虫与结构化存储

简介:电商比价系统是Web数据工程的经典实践场景,其本质是构建一条从HTTP请求、HTML解析、数据清洗、关系型存储到API服务的完整数据链路。核心原理在于精准模拟浏览器行为绕过基础反爬(如User-Agent、Referer、Cookie组合校验)&am…

2026/8/29 7:26:20
小米服务端校招主观题复盘:高并发与系统设计核心考点全解析

小米服务端校招主观题复盘:高并发与系统设计核心考点全解析

2018年秋招那会儿,我把小米服务端工程师的笔试主观题从头到尾啃了一遍。整张卷子没有选择题,全是需要现场组织语言、画架构、写方案的设计题。说实话,那是我秋招遇到的第一份“考思路比考记忆多”的卷子。现在回看,这套主观题其实…

2026/8/29 7:26:20
NS-2网络模拟器安装与实战:从有线到无线网络仿真指南

NS-2网络模拟器安装与实战:从有线到无线网络仿真指南

简介:本资源是经典网络仿真平台 ns-allinone-2.26 的完整源码发行包,面向计算机网络专业师生、协议研究者及仿真实验学习者,用于深入理解MAC层与网络层(路由)工作机制,支撑TCP/UDP、无线LAN、AODV、DSDV、S…

2026/8/29 7:26:20
KMP、Manacher、bfprt三大线性算法精讲:从暴力到最优

KMP、Manacher、bfprt三大线性算法精讲:从暴力到最优

1. 为什么把三个算法放在一讲最近在整理经典算法题精讲系列,这一讲比较特殊,把Manacher算法、bfprt算法、KMP算法放在了一起。乍一看这三个东西八竿子打不着——一个管回文串,一个管TopK,一个管字符串匹配。但把它们放一起讲是有道…

2026/8/29 7:26:20
【关注可白嫖源码】--课程设计--毕业设计--springboot导盲眼镜管理系统[编号:project54336](案件分析)

【关注可白嫖源码】--课程设计--毕业设计--springboot导盲眼镜管理系统[编号:project54336](案件分析)

本文仅展示核心实现逻辑与部分代码片段,完整项目源码、配套文档、数据库脚本内容较多,篇幅有限无法全部放出。 有需要完整资源的同学,可以在评论区留言【资料或领源码】,我会一 一回复站内私信,发送完整文件 摘 要 视…

2026/8/29 7:21:20