基于Deno与MCP协议快速构建AI工具服务器:从原理到实践
1. 项目概述一个为AI应用构建MCP服务器的现代模板如果你正在为大型语言模型LLM应用比如基于Claude、GPTs或Cursor等工具开发一个自定义的“工具箱”那么你很可能已经接触过模型上下文协议Model Context Protocol MCP。简单来说MCP就像一套标准化的“插件接口”它允许你的AI助手安全、可控地访问外部工具、数据源和API比如查询数据库、读取文件、调用天气服务等。而phughesmcr/deno-mcp-template这个项目就是一个专门为使用Deno运行时来快速构建这类MCP服务器而设计的现代化脚手架。我最初接触MCP时是从零开始搭建环境、配置协议、处理类型定义过程相当繁琐。直到发现了这个模板它把构建MCP服务器的核心流程标准化、模块化了让你能跳过大量重复的基建工作直接聚焦在实现你的业务逻辑上。它基于Deno这是一个由Node.js原班人马打造的、更安全、更现代的JavaScript/TypeScript运行时天生支持TypeScript和ES模块无需复杂的构建配置特别适合快速开发和部署轻量级服务。这个模板的核心价值在于它为你预设了一个结构清晰、类型安全、开箱即用的MCP服务器项目骨架。你只需要关心两件事定义你的工具Tools和配置你的资源Resources剩下的协议通信、错误处理、生命周期管理模板都帮你搞定了。无论是想为团队内部构建一个连接私有数据库的AI助手还是开发一个面向公众的、能操作特定SaaS平台的AI插件这个模板都能让你在几分钟内就搭起一个可运行的原型。2. 核心架构与设计思路拆解2.1 为什么选择Deno作为MCP服务器的运行时在决定使用这个模板之前理解其技术选型背后的逻辑至关重要。MCP服务器本质上是一个提供标准化JSON-RPC接口的HTTP或stdio服务。为什么模板作者phughesmcr选择了Deno而不是更常见的Node.js或Python首先安全性是首要考量。Deno默认是安全的脚本无法访问文件系统、网络或环境变量除非显式通过命令行标志如--allow-read--allow-net授权。这对于MCP服务器来说是天作之合。MCP的核心原则之一就是“最小权限访问”服务器只能访问用户明确授权的资源。Deno的权限模型与这一理念完美契合你可以在启动命令中精确控制服务器能做什么从根源上减少了安全风险。其次开发体验的极致简化。Deno内置了TypeScript编译器、测试运行器、代码格式化工具deno fmt和代码检查工具deno lint。这意味着你不需要分别配置tsc、jest、prettier和eslint。对于MCP开发这种强类型交互的场景TypeScript的支持是刚需而Deno让其变得无比自然。此外Deno使用URL导入模块直接引用远程ES模块告别了package.json和node_modules依赖管理清晰无比。最后性能与现代化标准。Deno构建在V8引擎和Rust之上性能优异。它原生支持顶层的await、Web标准API如fetch并且其标准库std非常实用。对于MCP服务器这种通常I/O密集网络请求、文件读写而非CPU密集的应用Deno的异步处理和网络库表现非常出色。注意如果你和你的团队已经深度绑定Node.js生态切换至Deno可能需要一个学习适应期。但就MCP服务器这种相对独立、边界清晰的项目而言Deno带来的安全性和开发效率提升是非常值得的。2.2 MCP模板的核心目录结构解析克隆phughesmcr/deno-mcp-template项目后你会看到一个非常清晰且克制的目录结构。这不仅仅是文件摆放更体现了作者对MCP服务器职责的深刻理解。deno-mcp-template/ ├── src/ │ ├── main.ts # 服务器入口MCP服务器实例的创建与配置中心 │ ├── tools/ # 工具Tools实现目录 │ │ └── example.ts # 示例工具演示如何定义一个工具 │ ├── resources/ # 资源Resources实现目录可选 │ │ └── example.ts # 示例资源演示如何定义一个资源 │ └── types.ts # 项目用到的共享TypeScript类型定义 ├── deno.json # Deno项目配置文件替代package.json ├── deno.lock # 依赖锁文件确保环境一致性 ├── .gitignore └── README.mdsrc/main.ts这是整个服务器的心脏。在这里你创建Server实例并通过use()方法将你在tools/和resources/目录下定义的功能“挂载”到服务器上。它还负责处理服务器的启动逻辑监听stdio或HTTP。src/tools/这是你大展拳脚的地方。MCP中的“工具”指的是AI可以调用的函数例如“获取天气”、“创建待办事项”、“查询用户信息”。每个工具都是一个独立的模块需要明确其输入参数inputSchema和具体的执行函数execute。模板中的example.ts提供了一个完整的范本。src/resources/资源代表了AI可以读取的“数据源”比如一个文本文件、一个网页的URL、或者数据库中的一张表。资源通过URI标识服务器可以声明它提供了哪些资源并在AI请求时返回其内容。如果你的MCP服务器不需要提供只读数据源这个目录可以忽略。deno.json这是Deno项目的神经中枢。它定义了脚本入口、导入映射、任务tasks、编译选项和依赖。模板中预配置了dev和start等命令让你通过deno task dev就能启动开发服务器。这种结构强制实现了关注点分离工具逻辑、资源逻辑、服务器配置各司其职使得代码易于维护、测试和扩展。当你需要新增一个功能时只需在tools/下新建一个*.ts文件然后在main.ts中引入并注册即可。2.3 模板的“开箱即用”特性与预设配置这个模板不仅仅是空目录它包含了让一个基础MCP服务器立刻跑起来的所有必要代码和配置。首先看deno.json它预置了几个关键配置{ tasks: { dev: deno run --watch --allow-read --allow-env src/main.ts, start: deno run --allow-read --allow-env src/main.ts }, imports: { modelcontextprotocol/sdk: jsr:modelcontextprotocol/sdk^0.6.0 } }dev任务在开发时使用--watch标志支持文件热重载修改代码后服务器会自动重启极大提升开发效率。start用于生产环境运行。权限方面默认只开启了--allow-read用于可能读取本地文件资源和--allow-env用于读取环境变量如API密钥这体现了最小权限原则。其次src/main.ts中已经搭建好了服务器骨架import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; // 导入工具 import { fetchWeatherTool } from ./tools/example.js; const server new Server( { name: my-mcp-server, version: 0.1.0 }, { capabilities: { tools: {}, resources: {} } } ); // 注册工具 server.setRequestHandler(工具列表请求 () [fetchWeatherTool]); server.setRequestHandler(工具调用请求 async (request) { if (request.params.name fetchWeatherTool.name) { // 调用工具执行函数 return await fetchWeatherTool.execute(request.params.arguments); } throw new Error(未知工具: ${request.params.name}); }); // 启动服务器stdio模式这是与AI桌面客户端通信的典型方式 const transport new StdioServerTransport(); await server.connect(transport);这段代码创建了服务器声明了能力注册了工具处理器并建立了stdio传输层。你几乎不需要修改这部分只需专注于导入和注册更多工具。最后src/tools/example.ts提供了一个详尽的工具定义示例包括如何使用zod库定义严格的输入参数模式schema如何在execute函数中实现业务逻辑以及如何返回结构化的结果。这为你编写自己的工具提供了完美的参考。3. 从零开始使用模板构建你的第一个MCP工具3.1 环境准备与项目初始化假设你已经安装了Deno可以通过官方脚本curl -fsSL https://deno.land/install.sh | sh一键安装现在开始创建你的第一个MCP服务器。第一步从GitHub克隆模板仓库deno run -A jsr:gb/ghlatest clone phughesmcr/deno-mcp-template my-weather-assistant cd my-weather-assistant这里使用了Deno的gh工具来克隆仓库。-A标志授予了所有权限因为克隆操作需要网络和文件系统权限。当然你也可以直接使用git clone命令。第二步进入项目目录立即尝试运行示例服务器验证一切正常deno task dev如果看到类似“Server running on stdio”或没有报错静静等待连接的状态说明模板服务器已经成功启动。此时你可以用任何实现了MCP客户端协议的AI应用如Claude Desktop 并正确配置了MCP服务器设置来连接它理论上就能调用示例中的“获取天气”工具了。实操心得在开发初期我建议使用一个简单的测试脚本来验证你的MCP服务器而不是每次都启动完整的AI客户端。你可以编写一个模拟客户端通过stdio或HTTP向你的服务器发送标准的JSON-RPC请求并打印响应。这能极大提升调试效率。模板的README里有时会提供这样的测试脚本如果没有可以基于MCP SDK的Client类自己写一个。3.2 剖析与改造一个示例工具让我们深入看看src/tools/example.ts理解一个MCP工具是如何构成的。import { z } from https://deno.land/x/zodv3.22.4/mod.ts; import { Tool } from modelcontextprotocol/sdk/server/index.js; // 1. 定义输入参数的模式Schema const WeatherArgsSchema z.object({ location: z.string().describe(城市名称例如Beijing, London), unit: z.enum([celsius, fahrenheit]).default(celsius).describe(温度单位), }); // 2. 定义工具本身 export const fetchWeatherTool: Tool { name: fetch_weather, description: 获取指定城市的当前天气情况, inputSchema: { type: object, properties: WeatherArgsSchema.shape, }, // 3. 实现执行函数 async execute(args: z.infertypeof WeatherArgsSchema) { const { location, unit } args; // 模拟API调用 await new Promise(resolve setTimeout(resolve, 100)); // 模拟网络延迟 const temp unit celsius ? 22 : 72; return { content: [ { type: text, text: 当前${location}的天气晴朗气温${temp}度${unit}。, }, ], }; }, };这个示例工具清晰地展示了三个部分参数验证使用ZodWeatherArgsSchema使用Zod库定义了工具需要的参数location字符串和unit枚举默认摄氏。Zod不仅能做运行时验证其.describe()方法生成的描述还会被AI客户端用来理解参数含义这对生成正确的调用参数至关重要。工具元数据name是工具的全局唯一标识符description告诉AI这个工具是做什么的AI会据此决定何时调用它inputSchema将Zod模式转换为JSON Schema这是MCP协议要求的格式。业务逻辑execute这是工具的核心。它接收验证后的参数执行实际操作这里模拟了网络请求并返回一个固定格式的结果。结果中的content数组可以包含文本、图像等多种类型。现在我们来将其改造成一个真实的工具比如调用一个免费的天气API例如Open-Meteo。首先我们需要更新deno.json添加我们需要的依赖比如用于HTTP请求的std/http但Deno内置的fetch通常就够了和可能的环境变量。{ tasks: { dev: deno run --watch --allow-read --allow-env --allow-net src/main.ts, start: deno run --allow-read --allow-env --allow-net src/main.ts }, imports: { modelcontextprotocol/sdk: jsr:modelcontextprotocol/sdk^0.6.0, zod: https://deno.land/x/zodv3.22.4/mod.ts } }注意我们在dev和start任务中增加了--allow-net权限因为我们的工具需要访问外部天气API。接着修改src/tools/example.ts或新建一个src/tools/weather.tsimport { z } from zod; import { Tool } from modelcontextprotocol/sdk/server/index.js; const WeatherArgsSchema z.object({ location: z.string().describe(城市名称例如Beijing, London), }); export const fetchRealWeatherTool: Tool { name: fetch_real_weather, description: 获取指定城市的实时天气和未来两日预报, inputSchema: { type: object, properties: WeatherArgsSchema.shape, }, async execute(args: z.infertypeof WeatherArgsSchema) { const { location } args; // 在实际项目中这里应该有一个从城市名到经纬度的地理编码服务。 // 为了简化我们使用一个固定的坐标例如伦敦或一个简单的映射。 // 这里假设location是“London”。 const latitude 51.5074; const longitude -0.1278; try { const response await fetch( https://api.open-meteo.com/v1/forecast?latitude${latitude}longitude${longitude}current_weathertruehourlytemperature_2mtimezoneauto ); if (!response.ok) { throw new Error(天气API请求失败: ${response.status}); } const data await response.json(); const currentTemp data.current_weather.temperature; const weatherCode data.current_weather.weathercode; // 可以根据weatherCode映射为中文描述这里简化处理 const weatherDesc getWeatherDescription(weatherCode); return { content: [ { type: text, text: 当前${location}的天气情况${weatherDesc}气温${currentTemp}°C。, }, { type: text, text: 未来几小时温度趋势${data.hourly.temperature_2m.slice(0, 6).join(‘ ‘)}°C, }, ], }; } catch (error) { // 良好的错误处理对于AI理解至关重要 return { content: [ { type: text, text: 获取${location}天气信息时出错${error.message}。请检查城市名称或稍后重试。, }, ], isError: true, // MCP协议中表示这是一个错误结果 }; } }, }; // 简单的天气代码映射函数 function getWeatherDescription(code: number): string { const map: Recordnumber, string { 0: “晴朗” 1: “大部晴朗” 2: “局部多云” 3: “多云” 45: “雾” 48: “冻雾” 51: “小雨” 61: “雨” 80: “阵雨” }; return map[code] || 天气代码${code}; }最后别忘了在src/main.ts中导入并注册这个新工具// ... 其他导入 import { fetchRealWeatherTool } from “./tools/weather.js”; // 假设文件名为weather.ts // ... 在server.setRequestHandler中注册 server.setRequestHandler(工具列表请求 () [fetchRealWeatherTool /*, 其他工具... */]); server.setRequestHandler(工具调用请求 async (request) { if (request.params.name fetchRealWeatherTool.name) { return await fetchRealWeatherTool.execute(request.params.arguments); } // ... 其他工具判断 });3.3 定义与提供资源Resources除了工具MCP服务器还可以提供“资源”。资源是只读的、可通过URI寻址的数据片段。例如你的服务器可以声明它提供了file:///etc/hosts这个资源当AI需要读取该文件内容时就会向你的服务器发起请求。在模板的src/resources/example.ts中有一个简单的示例import { Resource } from “modelcontextprotocol/sdk/server/index.js”; export const exampleResource: Resource { uri: “example://greeting”, name: “示例问候资源” description: “一个简单的静态文本资源” mimeType: “text/plain” // 当AI请求该资源时调用此函数获取内容 async getContent() { return { contents: [ { uri: “example://greeting” mimeType: “text/plain” text: “你好世界这是一个来自MCP服务器的资源。” }, ], }; }, };在main.ts中你需要通过server.setRequestHandler为resources/list和resources/read请求注册处理器将你的资源列表和读取逻辑关联上去。资源的典型应用场景包括提供静态文档如项目README、API文档。聚合动态数据如从内部仪表盘拉取数据生成一个汇总报告资源。文件系统代理在用户授权下将本地特定目录的文件以资源形式暴露给AI需谨慎处理权限。注意事项资源URI的设计应有清晰的命名空间避免冲突。例如使用your-server://作为前缀。同时getContent函数应做好错误处理对于不存在的资源或访问错误返回合适的错误信息。4. 开发、调试与部署全流程指南4.1 高效的本地开发与调试技巧使用deno task dev启动开发服务器后它会在stdio上等待连接。但如何调试呢这里有几个实用技巧1. 使用MCP Inspector进行可视化调试Anthropic官方提供了一个强大的调试工具叫MCP Inspector。你可以通过npm全局安装npm install -g modelcontextprotocol/inspector。然后在一个终端运行你的MCP服务器deno task start在另一个终端运行mcp-inspector —-transport stdio “deno task start”。Inspector会启动一个本地Web界面你可以在这里手动发送tools/listtools/call等请求并直观地查看请求和响应这对于调试工具逻辑和协议交互无比方便。2. 集成到Claude Desktop进行端到端测试这是最终的测试环境。编辑Claude Desktop的配置文件通常在~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS添加你的服务器配置{ “mcpServers”: { “my-weather-server”: { “command”: “deno” “args”: [ “run” “--allow-read” “--allow-env” “--allow-net” “/ABSOLUTE/PATH/TO/YOUR/PROJECT/src/main.ts” ], “env”: { “OPEN_METEO_API_KEY”: “your_key_here” // 如果需要 } } } }重启Claude Desktop后你就可以在对话中直接使用你的工具了。这是验证工具描述是否清晰、AI调用是否准确的最佳方式。3. 编写单元测试Deno内置了测试框架。为你的工具execute函数编写单元测试可以确保核心逻辑的稳定性。例如为fetchRealWeatherTool编写一个测试模拟fetch请求返回固定数据验证函数是否能正确解析并返回预期格式的内容。使用Deno.test和assert或expect风格的断言库即可。4.2 生产环境部署与配置管理开发完成后你需要将MCP服务器部署到一个稳定运行的环境中。部署方式取决于你的使用场景场景一个人或小团队使用与Claude Desktop集成这是最简单的情况。你只需要将最终代码打包其实Deno项目本身就是“打包”好的确保目标机器安装了相同版本的Deno。然后像上面调试那样在每个人的Claude Desktop配置中指向部署好的脚本路径可以是网络路径或本地路径。你可以将项目编译成一个可执行的单文件使用deno compile来简化分发。场景二作为公共服务供多个AI客户端通过HTTP连接MCP也支持HTTP/SSE传输。你需要修改src/main.ts中的启动部分使用new HTTPServerTransport创建一个HTTP服务器。然后将服务部署到任何云平台如Deno Deploy Fly.io Railway或你自己的服务器上。客户端配置中command字段将变为一个URL。关键的生产环境考量权限最小化在deno task start命令中只开启必要的权限。例如如果工具只需要网络访问就不要开--allow-read。环境变量管理API密钥、数据库连接字符串等敏感信息必须通过环境变量传入。在deno.json的start任务中它们已经通过--allow-env允许读取。在生产环境使用平台提供的Secret管理工具或.env文件配合dotenv库来管理。日志与监控在服务器代码中添加日志记录记录工具调用、错误等信息。Deno标准库提供了log模块。对于更复杂的监控可以考虑集成像Opentelemetry这样的遥测库。错误处理与用户体验确保所有可能的错误网络超时、API限流、无效输入都在工具execute函数中被捕获并返回对AI友好的错误信息。避免将内部堆栈跟踪泄露给客户端。版本管理当你的工具更新时考虑在服务器信息中更新版本号。这有助于客户端进行兼容性管理。4.3 性能优化与安全加固建议随着工具数量的增加一些优化和安全措施变得必要。性能优化连接池与缓存如果你的工具需要频繁访问数据库或外部API考虑在服务器生命周期内创建连接池而不是每次调用都新建连接。对于不经常变化的数据可以实现一个简单的内存缓存注意缓存失效策略。异步初始化如果某些工具需要加载大型模型或配置文件可以在服务器启动时进行异步初始化并将初始化结果保存在闭包中供execute函数使用避免每次调用都重复加载。精简依赖定期检查deno.json中的imports移除未使用的依赖。Deno的依赖是远程URL清晰的依赖树有助于快速启动。安全加固输入验证再次强调Zod是你的第一道防线。严格定义输入模式拒绝任何不符合预期的参数。对于字符串参数注意防范注入攻击如SQL注入、命令注入即使AI生成的内容通常可信也要做无害化处理。权限细分如果可能不要使用-A所有权限。仔细评估每个工具需要的权限并为不同的工具集配置不同的Deno运行权限这可能需要更复杂的架构如将高权限工具分离到独立进程中。速率限制如果你的服务器公开暴露应考虑对客户端IP或会话实施速率限制防止滥用。可以使用中间件或装饰器模式在调用工具前进行限流检查。审计日志记录所有工具的调用记录包括调用者如果协议支持、工具名、参数注意脱敏敏感参数和时间戳。这对于故障排查和安全审计至关重要。5. 进阶应用与生态集成5.1 构建复杂工具链与工具组合一个强大的MCP服务器往往不止提供一个工具而是提供一组相互关联的工具形成一个“工具链”。例如一个“客户支持助手”MCP服务器可能包含以下工具search_knowledge_base: 根据用户问题搜索内部知识库。create_support_ticket: 在工单系统中创建新的支持工单。get_customer_info: 根据客户ID查询客户基本信息。escalate_to_engineer: 将复杂问题升级分配给工程师。这些工具可以组合使用。AI可能会先调用search_knowledge_base如果没找到答案再调用get_customer_info确认客户详情最后调用create_support_ticket。你的MCP服务器内部这些工具可以共享一些状态或客户端比如同一个数据库连接池、同一个认证后的API客户端实例。你可以在src/main.ts中初始化这些共享资源然后通过闭包或依赖注入的方式传递给各个工具的execute函数。5.2 与现有后端服务及数据库集成大多数有意义的MCP工具都需要与现有系统交互。模板项目可以轻松集成各种后端服务。集成数据库假设使用PostgreSQL你可以使用Deno的PostgreSQL客户端如postgres。首先在deno.json中导入它然后在main.ts中初始化连接池import postgres from “jsr:db/postgres”; const pool new postgres.Pool({ connectionString: Deno.env.get(“DATABASE_URL”), }, 10); // 连接池大小然后在工具的execute函数中从连接池获取客户端并执行查询。务必做好SQL参数化查询防止注入。调用内部REST API使用Deno内置的fetch即可。建议将API基础URL和认证头如API Key配置在环境变量中。可以为所有需要调用该API的工具创建一个共享的、配置好的fetch函数封装处理通用的错误重试和认证逻辑。使用第三方SDK许多SaaS服务提供了TypeScript/JavaScript SDK。你可以通过JSR或直接URL将它们导入到你的项目中。例如集成GitHub API可以使用octokit集成Slack可以使用slack/web-api。确保遵循这些SDK的认证和初始化要求。5.3 扩展模板添加中间件与自定义传输层模板提供了基础框架但你完全可以扩展它以满足更复杂的需求。添加中间件MiddlewareMCP SDK的Server类本身可能不直接支持中间件模式但你可以通过高阶函数或包装execute函数来实现类似功能。例如创建一个“日志中间件”function withLogging(tool: Tool): Tool { return { …tool, async execute(args: any) { console.log([${new Date().toISOString()}] 调用工具: ${tool.name} 参数:, args); const start Date.now(); try { const result await tool.execute(args); console.log([${new Date().toISOString()}] 工具 ${tool.name} 调用成功耗时: ${Date.now() - start}ms); return result; } catch (error) { console.error([${new Date().toISOString()}] 工具 ${tool.name} 调用失败:, error); throw error; } }, }; } // 使用时 const loggedTool withLogging(fetchRealWeatherTool);你可以类似地创建认证中间件、参数转换中间件等。自定义传输层模板默认使用StdioServerTransport这是与桌面客户端通信的标准方式。如果你需要支持WebSocket、自定义TCP协议或其他通信方式你可以实现MCP SDK中定义的Transport接口。这通常用于将MCP服务器嵌入到其他应用程序中或者实现更复杂的通信场景。6. 常见问题排查与实战心得6.1 开发与运行中的典型错误及解决在开发和运行基于此模板的MCP服务器时你可能会遇到一些典型问题。下面是一个快速排查指南问题现象可能原因解决方案运行deno task dev立即报错提示“未找到模块”或“导入映射错误”。1.deno.json中的imports配置错误或URL不可达。2. 网络问题导致无法下载远程模块。1. 检查deno.json中imports的URL是否正确特别是版本号。2. 尝试运行deno cache --reload src/main.ts强制重新缓存依赖。3. 对于JSR包确保使用了正确的jsr:scope/packageversion格式。服务器启动成功但AI客户端如Claude Desktop无法连接或找不到工具。1. AI客户端的MCP服务器配置路径错误。2. 服务器输出的协议头不符合MCP标准。3. 权限不足服务器启动失败但未显式报错。1. 仔细检查客户端配置文件中command和args的绝对路径是否正确。2. 使用mcp-inspector测试服务器是否能正常响应initialize和tools/list请求。3. 确保启动命令包含了所有必要的--allow-*标志。可以尝试先用-A运行测试再逐步收紧权限。工具能被AI列出但调用时失败AI收到“工具调用错误”。1. 工具的execute函数内部抛出未捕获的异常。2. 输入参数不符合inputSchema但Zod验证被绕过或未生效。3. 网络请求超时或外部API返回错误。1. 在execute函数内部添加详细的try…catch并返回格式化的错误信息{content: […], isError: true}。2. 确认在main.ts的调用处理器中传递给execute的是request.params.arguments并且服务器SDK已根据schema做了初步验证。3. 检查外部API的可用性并为fetch添加超时和重试逻辑。工具调用成功但AI显示的结果格式混乱或无法解析。工具返回的content格式不符合MCP协议要求。确保返回的对象结构为{content: Array{type: string, text: string, …}}。对于纯文本使用{type: “text”, text: “…”}。复杂的多模态内容需遵循协议定义。使用mcp-inspector查看原始响应比对协议规范。在工具中读取环境变量返回undefined。1. 环境变量未设置。2. Deno进程没有--allow-env权限。3. 环境变量名拼写错误。1. 确认环境变量已在运行环境中设置如.env文件或云平台配置。2. 检查deno.json中start/dev任务是否包含--allow-env。3. 使用console.log(Deno.env.get(“YOUR_VAR”))调试或使用Deno.env.has()检查是否存在。6.2 从项目实践中萃取的独家心得经过多个项目的打磨我总结出一些在官方文档和模板中不会明确提及但却能极大提升开发体验和项目质量的技巧。心得一工具命名与描述的“艺术”工具name和description是AI理解和使用它的唯一依据。命名要清晰、具体、动词开头最好能反映其核心操作和领域例如用create_jira_issue而非jira。描述要详尽不仅说明功能最好能举例说明典型使用场景和输入输出。例如“在Jira项目中创建一个新的问题Issue。需要提供项目键、问题类型、摘要和描述。可选参数包括优先级、经办人等。成功时返回新创建问题的键如PROJ-123和链接。” 这样的描述能极大提高AI调用的准确性。心得二利用Zod的.describe()和.default()Zod不仅用于验证更是与AI沟通的桥梁。为每个参数使用.describe()提供人类可读的描述AI会利用这些信息来生成调用参数。合理使用.default()设置智能默认值可以简化AI需要提供的参数数量提升交互流畅度。例如一个send_email工具可以将cc参数默认设置为空数组[]。心得三设计“原子化”与“复合型”工具工具设计应遵循单一职责原则。一个工具只做一件事并把它做好原子化。例如get_user_by_id和update_user_profile应该是两个独立的工具。然而有时AI需要执行一个多步骤流程。虽然AI可以顺序调用多个原子工具但你也可以提供一个更高效的“复合型”工具。例如一个onboard_new_user工具内部封装了创建账户、发送欢迎邮件、分配初始权限等一系列操作。这需要在原子性和效率之间取得平衡。心得四为工具添加“模拟模式”或“空跑模式”在开发初期或者当外部API不稳定、有调用限制时为工具的execute函数添加一个“模拟模式”非常有用。可以通过环境变量MOCK_MODEtrue来触发。在模拟模式下工具返回预设的模拟数据而不是真正调用外部服务。这让你和AI可以完整地测试工具调用流程和结果处理逻辑而无需依赖外部依赖。这在演示或编写自动化测试时也特别方便。心得五版本化你的MCP服务器当你的工具集更新特别是进行不兼容的变更如删除参数、改变返回值结构时考虑对服务器进行版本化管理。可以在Server初始化时提供版本号并在客户端配置中指定兼容的版本范围。对于重大变更甚至可以并行运行不同版本的服务端让客户端逐步迁移。这在小团队内部可能不是问题但在开发供多人使用的公共服务时至关重要。