DeepCode MCP Server 构建最佳实践:从命名规范、响应设计到安全加固的完整指南
【免费下载链接】DeepCode"DeepCode: Open Agentic Coding (Agent Harness & Loop Engineering & Multi-Agent Orchestration)"项目地址: https://gitcode.com/GitHub_Trending/deepc/DeepCode
导读
本文是 DeepCode 开源仓库内置mcp-builder技能中通用 MCP(Model Context Protocol)服务端构建指南的完整解读。它面向两类读者:一是要为自己的服务(Slack、GitHub、Jira 等)编写高质量 MCP 服务器、让 LLM 能顺畅调用外部 API 的开发者;二是要在 DeepCode 这一 Agentic Coding 平台中注册、配置与消费 MCP 服务器的使用者。读完本文,你将掌握一套可直接落地的 MCP 服务器设计规范——包括服务器/工具命名、JSON 与 Markdown 双响应格式、分页元数据、stdio 与 Streamable HTTP 传输选型、OAuth 2.1 与 API Key 安全实践、工具注解语义以及完整的测试与文档要求,并能从 DeepCode 的运行时实现(core/mcp)中看到这些规范在真实产品中的落地形态。
一、文档定位:mcp-builder 技能体系中的"通用准则"
在 DeepCode 仓库中,mcp-builder是一个内置技能,其总纲文件 SKILL.md 将创建 MCP 服务器的流程划分为四个阶段:深度调研与规划(Phase 1)、实现(Phase 2)、评审与测试(Phase 3)、创建评估(Phase 4)。其中阶段 1.3 明确要求加载本文档——mcp_best_practices.md,并称之为"Core guidelines / Universal MCP guidelines"。
该文档在整个技能体系中处于"语言无关的通用规范"层,与另外三份文档构成完整体系:
| 文档(仓库根目录相对路径) | 作用 |
|---|---|
| reference/mcp_best_practices.md | 通用最佳实践:命名、响应、分页、传输、安全、注解、错误处理、测试、文档(本文主题) |
| reference/python_mcp_server.md | Python/FastMCP 专属实现指南(Pydantic 校验、@mcp.tool注册、完整示例) |
| reference/node_mcp_server.md | Node/TypeScript 专属实现指南(Zod 校验、registerTool注册、完整示例) |
| reference/evaluation.md | MCP 服务器评估体系:如何用 10 道可验证问题检验 LLM 对服务器的使用效果 |
DeepCode 项目自述为 "Open Agentic Coding (Agent Harness & Loop Engineering & Multi-Agent Orchestration)",其自身的 MCP 运行时(core/mcp/runtime.py)实现了对这些规范的消费端支持——包括服务器启动生命周期、工具发现与注册、注解解析、审批模式等,后文将逐一对应展开。
二、命名规范:让模型一眼找到正确的工具
2.1 服务器命名(Server Naming)
最佳实践文档给出了与语言绑定、且易于从任务描述推断的命名模式:
- Python:
{service}_mcp(小写 + 下划线),例如slack_mcp、github_mcp、jira_mcp; - Node/TypeScript:
{service}-mcp-server(小写 + 连字符),例如slack-mcp-server、github-mcp-server、jira-mcp-server。
命名应满足四条约束:通用(不绑定具体功能特性)、能描述所集成的服务、易于从任务描述中推断、不含版本号或日期。
这一约定在语言专属指南中被进一步落实为可运行的代码:Python 侧初始化mcp = FastMCP("example_mcp")(见 python_mcp_server.md),TypeScript 侧初始化new McpServer({ name: "example-mcp", version: "1.0.0" })(见 node_mcp_server.md)。
在 DeepCode 消费端,服务器名被严格校验:core/mcp/models.py 中的validate_server_name()使用正则^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$,即 1~80 个字符、仅允许字母数字与.、-、_,否则抛出McpConfigurationError。这意味着服务端命名的"易推断"与消费端的"可解析"是相互配套的。
2.2 工具命名(Tool Naming)
工具命名规范可以浓缩为四条:
- 使用 snake_case:如
search_users、create_project、get_channel_info; - 携带服务前缀:要预见到你的 MCP 服务器会与其他 MCP 服务器并存,用
slack_send_message而非send_message,用github_create_issue而非create_issue; - 动作导向:以动词开头(get、list、search、create 等);
- 足够具体:避免与其他服务器冲突的通用名。
格式归纳为:{service}_{action}_{resource},例如slack_send_message、github_create_issue。
这条规范在 DeepCode 的运行时中得到了"双重保障"式的实现。见 core/mcp/naming.py 的visible_tool_name():
- 每个来自 MCP 服务器的工具,会被映射为
mcp__{server}__{tool}形式(如mcp__slack__send_message),天然携带服务前缀; - 名称长度上限 64 字符(
MAX_TOOL_NAME_LENGTH = 64),超出时用 SHA-256 摘要(前 10 位十六进制)做确定性后缀,既保证唯一又可复现; - 遇到重名时自动追加
_1、_2编号,不会覆盖其他已注册工具。
也就是说,即使服务器作者漏写了服务前缀,DeepCode 也会在模型可见层强制带上mcp__<server>__前缀以避免跨服务器冲突(原始工具名仍以McpToolIdentity.raw_name保留,见 core/mcp/tools.py)。这在 tests/test_mcp_runtime.py 等测试中有覆盖。
三、工具设计:描述要"窄而准"
工具设计(Tool Design)部分强调:
- 工具描述必须窄范围、无歧义地描述其功能;
- 描述必须与实际功能精确匹配(防幻觉);
- 提供工具注解(
readOnlyHint、destructiveHint、idempotentHint、openWorldHint); - 保持操作聚焦且原子,一个工具只做一件事。
Python 实现指南进一步给出操作要点:描述应由函数签名 + docstring 自动生成(FastMCP 特性),docstring 中应包含参数说明、返回 JSON schema、使用示例("Use when / Don't use when")、错误处理说明。TypeScript 侧则强调:description字段必须显式提供,JSDoc 注释不会自动提取;inputSchema必须是 Zod schema 对象(而非 JSON schema);outputSchema应尽量定义以输出结构化数据。
四、响应格式:JSON 与 Markdown 双轨并行
最佳实践文档规定:所有返回数据的工具都应支持多种格式,通过response_format参数切换。
JSON 格式(response_format="json")——面向程序化处理:
- 机器可读的结构化数据;
- 包含所有可用字段与元数据;
- 字段名与类型保持一致。
Markdown 格式(response_format="markdown",通常为默认)——面向人类/LLM 阅读:
- 使用标题、列表与排版增强可读性;
- 将时间戳转换为人类可读格式(如
2024-01-15 10:30:00 UTC而非 epoch); - 显示名 + ID 并列展示(如
@john.doe (U123456)); - 省略冗余元数据(如只保留一个头像 URL,而非全部尺寸);
- 按逻辑对相关信息分组。
Python 侧推荐用StrEnum定义ResponseFormat,例如:
from enum import Enum class ResponseFormat(str, Enum): MARKDOWN = "markdown" JSON = "json" class UserSearchInput(BaseModel): query: str = Field(..., description="Search query") response_format: ResponseFormat = Field( default=ResponseFormat.MARKDOWN, description="Output format: 'markdown' for human-readable or 'json' for machine-readable" )TypeScript 侧用z.nativeEnum(ResponseFormat).default(ResponseFormat.MARKDOWN)实现等价约束,并在返回时同时给出content(文本展示)与structuredContent(结构化数据)——后者是 TypeScript SDK 支持"文本 + 结构化"双通道的现代模式。
DeepCode 消费端的处理与此呼应:core/mcp/tools.py 的_result_text()会遍历结果的content块提取文本,并在没有文本块时回退读取structuredContent并序列化为 JSON——说明服务端返回的两种形态在 DeepCode 中都能被正确呈现给模型。
五、分页规范:永远尊重 limit 参数
对于列出资源的工具,最佳实践文档要求:
- 始终尊重
limit参数; - 实现分页:使用
offset或游标(cursor)分页; - 返回分页元数据:
has_more、next_offset/next_cursor、total_count; - 绝不把全部结果一次性加载进内存(尤其针对大数据集);
- 默认限制合理:通常 20~50 条。
文档给出的示例分页响应:
{ "total": 150, "count": 20, "offset": 0, "items": [...], "has_more": true, "next_offset": 20 }在 Python 实现指南中,对应的字段约束是limit(ge=1, le=100,默认 20)与offset(ge=0,默认 0),且has_more通过total > offset + len(items)计算。TypeScript 指南还额外引入CHARACTER_LIMIT常量(示例为 25000 字符)防止响应过大撑爆上下文,超限时截断并在消息中提示Use 'offset' parameter or add filters to see more results.。
这与 DeepCode 的上下文管理哲学一致:Agent 的上下文窗口是稀缺资源,工具应返回"聚焦、相关"的数据(SKILL.md 阶段 1.1 的 Context Management 要求),分页 + 过滤正是实现手段。
六、传输层选型:stdio 还是 Streamable HTTP
6.1 两种传输的特性
Streamable HTTP——适合远程服务器、Web 服务、多客户端场景:
- 基于 HTTP 的双向通信;
- 支持多个并发客户端;
- 可作为 Web 服务部署;
- 支持服务器到客户端的通知(server-to-client notifications)。
stdio——适合本地集成、命令行工具:
- 标准输入/输出流通信;
- 设置简单,无需网络配置;
- 作为客户端的子进程运行;
- 重要:stdio 服务器不应向 stdout 打日志(日志请走 stderr)。
文档还明确指出:避免使用 SSE(已弃用,被 Streamable HTTP 取代)。
6.2 选型对照表
| 评判维度 | stdio | Streamable HTTP |
|---|---|---|
| 部署方式 | 本地(Local) | 远程(Remote) |
| 客户端 | 单个(Single) | 多个(Multiple) |
| 复杂度 | 低(Low) | 中等(Medium) |
| 实时性 | 无(No) | 有(Yes) |
6.3 DeepCode 中的传输契约与真实示例
DeepCode 的服务器定义模型(core/mcp/models.py 的McpServerDefinition)将传输类型限定为type: Literal["stdio", "sse", "streamableHttp"],并通过_transport_contract模型校验器强制执行互斥契约:
stdio服务器必须有command,且不得定义任何 HTTP 字段(url、headers、bearerTokenEnvVar等);- HTTP 类服务器(sse/streamableHttp)必须有合法的
http/httpsURL,且不得定义 stdio 进程字段(command、args、cwd、env等); required(必需)服务器不能deferLoading(延迟加载);- 传输类型不会从 URL 后缀或命令存在与否推断,必须显式声明。
内置预设文件 core/mcp/presets.json 给出了真实世界的选型样例:
{ "id": "browserbase", "server": { "type": "streamableHttp", "url": "https://mcp.browserbase.com/mcp", "envUrlParams": {"browserbaseApiKey": "BROWSERBASE_API_KEY"}, "toolTimeoutSeconds": 60, "approvalMode": "writes" } }, { "id": "playwright", "server": { "type": "stdio", "command": "npx", "args": ["-y", "@playwright/mcp@latest"], "toolTimeoutSeconds": 60, "approvalMode": "writes" } }可以看到:云端托管的 Browserbase 用streamableHttp远程接入,本地的 Playwright 用stdio拉起子进程——与最佳实践文档的选型标准完全一致。
七、安全最佳实践:认证、校验与防护
7.1 认证与授权
OAuth 2.1:
- 使用来自权威机构签发的证书进行安全的 OAuth 2.1;
- 处理请求前先校验访问令牌;
- 只接受明确面向你服务器签发的令牌。
API Keys:
- API Key 存于环境变量,绝不写死在代码里;
- 服务器启动时即校验 Key 的有效性;
- 认证失败时给出清晰的错误消息。
DeepCode 对此提供了更强的"存储边界"约束:core/mcp/models.py 的validate_no_literal_secrets()会检测env与headers中任何看起来敏感的名称(匹配authorization、cookie、api_key、password、secret、private_key等模式),一旦发现即抛出McpConfigurationError,强制改用envVars(转发环境变量)、credentialEnv(引用用户连接的凭据,格式provider:<connection-id>)或bearerTokenCredential。同时 core/mcp/runtime.py 的McpSessionRuntime注释强调:凭据只在连接启动前一刻解析,绝不进入清单、诊断信息或序列化后的运行时计划。
7.2 输入校验
- 对文件路径做清洗,防止目录遍历(directory traversal);
- 校验 URL 与外部标识符;
- 检查参数大小与取值范围;
- 在系统调用中防止命令注入;
- 对所有输入使用schema 校验(Pydantic / Zod)。
Python 侧推荐 Pydantic v2 写法:model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, extra='forbid')+Field(..., min_length=1, max_length=100)约束 +@field_validator自定义校验。TypeScript 侧用 Zod:.min(2, "Query must be at least 2 characters")、.int()、.max(100),并用.strict()禁止多余字段。DeepCode 的McpServerDefinition对 header 名、环境变量名、URL 参数名也都有正则校验,防止注入类攻击。
7.3 错误处理
- 不向客户端暴露内部错误;
- 安全相关的错误在服务端记录日志;
- 提供有用但不泄露细节的错误消息;
- 出错后正确清理资源。
7.4 DNS Rebinding 防护(本地 Streamable HTTP)
对于本地运行的 Streamable HTTP 服务器:
- 启用 DNS rebinding 防护;
- 校验所有入站连接的
Origin头; - 绑定
127.0.0.1而非0.0.0.0。
八、工具注解(Tool Annotations):hint 而非安全保证
最佳实践文档给出了四个注解的语义表:
| 注解 | 类型 | 默认值 | 含义 |
|---|---|---|---|
readOnlyHint | boolean | false | 工具不修改其环境 |
destructiveHint | boolean | true | 工具可能执行破坏性更新 |
idempotentHint | boolean | false | 相同参数重复调用无额外效果 |
openWorldHint | boolean | true | 工具与外部实体交互 |
文档特别强调:注解是提示(hints),不是安全保证(guarantees)。客户端不应仅凭注解做安全关键决策。
这一原则在 DeepCode 中得到了教科书式的落实。见 core/mcp/tools.py 的McpToolAdapter.read_only属性:
@property def read_only(self) -> bool: # MCP annotations are hints, not grants. Unknown is deliberately # mutating so default/writes policies fail toward confirmation. return self.annotations.read_only代码注释直译即为"注解是提示,不是授权"。当注解缺失时,DeepCode 故意按"可写"(mutating)处理,让默认/写策略向需要确认的方向失败(fail toward confirmation),即宁可让写操作触发人工审批,也不因缺失注解而放行。注解通过 core/mcp/models.py 的McpToolAnnotations.from_sdk()从 SDK 定义解析,并会以approvalMode(auto/prompt/writes/approve)叠加到全局权限之上,形成 MCP 专属的审批层级。
九、错误处理与错误消息设计
最佳实践文档要求:
- 使用标准 JSON-RPC 错误码;
- 工具错误在 result 对象内上报(而非协议层错误);
- 提供有帮助、具体、带下一步建议的错误消息;
- 不暴露内部实现细节;
- 出错时妥善清理资源。
文档给出的 TypeScript 错误处理示例(工具内部捕获并返回isError):
try { const result = performOperation(); return { content: [{ type: "text", text: result }] }; } catch (error) { return { isError: true, content: [{ type: "text", text: `Error: ${error.message}. Try using filter='active_only' to reduce results.` }] }; }注意两个细节:一是错误通过isError: true放在结果对象中返回,而不是中断 JSON-RPC 协议;二是错误消息末尾附上了可执行的下一步建议(Try using filter='active_only'...)——这正是"Actionable Error Messages"原则的体现(SKILL.md 阶段 1.1)。
Python 指南进一步示范了按 HTTP 状态码分类的错误映射:404→"请检查 ID 是否正确"、403→"权限不足"、429→"触发限流,请稍后重试"、超时→"请重试"。DeepCode 消费端在 core/mcp/tools.py 的execute()中会读取result.isError并完整透传文本、附带approvalMode等元数据,同时通过log_mcp_call记录调用遥测(观测失败不影响工具结果)。
十、测试要求:五类测试全覆盖
最佳实践文档要求全面的测试覆盖:
- 功能测试(Functional):验证有效/无效输入下的正确执行;
- 集成测试(Integration):测试与外部系统的交互;
- 安全测试(Security):校验认证、输入清洗、限流;
- 性能测试(Performance):检查负载与超时下的行为;
- 错误处理(Error Handling):确保错误上报与资源清理正确。
语言专属指南给出的构建与冒烟方法:
# TypeScript:先构建再测试,用 MCP Inspector 交互调试 npm run build npx @modelcontextprotocol/inspector # Python:语法校验 + MCP Inspector python -m py_compile your_server.py npx @modelcontextprotocol/inspector在 DeepCode 仓库中,MCP 运行时自身的测试覆盖了 test_mcp_runtime.py、test_mcp_runtime_lazy.py(延迟加载/按需激活)、test_mcp_oauth.py(OAuth 流程)等,可作为测试方法的参照样例。
十一、文档要求:让每个工具都可被发现、被信任
最佳实践文档对服务器作者提出的文档义务:
- 清晰记录所有工具与能力;
- 每个主要功能至少 3 个可工作的示例;
- 记录安全注意事项;
- 说明所需的权限与访问级别;
- 记录限流(rate limits)与性能特征。
在 DeepCode 的消费端,工具描述会被截断至 8000 字符展示给模型(core/mcp/tools.py),服务器的 instructions 也有截断上限(server_instruction4000 字符、instruction_context8000 字符,见 core/mcp/runtime.py)——因此"描述要精炼、要点前置"不只是审美问题,而是直接影响模型可读性的工程约束。
十二、配套:用评估闭环检验 MCP 服务器质量
虽然评估细则在独立的 reference/evaluation.md 中,但它与本文档的"Testing Requirements"构成闭环。其核心理念值得在此强调:MCP 服务器的质量不以"实现了多少工具"衡量,而以"只给工具、不给其他上下文的情况下,LLM 能否借此回答真实且困难的问题"衡量。评估要求创建 10 道独立、只读、不可破坏、答案可字符串比对且随时间稳定(基于历史数据)的问题,并通过scripts/evaluation.py(支持-t stdio/sse/http、-m指定模型、-o输出报告)运行得到准确率、平均耗时、平均工具调用数等指标。
结语
本文档所承载的规范——从{service}_{action}_{resource}命名、JSON/Markdown 双格式、分页元数据,到传输选型、安全加固、注解语义与测试/文档义务——构成了一个语言无关的"MCP 服务器质量底线"。在 DeepCode 中,这些规范既有 SKILL.md 层面的流程支撑,又有 core/mcp 运行时层面的强制落实(工具前缀化命名、注解按 hint 而非授权处理、凭据不入配置字面量、传输契约互斥校验),还有 core/mcp/presets.json 的真实预设作为参照。无论你是要编写一个全新的 MCP 服务器,还是要在 DeepCode 中接入第三方 MCP 服务,本文给出的规范与实现对照都可以直接作为设计评审清单使用。
【免费下载链接】DeepCode"DeepCode: Open Agentic Coding (Agent Harness & Loop Engineering & Multi-Agent Orchestration)"项目地址: https://gitcode.com/GitHub_Trending/deepc/DeepCode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考