拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MCP自定义服务器进阶指南:错误处理、流式输出与部署实践

从去年开始我陆陆续续在几个真实项目里接了 MCP 自定义服务器开发的活从最简单的“给 AI 加一个查内部文档库的工具”到后来做带流式输出、异常频率极高的生产级服务这一路踩过的坑比我预想要多得多。MCPModel Context Protocol模型上下文协议本质上就是给 AI 应用Claude、Cursor、Codex、Trae 这类 Host提供外部工具、数据源接入的统一协议你可以把它理解成 AI 世界的 USB-C 接口——以前各家接入方式乱七八糟现在协议统一了Server 做一份所有 Host 都能直接用。这篇进阶指南重点就放在四件事上错误处理、流式输出、TypeScript 类型组织和部署运维。如果你已经写通过一个简单的 MCP server想知道怎么把它升级成生产可用的东西这篇内容应该正好对得上。很多朋友分不清 MCP Host 和 MCP Server我这里用一句话说透Host 是运行环境也就是“谁来发起调用”Claude Desktop、Cursor、Trae、Codex 都属于 HostServer 是真正干活的服务它对外暴露工具tools、资源resources和提示词promptsHost 通过 MCP 协议动态发现、调用这些能力。第三方工具方只需要把 Server 实现好AI 端就能自动接上不需要为每个 AI 产品各写一套适配器。下面展开的内容我会带上具体代码、错误码设计思路和上线后的运维经验希望不是那种“看完了还是不会写”的泛泛教程。1. 动手之前MCP 自定义服务器到底在解决什么问题1.1 MCP 生态里大家都在做什么从近期搜索热度看这个领域的实际动作比很多人想象的要大得多。设计协同这边Figma MCP、蓝湖 MCP 成了高频词AI 能直接读取设计稿的图层、标注和 token开发工具这边Codex 联动 Burp MCP、Cursor 好用的 MCP、Spring Boot MCP 一批批冒出来连金融领域都有人在用通达信本地数据做 MCP 服务把行情、选股能力开放给 AI本地部署方向更是热闹Ollama 本地部署、DeepSeek 本地部署、Dify 本地部署这些词条跟 MCP 频繁绑定在一起。这些现象背后的共性是所有人都在试图把 AI 从“聊天机器人”变成“能操作你工作流里各种系统的 Agent”。设计稿不用再手动导出图片喂给 AI代码库不用再复制粘贴上下文行情数据不用再靠截图和 OCR。MCP Server 就是连接 AI 与这些系统的“万能插座”谁的 Server 做得好谁的 AI 工作流就能跑得更顺。搞清楚自己要服务的对象非常关键。如果你的 Server 只给本地模型用跑在 stdio 模式就足够如果要给远程团队共享那就必须走 HTTP SSE 的远程模式并且要额外处理鉴权、限流、日志收集。这两条路线的错误处理策略和部署运维方式差别非常大千万不要用一套方案去套所有场景。1.2 为什么我推荐用 TypeScript 写 MCP ServerMCP 官方给了 Python、TypeScript、Java 等多套 SDK但我在生产环境里更倾向于 TypeScript原因很实在。第一是生态对得齐。MCP Host 大多嵌在 IDE 或桌面应用里用 TypeScript 写的 Server 可以直接做成本地 CLI 工具通过 npx 启动同一套代码还能在 Node.js 服务里被同构调用几乎不用改。跨语言调用时Python 和 Node 进程之间的协议稳定性并没有想象中那么差但要处理依赖打包、运行环境隔离这些事情心智负担反而更大。第二是类型系统能帮你拦住一大半“工具 Schema 写错”的低级错误。服务端定义的工具输入参数和 AI 端拿到的 JSON Schema只要结构对不上调用时必然报参数校验失败。TypeScript 加运行时校验器比如 zod基本能在编译期和运行期双重拦截这个收益在工具数量超过十个以后尤其明显。第三是你大概率已经有 Node.js 环境调试工具链成熟和 IDE 集成度也高。Java 那条路线更适合做重型企业内部服务Python 适合快速原型但 TypeScript 在“兼顾规模、性能、快速迭代”上是平衡点最靠前的选择。本文的代码示例和踩坑记录都基于 TypeScript SDK不过错误处理、流式输出、部署这些结论其他语言同样适用。2. 错误处理把排查时间从一天压缩到十分钟MCP 使用的是 JSON-RPC 2.0 协议所有请求响应都是带 id 的 JSON 消息错误通过 error 字段返回。这个结构本身并不复杂但在实际开发里最折磨人的不是“协议错误”本身而是“业务错误”——你的工具调用了外部 API、查了数据库、跑了脚本结果失败了你该怎么把这个失败准确传达给 AI 端让 AI 能做出正确的应对。2.1 MCP 错误返回的结构与规范一条标准的 MCP 错误响应长这样{ jsonrpc: 2.0, id: 42, error: { code: -32602, message: Invalid params, data: { tool: search_docs, detail: 参数 q 不能为空且长度必须大于 1 } } }code 和 message 是 JSON-RPC 层面的data 是给调用方额外信息的扩展位。我习惯把 data 设计成一个结构化对象工具名、参数、内部错误原因、堆栈摘要都塞进去这样 AI 端拿到错误后能准确判断到底出了什么问题而不是看到一行模糊的“Internal error”之后反复在原地打转。关于 data 字段我吃过亏之后才明白一个道理千万别省也别只放一句话。因为 AI 端收到错误信息后很可能会基于这个消息自动调整参数重试。比如你告诉它“仓库 ID 不存在当前可用的 ID 包括 repo_a、repo_b”它下一步就能直接用 repo_a 去查你要是只回一句“查询失败”它就只能反复试差不多的参数白白浪费 token 和时间。2.2 错误码设计把工具错误和协议错误分开MCP 协议把 -32700 到 -32603 这个区间留给了标准 JSON-RPC 错误解析错误、无效请求、方法找不到、无效参数、内部错误这些是协议层的谁都不能改。但你的工具自己抛出来的错误应该用统一规划的错误码而且要提前设计不能随手写。我推荐的做法是-1000 ~ -1999 留给通用工具错误外部 API 超时、数据库连接失败、鉴权失败-2000 ~ -2999 留给业务参数错误-3000 留给特定领域的错误。每个错误码对应一个 message 模板data 里附上解决建议。在 SDK 里注册一个自定义错误类型export class McpToolError extends Error { constructor( public readonly code: number, message: string, public readonly detail?: unknown, public readonly suggestion?: string, options?: ErrorOptions ) { super(message, options); this.name McpToolError; } toMcpError() { return { code: this.code, message: this.message, data: { detail: this.detail, suggestion: this.suggestion, }, }; } }所有工具函数里统一抛出 McpToolError外层用一个拦截器统一捕获并转换成标准 MCP 错误响应。不要在每个工具里单独写 try/catch 和 response 组装很容易漏而且结构不统一AI 端解析时碰到格式漂移会非常难受。2.3 三个高频错误场景的处理套路第一个高频场景是鉴权失败。AI 端很可能拿着过期的 token 来调用你的工具这时候错误信息里要明确提示“token 已过期需要重新获取”如果你能判断出服务端返回的是 401/403还可以附上刷新指引。实测下来Figma MCP 这类场景里很多人卡在 token 获取这一步其实 Server 端如果把错误提示写得足够细比如“请在 Figma 设置里创建 Personal Access Token 并设置好权限范围”整个调试过程会顺很多。第二个是速率限制。你的 Server 可能对接了外部 API比如每分钟只能调用 5 次的内部接口这时必须返回带 429 语义的错误。MCP 协议没有强制要求这个东西但建议把冷却时间写进 data.suggestion比如“当前已超出调用次数限制请在 60 秒后重试”。AI 端非常擅长解析这种提示收到后会自动等待再重试比人类用户手动处理高效得多。第三个是外部依赖全挂了。比如你的工具依赖一个内部 Elasticsearch 集群集群连不上的时候如果直接把 ECONNREFUSED 堆栈抛给 AI 端毫无帮助。我一般做成“用户友好信息 内部错误码”的双层结构AI 端看到友好信息知道是依赖故障内部错误码让开发者能顺着日志找到根因。不要把所有内部细节暴露给端上不仅阅读性差还有信息泄露风险。2.4 日志与错误追踪别等用户来报 bug生产级 MCP Server 一定要有日志系统。我的做法是记录结构化日志不只是 console.log而是输出 JSON 行时间、请求 id、工具名、参数摘要、耗时、错误码、错误消息。这样后续不管接 ELK 还是简单的 grep都能快速定位。一个特别值得推广的技巧给每次请求生成一个 traceId放进 MCP 响应的 data 字段同时在日志里记录这个 traceId。用户或者 AI 端遇到问题回传错误信息时直接用 traceId 去日志里捞完整调用链路定位效率能提升一个量级。“错误信息里必须带 traceId”这件事我已经写进了团队的 code review checklist强烈建议你也这么做。3. 流式输出从“转菊花”到“实时刷屏”的实现流式输出这个词在多个场景里被反复提起尤其是搜“vue 聊天对话 AI 流式输出”、“标签返回未完整怎么处理”这些词的朋友大概率都在处理同一个问题模型回答太长一次性返回太慢用户端体验像在看菊花转圈。MCP Server 如果要做得好同样绕不开流式输出只是这里的“流”和传统 Web 接口的“流”有一些差别。3.1 MCP Tool 能否做到逐 Token 输出先说结论MCP 的 tools 调用本身是请求-响应模式Server 端返回一个完整结果并没有内置“逐 token 推送”的机制。但为什么很多 MCP Server 可以实现流式效果靠的是两个机制一是工具返回的文本内容可以是一个可迭代的流SDK 层会把它分块填充到响应里二是 MCP 支持进度通知progress notificationServer 可以在长任务执行过程中主动推送进度告诉 Host“我做到 50% 了”。实际体验是对于生成类任务让 AI 读长文本、生成报告我一般让工具直接返回完整内容但内容如果特别长就在服务端做分块转出避免一次性大响应对于耗时操作查一堆数据、跑批处理用进度通知的方式让用户看到执行状态反馈感比干等强很多。实现层面用 ReadableStream 来做流式响应是一个可行思路。不过这里必须提醒一句MCP SDK 不同版本对流的支持程度差别很大截止到目前稳定版本文本内容里的流式支持还在快速迭代中你需要确认自己用的 SDK 版本里 stream 相关字段是否可用Host 端是否支持解析。很多坑都出在“Server 端塞了流但 Host 端根本不读”所以上线前一定先用 MCP Inspector 或类似调试工具跑一遍长响应测试。3.2 长任务进度通知让用户不再以为卡死了如果你的工具要跑一个长时间操作比如批量生成图片、抓取整站数据最怕的是发起之后用户干等屏幕上一点反馈都没有。MCP 协议的 notifications/progress 可以解决这个问题Server 在执行过程中向 Host 推送进度。在 TypeScript SDK 里大概是这样触发的await client.notification({ method: notifications/progress, params: { progressToken: request.params._meta?.progressToken, progress: 50, total: 100, message: 正在抓取第 5/10 页, }, });progressToken 需要从请求的 _meta 里透传过来。Host 如果没有设置 _meta说明它不关心进度你发通知它也不会理你。所以实现逻辑上只有当你拿到了 progressToken才去构造并推送进度通知没拿到就直接安静地跑完返回结果不要为了演示效果强行发容易造成协议不兼容问题。进度通知的设计原则是低频、有价值。每 1% 推一次没有意义容易把通信链路刷爆。我一般按阶段推每完成一个子任务推送一次最多不超过 20 次message 里带上当前阶段正在做什么让用户有“能看得到进展”的实感。3.3 流式中断、心跳与资源回收流式输出最头疼的问题不是“怎么发”而是“发到一半断掉了怎么办”。AI 端可能在读完部分结果后就关闭连接或者用户不耐烦直接取消。你的 Server 要能感知到断流并中断底层任务否则任务还在后台加载资源、白白消耗算力。在 ReadableStream 实现里可以监听 cancel 事件触发底层的 AbortController 取消计划任务const controller new AbortController(); const stream new ReadableStream({ start(streamController) { // 开始产出数据块 }, cancel(reason) { // 连接被对端关闭取消正在执行的后台任务 controller.abort(reason); }, });还有一类场景是流式输出长时间没有数据产出Host 端可能判断超时。如果是生成型任务建议加一个保活机制比如每 15 秒在流里写入一个空白字符或注释字符告诉对端“我还活着还在算”。这个细节在对接 SSE、WebSocket 等长连接传输时尤其重要很多“调用超时”的诡异问题归根结底就是缺少心跳。3.4 流式输出的六大注意事项处理流式输出我踩过六个坑基本是新手必踩的。第一个坑是不知道 Host 端可能不支持流。所以你要有降级方案检测对端声明能力不支持时直接走一次性返回值不能硬上流式接口。第二个坑是流式结果和普通结果混用。同一个工具有时返回流、有时返回普通文本导致 Host 端解析逻辑写得很痛苦。建议一个工具固定一种返回模式要么总是流式要么总是普通文本。第三个坑是字符串编码问题。流式输出长文本时如果手动切分中文字符经常把一个 UTF-8 字符截成两半对端拿到乱码。正确做法是用 TextEncoder/buffer 做字符边界感知的切割不要简单按字节数 slice。第四个坑是忘了设置背压backpressure。Server 端拼命 push 数据对端消费不过来内存直接起飞在 SSE 或 WebSocket 场景下尤其明显。实现时要关注对端的消费速度必要时暂停生产、等待消费。第五个坑是取消逻辑没做清理。用户取消后后台任务还在跑数据库连接、外部 API 调用一个没断积少成多会把服务拖垮。最后一个坑是调试不便。建议用 MCP Inspector 或自己写一个最小的 Host 脚本把流式响应完整读一遍验证保活、取消、分块行为都符合预期再交付。不能只在浏览器里看一眼“好像能出字”就完事。4. TypeScript 开发要点类型一时爽调试火葬场TypeScript 用好了能省一大半排查时间用不好也是给自己埋雷。MCP Server 的 TS 开发里最大的收益来自给工具定义精确的类型最大的坑也来自类型和运行时的不一致。4.1 工具 Schema 的类型推断一次定义处处复用给每个自定义工具写一个“工具名 输入 Schema 处理函数”的类型关联让 TypeScript 能自动推断出 Schema 的结构。官方 SDK 里可以用辅助注册器把 Schema 定义和处理函数绑定这样你在修改 Schema 时编译器会提醒你同步修改处理函数。举个例子const searchDocsTool { name: search_docs, description: 搜索内部文档库支持关键词和标签过滤。, inputSchema: { type: object, properties: { query: { type: string, description: 检索关键词 }, tags: { type: array, items: { type: string }, description: 标签列表可空 }, limit: { type: number, default: 5, minimum: 1, maximum: 20 }, }, required: [query], } as const, async handler(args: { query: string; tags?: string[]; limit?: number }) { // 具体实现 }, };这段代码的关键是 as const 产生的字面量类型能帮你校验 Schema 和 handler 的参数结构是否一致。我之前手写 Schema 时把 required 写成了 require结果 AI 端始终无法通过参数校验查了整整半天。后来加上类型约束和运行时校验器才彻底解决这类问题。4.2 tsconfig 关键配置严格模式之外还要注意什么MCP Server 工程里我的 tsconfig 一般这么设{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, strict: true, declaration: true, sourceMap: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, outDir: ./dist, rootDir: ./src }, include: [src/**/*], exclude: [node_modules, dist, test] }strict 必须开这是底线moduleResolution 用 NodeNext 可以让 import 语法在 Node ESM 下保持一致sourceMap 在生产排查里很有用线上报错堆栈能直接映射回 TS 源码。skipLibCheck 建议开着否则你依赖的第三方包类型有瑕疵时会很痛苦如果对类型安全要求极高可以不开但要接受更多检查噪音。最近很多人问到一个和热词里“选项 baseurl 已弃用”类似的问题TypeScript 新版本对旧配置的弃用警告。技术上的解法很简单把 tsconfig 里废弃的老字段清理掉尤其是那些 TS 5.x 时代常见但计划在未来版本移除的配置同时把 typescript 依赖版本固定避免依赖解析器拉到一个和你预期不一致的大版本。很多“接口对接不上”的诡异问题到头来不是 MCP 协议的问题而是 TS 版本不一致导致生成的类型定义和行为漂移这个坑特别隐蔽。4.3 类型兼容问题为什么“深度方得始终”开发 MCP Server 时还会遇到类型不兼容的经典场景比如你创建好了工具类型结果传给一个要求 Mapstring, Tool 的注册器TypeScript 报类型不匹配。原因通常是被推断出来的工具类型是联合类型而注册器要求的是“结构化类型必须精确匹配”。这时候有两种解法一种是自己定义统一的 Tool 接口让每个工具对象显式满足 Tool 类型另一种是给注册器加一个类型参数让它自动收窄联合类型。我更推荐第一种。显式标注工具类型虽然啰嗦但能让所有工具在编译期就对齐接口。热词里那个“typescript [{}]”和“vue 类型工具与现有 typescript 7 不兼容”的搜索本质上都是类型版本或者联合类型推断引发的问题。处理原则是类型问题不要靠 any 糊弄该收敛的收敛该用 satisfies 操作符做局部校验的也要用起来从源头上消除类型漂移。5. 部署实践从本机跑通到服务化运行写好了 Server真正的考验才刚开始。MCP Server 部署分成两大流派stdio 模式和远程模式。很多人一上来就想部署到公网但很多时候其实只需要本地 stdio 模式就够了如果服务要被团队共享、被远程调用那才需要考虑 HTTP/SSE 或者改造后的流式传输模式。5.1 stdio 模式最简单也最容易被忽略的问题stdio 模式就是 Host 直接启动你的 Server 进程通过标准输入输出通信。好处是没有网络层问题调试方便本地模型比如 Ollama、DeepSeek 本地部署配这种模式最顺。但隐患也很明显Server 进程的生命周期完全由 Host 管理如果你的 Server 在退出时没有正确清理资源数据库连接、临时文件、子进程长期反复启动关闭系统资源会被慢慢吃光。我之前遇到过一个案例一个 Server 每次调用都会拉起一个子进程去做 OCR退出的子进程没有被 wait系统僵尸进程越堆越多最后整个开发机都卡了。所以 stdio 模式下的 Server 一定要实现优雅退出监听 SIGINT/SIGTERM把正在执行的任务及时终止手动关闭连接池再退出进程。还有一个新手最容易犯的错stdio 模式下 stdout 是协议通道你如果 console.log 输出调试信息会把协议流污染导致 Host 解析失败。正确做法是全部用 stderr 或专门的文件日志输出去写调试信息。这条我几乎每次指导新人都要强调一遍因为问题实在太常见了。5.2 远程模式鉴权、限流、安全一个都不能少当 Server 要跑在服务器上供多个人调用时你需要从 stdio 切换到 HTTP SSE 模式把 MCP Server 做成一个 Web 服务。这一步的复杂度不止是加一层 HTTP 而已跨域、鉴权、限流、TLS、日志、健康检查、滚动更新一整套运维需求全都要考虑。一个比较省心的方案是 Docker 化部署。Dockerfile 核心内容我一般写成FROM node:20-slim AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY tsconfig.json ./ COPY src ./src RUN npm run build FROM node:20-slim AS runtime WORKDIR /app ENV NODE_ENVproduction COPY --frombuild /app/dist ./dist COPY --frombuild /app/package*.json ./ RUN npm ci --omitdev npm cache clean --force EXPOSE 3000 CMD [node, dist/index.js]多阶段构建的好处很直接最终镜像不包含源码和 devDependencies体积小很多攻击面也小。运行阶段容器里只保留编译产物和必要依赖启动速度、安全性和可维护性都会更好。部署之前把环境变量设计成可配置的比如 PORT、外部 API 地址、日志级别、鉴权 token。不要把这些值硬编码进源码尤其是 token泄露一次就要全部轮换代价非常大。热词里“Figma MCP token 在哪获取”这类问题说明很多人在 MCP Server 的密钥管理上踩过坑。密钥的正确姿势是放到宿主机的环境变量、密钥管理服务或容器的 secret 机制里不要在代码和配置文件里出现任何明文。远程模式下鉴权方案建议至少用 Bearer Token 或更严格的 OAuth2 / API Key 体系千万不要裸奔。5.3 部署后的可观测性配置Server 上线后不能变成黑盒。我建议从第一天就接入基础监控体系进程级监控CPU、内存、事件循环延迟、请求级监控QPS、错误率、P95 耗时、依赖监控外部 API 失败率、数据库连接数。这不一定要上重型 APM一个简单的 Prometheus exporter 加 Grafana 面板就能覆盖大部分需求。如果 Server 部署在云服务器上强烈建议配置进程管理器比如 systemd 或 pm2保证进程崩溃后能自动重启。同时配置日志轮转避免长时间运行后日志文件把磁盘打满。这些在本地跑 Server 时完全无感但一旦服务化运行每一样都可能成为线上事故的原因。部署这件事没有太多浪漫可言就是把这些基础工作一件件做扎实。6. 问题排查与调试实录给未来的自己写一份避坑手册写进阶指南不把调试经验写透等于白写。我把这个项目从开发到上线过程中最常遇到的问题整理成一张速查表希望帮你少走弯路。6.1 常见问题速查表现象可能原因解决思路Host 启动 Server 后立即报错退出stdout 被调试日志污染所有业务日志改走 stderr确保 stdout 只有协议消息AI 端报“工具参数校验失败”Schema 的 required 写错或类型不匹配类型约束 运行时校验器比如 zod调用工具等待很久后超时事件循环被阻塞或者没有心跳长任务丢到后台队列同步返回进度 token加保活工具返回内容被截断流式切分字符导致 UTF-8 断裂或 Host 不支持大响应用 TextEncoder/buffer 做边界感知切割降级为完整文本进度通知发不出去没透传 progressToken或 Host 不支持 progress检查 _meta 里的 progressToken拿到才推送部署到服务器后 localhost 成功、外部访问失败监听 127.0.0.1 而不是 0.0.0.0检查监听地址和云服务器安全组规则MCP 版本不同导致协议消息不识别SDK 版本差异统一 SDK 版本锁定 package-lock.json升级前跑回归内存持续增长流式输出没有背压、或取消任务没有清理加背压控制取消时执行资源回收这张表里的问题都是我实际踩过的列出来时仍然感觉每一件都带着当时的画面感。比如有人跟我说“我的 Server 注册成功了但 Host 一调用就崩”我的第一反应一定是问“你是不是用了 console.log 打印东西”十有八九命中。6.2 调试工具链MCP Inspector 和自诊断脚本MCP Inspector 是官方的调试利器可以在浏览器里配置 Server 路径然后手动调用工具、查看请求响应详情。它的价值在于你可以绕开 AI Host 的黑箱逻辑看到完整的 JSON-RPC 报文从而判断错误到底出在 Server 端还是 Host 端。这种“先定位层再定位模块”的思路是排查一切 MCP 问题的基础。另外一个很实用的做法是写一个最小的自诊断脚本直接实例化自己的 Server模拟一次完整调用先 listTools再 callTool再处理返回结果。这样在提交代码前就能快速回归核心链路尤其在改动了错误处理或流式输出逻辑之后自诊断脚本能帮你省下一堆不必要的联调时间。6.3 我的三个独家避坑经验最后分享三个我很少在文档里看到的经验。第一个是版本的快照意识。MCP SDK、TypeScript、Node.js 这几个依赖的版本一落后就可能出现隐性协议行为差异。项目里一定要固定版本并定期升级回归不要把升级拖到不可控的时候。之前踩过一个大坑某个版本 SDK 改了进度通知的消息格式升级后旧 Host 直接静默忽略排查了很久才发现是版本兼容问题。第二个是“AI 端拿到错误信息后真的会自我修复”。所以错误信息里的 suggestion 字段不是写给用户看的是写给 Agent 看的。你写“请提供正确的 API Key”这句话时AI 端下次很可能会自动引导用户去配置 Key。好的 suggestion 信息能把整个 Agent 的容错能力提高一个档次这是很多团队完全没利用到的能力。第三个是“先想清楚故障边界再写代码”。MCP Server 毕竟是外部工具的中间层你把输入输出的边界定义得越清晰AI 端发挥的空间就越大反过来如果所有能力都揉在一个大工具里错误处理、日志追踪、参数校验都会变得极其痛苦。我后来把所有 Server 都改成了“小工具大组合”的模式维护成本直线下降。写 MCP Server 这两年最大的感受是它并没有那么神秘本质就是一套被 AI 消费的 API 设计规范只是设计对象从“人类前端”变成了“AI Agent”。真正决定一个 Server 好不好用的不是框架选得多新而是错误信息是否精准、流式输出是否顺滑、类型是否严谨、部署后是否可观测。把这四件事想透了你的 MCP Server 就能从“能跑”走到“好用”。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门