尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

TypeScript MCP SDK进阶:高级特性与最佳实践

TypeScript MCP SDK进阶:高级特性与最佳实践 摘要TypeScript MCP SDK进阶教程涵盖高级工具定义、上下文管理、自定义传输、中间件机制和类型安全最佳实践帮助开发者构建生产级MCP Server。TypeScript MCP SDK进阶 高级特性与最佳实践上周有个朋友问我他用 Python 的 FastMCP 写了个 MCP 服务器跑得好好的为啥我非得折腾 TypeScript SDK。原因很简单我手头的项目前端是 React 全家桶后端是 Node.js用 TS 写 MCP 服务器可以跟现有代码库共享类型定义不用来回切语言。这篇就来聊聊我在实际项目里用 TypeScript MCP SDK v2 踩出来的一些经验和模式。TypeScript SDK v2 的核心变化先说个大背景。TypeScript SDK 在 2026 年 7 月跟着 MCP 2026-07-28 规范一起发布了 v2 版本。包名从之前的modelcontextprotocol/sdk拆成了两个独立包modelcontextprotocol/server和modelcontextprotocol/client。最大的变化是 schema 验证引入了 Standard Schema 标准。你可以用 Zod v4、Valibot、ArkType 等任何兼容的库来定义工具参数的 schemaSDK 不再绑死某一种验证器。这对我来说是个好消息因为项目里已经在用 Zod 了。先看一个最基本的服务器搭建// 导入 MCP Server 核心类和 stdio 传输层import{McpServer}frommodelcontextprotocol/server;import{StdioServerTransport}frommodelcontextprotocol/server/stdio;// 使用 Zod v4 做 schema 验证import*aszfromzod/v4;// 创建服务器实例指定名称和版本号// 客户端连接时会拿到这些信息用于识别constservernewMcpServer({name:my-advanced-server,version:1.0.0,});// 注册一个工具第二参数是配置对象第三参数是处理函数// inputSchema 用 Zod 定义SDK 自动转换为 JSON Schema 发给客户端server.registerTool(greet,{description:根据名字打招呼,inputSchema:z.object({name:z.string().min(1).describe(对方的名字),formal:z.boolean().optional().default(false).describe(是否使用正式语气),}),},async({name,formal}){// 处理函数接收的是已经验证过的参数// 类型由 Zod schema 推断不用手动写类型constgreetingformal?尊敬的${name}您好:嗨${name};return{content:[{type:text,text:greeting}],};});// 启动服务器使用 stdio 传输// stdio 适合本地命令行工具场景asyncfunctionmain(){consttransportnewStdioServerTransport();awaitserver.connect(transport);}main();这里有个我踩过的坑。v2 刚出来的时候很多教程还在用server.tool()方法注册工具那是 v1 的 API。v2 统一改成了server.registerTool()而且参数结构也变了。如果你从 v1 迁移过来一定注意这个方法名的变化。类型推断的魔法TypeScript SDK 最让我爽的一点是类型推断。你看上面那段代码处理函数的参数{ name, formal }完全不需要手动声明类型Zod schema 定义好了之后TypeScript 自动推断出来。对比一下 Python 那边。FastMCP 通过函数签名的类型注解来生成 schema虽然也很方便但有个限制是你必须在函数签名上写完整的类型。TypeScript 这边可以更灵活地组合 schema// 定义一个可复用的 schema 片段// 多个工具可以共享这个 schemaconstpaginationSchemaz.object({page:z.number().int().min(1).default(1).describe(页码从1开始),pageSize:z.number().int().min(1).max(100).default(20).describe(每页条数),});// 用 extend 组合出更复杂的 schema// 这种组合方式在 Python 里要靠 Pydantic 的继承不如这个灵活constuserListSchemapaginationSchema.extend({keyword:z.string().optional().describe(搜索关键词),status:z.enum([active,inactive,banned]).optional().describe(用户状态),});// 注册工具时直接传入组合后的 schema// 处理函数的参数类型自动推断包含 page, pageSize, keyword, statusserver.registerTool(search-users,{description:搜索用户列表支持分页和过滤,inputSchema:userListSchema,},async(params){// params 类型完全由 schema 推断// IDE 里自动补全和类型检查都正常工作const{page,pageSize,keyword,status}params;// 模拟数据库查询constresultsawaitmockDbQuery(page,pageSize,keyword,status);return{content:[{type:text,text:JSON.stringify(results)}],};});// 模拟数据库查询函数asyncfunctionmockDbQuery(page:number,pageSize:number,keyword?:string,status?:string){return{page,pageSize,total:42,items:[{id:1,name:test}],};}中间件机制v2 引入了中间件包的概念。这些中间件包是针对特定运行时或 Web 框架的薄适配器帮你把 MCP 服务器挂到 Express、Fastify、Hono 或者原生 Node.js HTTP 上。这里我要分享一个独家踩坑经验。我一开始用 Express 中间件包挂载 MCP 服务器结果发现客户端连接时一直报 400 错误。查了半天才发现Express 的 body-parser 默认限制 body 大小为 100kb而某些包含大量上下文的请求会超过这个限制。解决办法是在 MCP 路由之前设置更大的 body 限制// 导入 Express 中间件包和 Express 本身importexpressfromexpress;import{hostValidation}frommodelcontextprotocol/express;constappexpress();// 关键坑点 必须在 MCP 路由之前提高 body 大小限制// 默认 100kb 对某些包含大量上下文的请求不够用// 我设成了 10mb根据你的实际场景调整app.use(express.json({limit:10mb}));// 启用 Host header 验证防止 DNS 重绑定攻击// 这是中间件包提供的安全功能app.use(hostValidation());// MCP 路由挂载点// 客户端通过 http://localhost:3000/mcp 连接app.use(/mcp,mcpExpressHandler);app.listen(3000,(){console.log(MCP server running on http://localhost:3000/mcp);});对比 Python 这边FastMCP 也支持 HTTP 传输但配置方式完全不同。FastMCP 内置了 uvicorn 或 Starlette 的集成你直接调用mcp.run(transporthttp)就行。TypeScript 这边需要你自己选 Web 框架灵活但多了一步配置。上下文管理MCP 的请求处理函数可以接收一个上下文对象里面包含了当前会话的信息和可以调用的方法。在 TypeScript SDK 里这个上下文是通过处理函数的第二个参数传入的// 导入 Server 工具上下文类型importtype{ServerToolContext}frommodelcontextprotocol/server;server.registerTool(process-large-file,{description:处理大文件支持进度上报和日志,inputSchema:z.object({filePath:z.string().describe(文件路径),}),},async({filePath},context:ServerToolContext){// context 里可以访问到进度上报和日志功能// 这些是 MCP 协议层面的能力// 发送日志通知给客户端// 客户端可以在 UI 上展示这些日志context.sendLoggingMessage({level:info,logger:file-processor,data:{message:开始处理文件${filePath}},});// 模拟文件处理过程consttotalLines10000;for(leti0;itotalLines;i){// 每处理 1000 行上报一次进度if(i%10000){context.sendLoggingMessage({level:debug,logger:file-processor,data:{progress:${i}/${totalLines}},});}}return{content:[{type:text,text:文件处理完成共${totalLines}行}],};});这里有个坑我踩了好几次。上下文对象的sendLoggingMessage方法只有在客户端先发送了logging/setLevel请求之后才会真正工作。如果客户端没设置日志级别你发的日志通知会被静默忽略。调试的时候很容易以为代码有 bug其实是客户端那边没开启日志接收。与 Python SDK 的对比我两个 SDK 都用过不少总结一下核心差异维度TypeScript SDK v2Python FastMCPSchema 验证Standard Schema 标准支持 Zod/Valibot/ArkType基于 Python 类型注解底层用 Pydantic类型推断Zod schema 自动推断处理函数参数类型函数签名注解直接作为类型传输层stdio Streamable HTTP中间件适配多框架stdio SSE HTTP内置 uvicorn 集成运行时Node.js / Bun / DenoCPython 3.10异步模型原生 async/await事件循环由运行时管理asyncio需要注意事件循环嵌套问题包管理npm / bun / deno addpip / uv add部署体积较小适合 serverless较大Docker 镜像通常 200MB我的建议是这样。如果你的项目已经用了 Node.js 或者前端技术栈选 TypeScript SDK类型定义可以跨前后端复用。如果你做的是数据分析、机器学习相关的 MCP 服务器选 Python生态里有大量现成的库可以直接用。完整代码下面是一个完整的 TypeScript MCP 服务器项目包含工具、资源和提示三大能力可以直接运行// file: src/server.ts// 完整的 MCP 服务器示例包含工具、资源和提示// 运行方式: npx tsx src/server.ts// 依赖安装: npm install modelcontextprotocol/server zod// 导入依赖 import{McpServer}frommodelcontextprotocol/server;import{StdioServerTransport}frommodelcontextprotocol/server/stdio;import*aszfromzod/v4;// 创建服务器实例 constservernewMcpServer({name:advanced-demo-server,version:1.0.0,});// 工具部分 // 一个带完整参数校验和错误处理的计算器工具constcalculatorSchemaz.object({operation:z.enum([add,subtract,multiply,divide]).describe(运算类型),a:z.number().describe(第一个操作数),b:z.number().describe(第二个操作数),});server.registerTool(calculator,{description:四则运算计算器,inputSchema:calculatorSchema,},async({operation,a,b}){// 根据运算类型执行对应计算letresult:number;switch(operation){caseadd:resultab;break;casesubtract:resulta-b;break;casemultiply:resulta*b;break;casedivide:// 除法需要处理除零错误if(b0){// 返回 isError 为 true 的结果// 这属于工具执行错误不是协议错误return{content:[{type:text,text:错误: 除数不能为零}],isError:true,};}resulta/b;break;default:// 理论上不会走到这里因为 Zod 已经校验过return{content:[{type:text,text:不支持的运算类型}],isError:true,};}// 正常返回计算结果return{content:[{type:text,text:${a}${operation}${b}${result}}],};});// 资源部分 // 注册一个文件资源模板支持动态 URI// 客户端可以通过 file:///path/to/file 的形式读取内容server.registerResource(config,config://app/settings,应用配置,获取当前应用的配置信息,async(uri){// 模拟读取配置文件constconfig{appName:advanced-demo,version:1.0.0,environment:process.env.NODE_ENV||development,};return{contents:[{uri:uri.href,mimeType:application/json,text:JSON.stringify(config,null,2),}],};});// 提示部分 // 注册一个代码审查提示模板server.registerPrompt(code-review,对给定代码进行审查并给出改进建议,{// 参数定义code:z.string().describe(要审查的代码),language:z.string().optional().describe(编程语言),},async({code,language}){// 构造审查提示消息// 可以返回多条消息模拟多轮对话constlangTextlanguage?这是一段${language}代码:这是一段代码;return{messages:[{role:user,content:{type:text,text:${langText}请审查以下代码并给出改进建议:\n\n\\\\n${code}\n\\\,},},],};});// 启动服务器 asyncfunctionmain(){consttransportnewStdioServerTransport();awaitserver.connect(transport);console.error(MCP server started on stdio);// 注意这里用 console.error 而不是 console.log// 因为 stdout 被 MCP 协议占用了日志只能输出到 stderr}main().catch(console.error);效果验证把上面的代码保存为src/server.ts安装依赖后用 MCP Inspector 测试# 安装依赖npminstallmodelcontextprotocol/server zod# 用 MCP Inspector 启动测试npx modelcontextprotocol/inspector npx tsx src/server.ts在 Inspector 里你可以看到三个能力都注册成功了。调用 calculator 工具传入{ operation: divide, a: 10, b: 0 }会返回除零错误。传入{ operation: add, a: 1, b: 2 }返回1 add 2 3。资源部分访问config://app/settings可以拿到 JSON 格式的配置信息。提示部分调用code-review并传入代码会返回构造好的审查提示消息。常见问题与避坑1. stdout 被污染导致协议解析失败这是最常见的问题。MCP stdio 传输模式下stdout 是协议通道你不能往里面console.log任何东西。一旦写了客户端就会收到无法解析的 JSON-RPC 消息然后报错。所有日志必须走console.error输出到 stderr。我之前有一次在工具处理函数里加了个console.log调试客户端直接连接断开排查了好久。2. Zod 版本不兼容v2 SDK 要求 Zod v4。如果你项目里还装着 Zod v3导入路径要写成zod/v4而不是zod。两个版本可以共存但别搞混了。混用的后果是 schema 验证通过但类型推断对不上编译器报一堆莫名其妙的类型错误。3. 中间件包的 Host header 验证Express 和 Fastify 的中间件包默认开启了 Host header 验证防止 DNS 重绑定攻击。本地开发的时候如果你用 IP 地址而不是 localhost 访问会被拒绝。调试时可以先临时关掉这个验证但上线前一定记得开回来。4. 异步错误没有被捕获处理函数里的 async 错误如果没 try-catch会变成 unhandled promise rejection服务器进程可能直接崩掉。建议在每个处理函数外面包一层 try-catch把异常转成isError: true的工具结果返回。5. registerTool vs tool 方法混淆网上很多教程还在用 v1 的server.tool()方法。v2 改成了server.registerTool()参数结构也变了。从 v1 迁移时一定要对照官方文档改 API 调用别直接复制旧代码。小结这篇聊了 TypeScript MCP SDK v2 的几个核心特性。类型推断配合 Zod schema 让开发体验非常丝滑处理函数的参数类型完全自动推断。中间件包帮你快速接入各种 Web 框架但要注意 body 大小限制和 Host 验证这两个坑。上下文对象提供了日志和进度上报能力但需要客户端先开启对应功能才能生效。跟 Python SDK 相比TypeScript 更适合 Node.js 技术栈的项目类型定义可以跨前后端共享。下一篇我们会深入工具开发的参数校验和错误处理细节。相关推荐Python MCP SDK入门FastMCP快速开发TypeScript快速上手用Node.js写你的第一个MCP Server工具开发实战参数校验、错误处理与异步工具
返回列表