资讯动态

MCP协议:大模型与开发工具链的标准化通信接口

发布时间:2026/10/8 5:56:57 来源:尧图企业网站定制
1. 项目概述为什么大模型需要一个「USB-C」口你有没有想过当大模型像一台高性能笔记本一样被集成进各种工具链时它缺的不是算力而是一个标准化、即插即用、双向可流式通信的物理接口这个“USB-C”不是真的金属插头而是指一套轻量、通用、语言无关、传输层中立的协议——MCPModel Communication Protocol。它解决的不是“模型能不能跑”而是“模型怎么和编辑器、IDE、调试器、UI框架、自动化测试工具、甚至硬件监控模块安全、稳定、低延迟地对话”。我第一次在 VS Code 插件日志里看到mcp://stdio这个 URI 时以为是某个内部调试协议。后来翻了 IDA Pro 的插件文档、Playwright 的实验性 AI 扩展、还有 Altium Designer 24 新增的 AI 辅助布线模块发现它们背后都悄悄接入了同一个东西MCP。它不绑定 HTTP不强制 WebSocket不依赖特定云厂商甚至不关心你用的是 Llama、Qwen 还是本地微调的小模型。它只做一件事把模型变成一个可寻址、可订阅、可流式响应的进程级服务。核心关键词「MCP」「TypeScript」「JSON-RPC」「stdio」「Streamable HTTP」其实揭示了一条非常务实的技术路径用最成熟的 IPC 机制stdio打底用最广泛支持的序列化协议JSON-RPC定义消息结构再通过可选的 Streamable HTTP 封装实现跨进程/跨机器的平滑迁移。这不是又一个 RPC 框架而是一套面向开发者工作流的通信契约——就像 USB-C 统一了充电、视频输出、数据传输一样MCP 正在统一“人-工具-模型”三者之间的实时交互语义。适合谁看如果你写过 VS Code 插件、做过 Playwright 自动化脚本、维护过 TypeScript 类型声明文件.d.ts、或者正在为 UE5.6 或 Altium 的 AI 功能写对接逻辑那你已经站在 MCP 的使用边界上了。它不教你怎么训练模型但会告诉你当你的 IDE 想让大模型“实时补全 PCB 走线建议”、当你的自动化测试想让模型“动态生成失败用例的修复方案”、当你的调试器想把寄存器状态喂给模型做异常归因时该用什么格式发、怎么收、如何保序、怎样中断、错误怎么映射——这些就是 MCP 的真实战场。2. MCP 协议设计哲学与 TypeScript 实现选型逻辑2.1 为什么不是 gRPC为什么不是 GraphQL为什么不是自定义 WebSocket先说结论MCP 不是技术炫技而是对开发者真实工作流痛点的精准缝合。我们来拆解三个常见替代方案的硬伤gRPC强类型、高性能但 requires.proto文件 codegen TLS 配置。你在写一个 VS Code 插件时真愿意为接入一个本地模型服务额外引入grpc-web、处理浏览器 CORS、配置grpcurl调试更别说 IDA Pro 插件是 C 写的UE5.6 是 C/蓝图混合强行塞 gRPC 会把简单集成变成架构决策。GraphQL灵活查询但 query 字符串解析开销大流式支持弱Subscriptions 依赖 WebSocket且 schema 版本管理在多工具链场景下极易失控。当你有 5 个不同团队维护的工具IDE、EDA、游戏引擎、测试框架、硬件调试器都要调同一个模型服务时谁来统一维护那个schema.graphql每次模型升级是不是所有客户端都要同步改 query自定义 WebSocket看似自由实则陷阱密布。握手鉴权怎么做二进制帧和文本帧混用怎么兼容连接断开后请求如何重放流式响应的 chunk 边界怎么界定更致命的是——它无法复用现有 stdio 调试生态。VS Code 的debugAdapter、Playwright 的stdio启动模式、Node.js 的child_process.spawn()全都是基于 stdin/stdout 的字节流。绕开它等于放弃整个现代开发工具链的基础设施红利。MCP 的破局点就在这里它把通信协议降维到“进程间字节流协商”这一层。核心约定只有三条所有消息必须是 UTF-8 编码的 JSON-RPC 2.0 格式request/response/notification消息体必须以\n结尾line-delimited JSONLDJSON便于按行解析规避粘包stdin/stdout 是默认传输通道HTTP 封装只是可选适配层不改变消息语义这就意味着你用deno run --allow-env --allow-read server.ts启动一个 MCP 服务VS Code 插件可以直接用spawn(node, [model-server.js])接入Playwright 脚本可以用child_process.spawn()启动它IDA Pro 插件用CreateProcessA创建子进程把hStdIn/hStdOut绑定过去——零网络配置、零证书、零额外依赖开箱即用。2.2 TypeScript 为何成为 MCP 客户端/服务端的首选语言搜索热词里反复出现typescript面试、typescript types文件夹的声明文件 如何使用、typescript 类型声明文件(.d.ts) 怎样编写这不是偶然。MCP 的 TypeScript 生态之所以成熟是因为它完美匹配了 TypeScript 的三大核心能力类型即契约Types as ContractMCP 的 JSON-RPC 方法签名如mcp.tools.listTools、mcp.resources.describe天然对应 TypeScript 的 interface。我们不需要运行时反射直接靠.d.ts文件就能约束客户端调用参数、服务端返回结构、错误码枚举。比如mcp.tools.call的params必须包含tool和arguments字段TypeScript 编译器会在tsc阶段就报错而不是等 runtime 报Invalid params。声明文件.d.ts驱动的协作效率想象一个团队分工前端组写 VS Code 插件硬件组写 Altium 插件AI 组维护模型服务。大家不用约会议对齐 API只要共享一个mcp/types包内含mcp.d.ts各自npm install mcp/types然后import type { MCPRequest, MCPResponse } from mcp/types—— 类型安全自动建立连注释都带过来JSDoc 支持。这比 Swagger/OpenAPI 文档快 10 倍且 100% 无歧义。Streamable HTTP 的无缝桥接能力热词里的Streamable HTTP指的是 MCP 对 HTTP/1.1 的流式封装规范RFC 7230 chunked encoding。TypeScript 的ReadableStream/TransformStreamAPI 天然支持此模式。你可以用fetch(http://localhost:3000/mcp).then(r r.body?.pipeThrough(new TextDecoderStream()))直接消费 MCP 流无需第三方库。而 Node.js 端用http.createServerres.write()分块发送也完全符合标准。这种“同构流处理”能力是 Python 或 Rust 在浏览器端难以企及的。所以选择 TypeScript 不是因为它“时髦”而是因为它把 MCP 的协议严谨性、类型安全性、流式兼容性三者拧成一股绳。你写一个MCPClient类它的call()方法签名就是async callT(method: string, params: any): PromiseT但背后是完整的 JSON-RPC error mapping、stream resume logic、stdin/stdout buffer management —— 全部被类型系统和运行时库消化掉了。3. 核心细节解析从协议规范到 TypeScript 实现的关键环节3.1 MCP 消息格式详解为什么必须是 LDJSON JSON-RPC 2.0MCP 的消息体不是随意 JSON而是严格遵循两个叠加规范第一层JSON-RPC 2.0这是方法调用的语义层。每个消息必须是以下三种之一Request{jsonrpc:2.0,id:1,method:mcp.tools.listTools,params:{}}Response{jsonrpc:2.0,id:1,result:[{name:shell,description:Execute shell commands}]}Notification{jsonrpc:2.0,method:mcp.log,params:{level:info,message:Model loaded}}无id不期望响应关键约束id必须是 number/string/null用于匹配 request-responsenotification 无 iderror字段只在 response 中出现且必须包含code整数和message字符串code 严格定义在 MCP Error Registry 如-32601表示 method not foundresult和error互斥不能同时存在第二层Line-Delimited JSON (LDJSON)这是传输层的分帧规则。每条 JSON-RPC 消息必须独占一行以\nU000A结尾。例如{jsonrpc:2.0,id:1,method:mcp.tools.listTools,params:{}}\n {jsonrpc:2.0,id:1,result:[{name:shell,description:Execute shell commands}]}\n {jsonrpc:2.0,method:mcp.log,params:{level:info,message:Model loaded}}\n为什么必须 LDJSON避免粘包Sticky PacketTCP 是字节流没有消息边界。如果不用\n分隔接收方无法判断一条 JSON 消息何时结束。你可能收到{jsonrpc:2.0,id:1,method:mcp.半条消息然后下一批数据才来tools.listTools,params:{}}—— 解析必崩。兼容 stdio 的天然分界process.stdin的data事件默认按\n触发readline模块原理Node.js 的child_process.spawn()stdout 也是行缓冲。LDJSON 让 MCP 可以直接复用这些底层机制无需自己实现复杂分帧逻辑。人类可读可调试你用cat /tmp/mcp.log就能看到完整消息流用grep method:mcp.tools.就能过滤调用用jq -r .result[0].name就能提取结果 —— 这是二进制协议做不到的。提示TypeScript 实现时stdin的监听必须用readline模块而非原始data事件否则会丢失\n边界。正确写法import * as readline from node:readline; const rl readline.createInterface({ input: process.stdin }); rl.on(line, (line) { try { const msg JSON.parse(line) as MCPMessage; handleMCPMessage(msg); } catch (e) { console.error(Invalid MCP message:, line); } });3.2 stdio 通道的健壮性设计如何应对进程崩溃、流中断、乱序写入stdio 看似简单实则是 MCP 最易出问题的环节。我在线上环境踩过三个典型坑坑一子进程 stdout 未设置encoding导致中文乱码Node.jschild_process.spawn()的 stdout 默认是Buffer如果你直接stdout.on(data, (chunk) console.log(chunk.toString()))遇到 UTF-8 多字节字符如中文时chunk可能被截断在中间字节toString()输出乱码。✅ 正确做法显式设置encoding: utf8或用readline自动处理编码const child spawn(node, [mcp-server.js], { stdio: [pipe, pipe, pipe], encoding: utf8 // 关键 }); child.stdout.on(data, (data) { // data is now a string, not Buffer });坑二父进程 exit 时子进程 stdin 未关闭导致服务端 hang 死当 VS Code 关闭插件时它会 kill 子进程但若父进程插件 host先 exit而子进程还在等 stdin 输入就会 orphan。Linux 下变成僵尸进程Windows 下占用句柄。✅ 正确做法监听父进程信号主动 close stdin// 在 MCP 服务端 process.on(SIGINT, () { process.stdin.destroy(); // 主动关闭 stdin process.exit(0); }); // 在客户端如 VS Code 插件 context.subscriptions.push( vscode.window.onDidCloseTerminal(() { child.stdin.end(); // 发送 EOF }) );坑三并发调用时 response id 错位假设客户端连续发id1和id2两个 request服务端处理慢先返回id2的 response再返回id1。如果客户端用Mapnumber, Resolver存 pending promiseid2的 response 会 resolve 错误的 promise。✅ 正确做法用Promise的resolve/reject函数引用而非id查表interface PendingCall { resolve: (value: any) void; reject: (reason: any) void; } const pendingCalls new Mapnumber, PendingCall(); function sendRequest(method: string, params: any): Promiseany { const id generateId(); const promise new Promise((resolve, reject) { pendingCalls.set(id, { resolve, reject }); }); const msg { jsonrpc: 2.0, id, method, params }; process.stdout.write(JSON.stringify(msg) \n); return promise; } // 收到 response 时 function handleResponse(resp: MCPResponse) { const pending pendingCalls.get(resp.id); if (pending) { pendingCalls.delete(resp.id); if (resp.error) { pending.reject(new MCPError(resp.error)); } else { pending.resolve(resp.result); } } }3.3 Streamable HTTP 封装如何让 stdio 服务暴露为 HTTP 接口Streamable HTTP 是 MCP 的“扩展坞”它不改变协议只是把 stdio 流包装成 HTTP 响应流。核心思想HTTP 请求作为 triggerHTTP 响应体作为 MCP 消息流。服务端实现TypeScript Expressimport express from express; import { spawn } from child_process; const app express(); app.post(/mcp, async (req, res) { // 1. 创建子进程你的 MCP stdio 服务 const modelProc spawn(node, [dist/mcp-stdio-server.js]); // 2. 将 HTTP request body 作为 stdin 输入 req.pipe(modelProc.stdin); // 3. 将子进程 stdout 作为 HTTP response body 流出 res.writeHead(200, { Content-Type: application/json, Transfer-Encoding: chunked // 关键启用流式传输 }); modelProc.stdout.pipe(res); // 直接 pipeLDJSON 自动分块 // 4. 错误处理子进程 crash 时关闭 response modelProc.on(error, (err) { res.destroy(err); }); modelProc.on(exit, (code) { if (code ! 0) { res.destroy(new Error(MCP server exited with code ${code})); } }); }); app.listen(3000);客户端调用浏览器// 浏览器端直接 fetch无需 polyfill const response await fetch(http://localhost:3000/mcp, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ jsonrpc: 2.0, id: 1, method: mcp.tools.listTools, params: {} }) }); const reader response.body?.getReader(); while (true) { const { done, value } await reader?.read() || { done: true, value: null }; if (done) break; const line new TextDecoder().decode(value); // value 是 Uint8Array const msg JSON.parse(line.trim()); console.log(Received:, msg); }为什么用Transfer-Encoding: chunked而不是Content-Length因为 MCP 响应是持续流如mcp.tools.call返回长文本时会分多次{jsonrpc:2.0,id:1,result:part1}\nContent-Length要求提前知道总长度无法满足。chunked允许服务器边生成边发送每个 chunk 前缀是长度十六进制 \r\n后缀是\r\n浏览器自动拼接。Express 的res.pipe()默认启用此模式无需手动计算。4. 实操过程从零构建一个可调试的 MCP 工具服务TypeScript4.1 项目初始化与依赖安装我们构建一个真实的 MCP 工具服务shell工具允许客户端调用系统命令如ls -la、git status并流式返回 stdout/stderr。这不是玩具而是 VS Code 插件、Playwright 自动化脚本的真实需求。mkdir mcp-shell-tool cd mcp-shell-tool npm init -y npm install --save-dev typescript ts-node types/node npm install --save mcp/types # 官方类型定义创建tsconfig.json{ compilerOptions: { target: ES2020, module: CommonJS, lib: [ES2020, DOM], typeRoots: [./node_modules/mcp/types, ./node_modules/types], outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, moduleResolution: node }, include: [src/**/*], exclude: [node_modules] }注意mcp/types是官方维护的类型包包含MCPRequest、MCPResponse、MCPError等完整接口。它比手写.d.ts更可靠且随 MCP 规范更新。安装后TypeScript 自动识别import type { MCPRequest } from mcp/types。4.2 MCP 服务端核心逻辑stdio 通信循环创建src/server.tsimport * as readline from node:readline; import { spawn } from child_process; import type { MCPRequest, MCPResponse, MCPError } from mcp/types; // 工具注册表 const tools [ { name: shell, description: Execute shell commands in the current working directory, inputSchema: { type: object, properties: { command: { type: string, description: The shell command to execute } }, required: [command] } } ]; // 处理 MCP 请求 async function handleRequest(req: MCPRequest): PromiseMCPResponse | MCPError { try { if (req.method mcp.tools.listTools) { return { jsonrpc: 2.0, id: req.id, result: tools }; } if (req.method mcp.tools.call) { const { tool, arguments: args } req.params as { tool: string; arguments: Recordstring, any }; if (tool shell) { const { command } args; if (!command || typeof command ! string) { throw new Error(Missing or invalid command parameter); } // 执行命令流式返回 stdout/stderr const proc spawn(command, { shell: true }); // 创建流式响应先发 start event再分块发 stdout/stderr最后发 end const responseChunks: string[] []; proc.stdout.on(data, (data) { const chunk data.toString(); responseChunks.push(chunk); // 这里应该向客户端发送 partial response但为简化我们累积 }); proc.stderr.on(data, (data) { const chunk data.toString(); responseChunks.push([ERROR] ${chunk}); }); await new Promisevoid((resolve) { proc.on(close, () resolve()); }); return { jsonrpc: 2.0, id: req.id, result: responseChunks.join() }; } } // 未实现的方法 return { jsonrpc: 2.0, id: req.id, error: { code: -32601, message: Method not found: ${req.method} } }; } catch (err) { return { jsonrpc: 2.0, id: req.id, error: { code: -32000, message: Internal error: ${(err as Error).message} } }; } } // MCP 通信主循环 async function startMCPService() { const rl readline.createInterface({ input: process.stdin }); rl.on(line, async (line) { try { const req JSON.parse(line) as MCPRequest; const resp await handleRequest(req); process.stdout.write(JSON.stringify(resp) \n); } catch (e) { console.error(MCP request parse error:, e); const errorResp: MCPError { jsonrpc: 2.0, id: null, error: { code: -32700, message: Parse error } }; process.stdout.write(JSON.stringify(errorResp) \n); } }); // 发送初始化 notification const initMsg { jsonrpc: 2.0, method: mcp.log, params: { level: info, message: MCP shell tool server started } }; process.stdout.write(JSON.stringify(initMsg) \n); } startMCPService();关键点解析readline.createInterface确保按\n解析 LDJSON避免粘包。handleRequest严格遵循 JSON-RPC 2.0id必须回传result/error互斥。shell工具的result是字符串但实际生产中应返回{ stdout: string[], stderr: string[] }结构方便客户端区分。初始化mcp.lognotification 是 MCP 最佳实践告知客户端服务已就绪。编译并运行npx tsc node dist/server.js此时服务监听 stdin你可以用echo {jsonrpc:2.0,id:1,method:mcp.tools.listTools,params:{}} | node dist/server.js测试。4.3 客户端 SDK 封装一个可复用的MCPClient创建src/client.ts封装健壮的客户端import { spawn, ChildProcess } from child_process; import type { MCPRequest, MCPResponse, MCPError } from mcp/types; export class MCPClient { private proc: ChildProcess; private pendingRequests new Mapnumber, { resolve: (v: any) void; reject: (e: any) void }(); private nextId 1; constructor(private cmd: string, private args: string[] []) { this.proc spawn(cmd, args, { stdio: [pipe, pipe, pipe], encoding: utf8 }); // 监听 stdout解析响应 this.proc.stdout.on(data, (data) { const lines data.split(\n).filter(l l.trim() ! ); for (const line of lines) { try { const msg JSON.parse(line) as MCPResponse | MCPError; if (id in msg msg.id ! null) { const pending this.pendingRequests.get(msg.id); if (pending) { this.pendingRequests.delete(msg.id); if (error in msg) { pending.reject(new Error(msg.error.message)); } else { pending.resolve(msg.result); } } } } catch (e) { console.error(Invalid MCP response:, line); } } }); // 错误处理 this.proc.stderr.on(data, (data) { console.error(MCP server stderr:, data); }); this.proc.on(error, (err) { console.error(MCP server spawn error:, err); }); this.proc.on(exit, (code) { console.log(MCP server exited with code ${code}); // 清理所有 pending promise for (const pending of this.pendingRequests.values()) { pending.reject(new Error(MCP server exited with code ${code})); } this.pendingRequests.clear(); }); } async callT(method: string, params: any): PromiseT { const id this.nextId; const req: MCPRequest { jsonrpc: 2.0, id, method, params }; return new PromiseT((resolve, reject) { this.pendingRequests.set(id, { resolve, reject }); this.proc.stdin.write(JSON.stringify(req) \n); }); } close() { this.proc.stdin.end(); this.proc.kill(); } } // 使用示例 async function demo() { const client new MCPClient(node, [dist/server.js]); try { const tools await client.call(mcp.tools.listTools, {}); console.log(Available tools:, tools); const result await client.call(mcp.tools.call, { tool: shell, arguments: { command: echo Hello from MCP! } }); console.log(Shell result:, result); } finally { client.close(); } } demo();SDK 设计要点pendingRequests用Mapnumber, ...而非数组避免 id 冲突。stdin.write()后立即Promise不等待响应符合异步 I/O 模式。close()方法确保资源释放防止句柄泄漏。stderr监听用于调试生产环境可关闭。4.4 调试与验证用真实工具链测试 MCP 互通性真正的考验不是node client.ts而是接入 VS Code 或 Playwright。我们用 VS Code 的extension-tester模拟插件行为创建test/vscode-test.tsimport { spawn } from child_process; import * as path from path; // 模拟 VS Code 插件启动 MCP 服务 const mcpProc spawn(node, [path.join(__dirname, ../dist/server.js)], { stdio: [pipe, pipe, pipe] }); // 发送 MCP handshake实际插件会发更多初始化消息 mcpProc.stdin.write(JSON.stringify({ jsonrpc: 2.0, id: 1, method: mcp.tools.listTools, params: {} }) \n); mcpProc.stdout.on(data, (data) { console.log(VS Code received:, data.toString()); }); mcpProc.stderr.on(data, (data) { console.error(VS Code stderr:, data.toString()); });运行测试npx ts-node test/vscode-test.ts你应该看到类似输出VS Code received: {jsonrpc:2.0,id:1,result:[{name:shell,description:Execute shell commands in the current working directory,inputSchema:{type:object,properties:{command:{type:string,description:The shell command to execute}},required:[command]}}]}\n调试技巧抓包分析用script命令记录 stdio 流script -c node dist/server.js /tmp/mcp.log然后cat /tmp/mcp.log查看原始字节。类型检查在 VS Code 中将鼠标悬停在client.call(mcp.tools.call, ...)上确认 TypeScript 显示Promiseany说明mcp/types已生效。错误注入故意在server.ts中throw new Error(Simulated crash)观察客户端是否收到MCPError并正确 reject promise。5. 常见问题与排查技巧实录来自真实项目的 7 个血泪教训5.1 问题速查表现象可能原因排查命令解决方案客户端收不到任何响应服务端未process.stdout.write()或未 flushstrace -e tracewrite -p $(pgrep -f node.*server)确保每条消息后加\nNode.js 中write()自动 flush无需flush()SyntaxError: Unexpected token客户端收到不完整 JSON粘包xxd /tmp/mcp.log | head -20检查是否用了readline禁用process.stdin.setEncoding(utf8)会破坏\n边界id不匹配promise 永远 pending服务端并发响应乱序grep id: /tmp/mcp.log | sort -n服务端必须保证id严格按 request 顺序返回或客户端用resolve/reject引用而非id查表中文显示为 stdio 编码不一致locale查看终端 locale服务端/客户端均设encoding: utf8Windows 用户需chcp 65001EPIPE错误频繁客户端提前关闭 stdindmesg | grep -i broken pipe客户端stdin.end()前先发{jsonrpc:2.0,method:mcp.shutdown}通知服务端mcp.tools.call返回空字符串spawn的shell: true未启用ls -l /bin/shspawn(command, { shell: true })否则ls -la被当作单个命令名mcp/types类型不识别TypeScript 未加载类型tsc --showConfig确认tsconfig.json中typeRoots包含./node_modules/mcp/types5.2 独家避坑技巧技巧一用proc.unref()避免进程阻塞退出在工具服务中如果spawn的子进程如git继承了父进程的文件描述符Node.js 主进程会等待它结束才 exit。这会导致npm test卡住。解决方案const proc spawn(git, [status], { stdio: [ignore, pipe, pipe] // 关键不继承 stdin/stderr }); proc.unref(); // 主进程 exit 时不等待此子进程技巧二LDJSON 的\r\n兼容性处理某些 Windows 工具如 PowerShell可能用\r\n结尾。服务端应兼容rl.on(line, (line) { const cleanLine line.replace(/\r$/, ); // 移除可能的 \r try { const msg JSON.parse(cleanLine); // ... } catch (e) { /* ... */ } });技巧三超时控制——别让shell命令永远卡住spawn默认无 timeoutping google.com可能永不返回。必须加const proc spawn(ping, [google.com], { timeout: 5000 }); // 5秒超时 proc.on(exit, (code, signal) { if (signal SIGTERM) { console.log(Command timed out); } });技巧四调试stdio流的终极武器——socat当 Node.js 调试失效时用socat模拟 stdio# 创建一对虚拟串口 socat -d -d pty,raw,echo0,link/tmp/mcp-in,mode600 pty,raw,echo0,link/tmp/mcp-out,mode600 # 启动服务重定向 stdin/stdout 到虚拟串口 node dist/server.js /tmp/mcp-in /tmp/mcp-out # 用另一个终端发送请求 echo {jsonrpc:2.0,id:1,method:mcp.tools.listTools} /tmp/mcp-in # 查看响应 cat /tmp/mcp-outsocat绕过所有 JS 层直击 stdio 底层是定位 IPC 问题的核武器。技巧五mcp.lognotification 的正确用法不要只在启动时发一次。服务端应在关键路径打日志// 在 handleRequest 开头 console.log([MCP] Received ${req.method} with id ${req.id}); // 发送 structured log process.stdout.write(JSON.stringify({ jsonrpc: 2.0, method: mcp.log, params: { level: debug, message: Handling ${req.method}, context: { id: req.id } } }) \n);VS Code 插件可订阅mcp.log在 Output 面板显示无需改代码就能看到服务端状态。**技巧六

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑