6.7 KiB
6.7 KiB
MCP 服务器最佳实践
快速参考
服务器命名
- Python:
{service}_mcp(例如slack_mcp) - Node/TypeScript:
{service}-mcp-server(例如slack-mcp-server)
工具命名
- 使用蛇形命名法,并带服务前缀
- 格式:
{service}_{action}_{resource} - 示例:
slack_send_message、github_create_issue
响应格式
- 同时支持 JSON 和 Markdown 两种格式
- JSON 用于程序化处理
- Markdown 用于人类可读
分页
- 始终尊重
limit参数 - 返回
has_more、next_offset、total_count - 默认每页 20–50 条
传输方式
- Streamable HTTP:适用于远程服务器、多客户端场景
- stdio:适用于本地集成、命令行工具
- 避免使用 SSE(已弃用,推荐使用 streamable HTTP)
服务器命名约定
遵循以下标准化命名模式:
Python:使用格式 {service}_mcp(小写加下划线)
- 示例:
slack_mcp、github_mcp、jira_mcp
Node/TypeScript:使用格式 {service}-mcp-server(小写加连字符)
- 示例:
slack-mcp-server、github-mcp-server、jira-mcp-server
名称应通用、能够描述所集成的服务、易于从任务描述中推断,且不带版本号。
工具命名与设计
工具命名
- 使用蛇形命名法:
search_users、create_project、get_channel_info - 包含服务前缀:预见到你的 MCP 服务器可能与其他 MCP 服务器一起使用
- 使用
slack_send_message,而非仅用send_message - 使用
github_create_issue,而非仅用create_issue
- 使用
- 以动作导向:以动词开头(get、list、search、create 等)
- 保持具体:避免可能与其他服务器冲突的通用名称
工具设计
- 工具描述必须精确且无歧义地描述功能
- 描述必须与实际功能完全匹配
- 提供工具注解(readOnlyHint、destructiveHint、idempotentHint、openWorldHint)
- 保持工具操作聚焦且原子化
响应格式
所有返回数据的工具都应支持多种格式:
JSON 格式(response_format="json")
- 机器可读的结构化数据
- 包含所有可用字段和元数据
- 字段名和类型保持一致
- 用于程序化处理
Markdown 格式(response_format="markdown",通常为默认值)
- 人类可读的格式化文本
- 使用标题、列表和格式以提高清晰度
- 将时间戳转换为人类可读格式
- 显示名称的同时在括号中附上 ID
- 省略冗长的元数据
分页
对于列出资源的工具:
- 始终尊重
limit参数 - 实现分页:使用
offset或基于游标的分页 - 返回分页元数据:包括
has_more、next_offset/next_cursor、total_count - 切勿将所有结果加载到内存中:对于大数据集尤其重要
- 默认设置合理的限制:通常为 20–50 条
分页响应示例:
{
"total": 150,
"count": 20,
"offset": 0,
"items": [...],
"has_more": true,
"next_offset": 20
}
传输方式选项
Streamable HTTP
最适合:远程服务器、Web 服务、多客户端场景
特点:
- 基于 HTTP 的双向通信
- 支持多个同时连接的客户端
- 可部署为 Web 服务
- 支持服务器到客户端的通知
使用场景:
- 同时服务多个客户端
- 部署为云服务
- 与 Web 应用集成
stdio
最适合:本地集成、命令行工具
特点:
- 标准输入/输出流通信
- 设置简单,无需网络配置
- 作为客户端的子进程运行
使用场景:
- 为本地开发环境构建工具
- 与桌面应用集成
- 单用户、单会话场景
注意:stdio 服务器不应将日志输出到 stdout(请使用 stderr 记录日志)
传输方式选择
| 标准 | stdio | Streamable HTTP |
|---|---|---|
| 部署 | 本地 | 远程 |
| 客户端 | 单个 | 多个 |
| 复杂度 | 低 | 中 |
| 实时性 | 否 | 是 |
安全最佳实践
认证与授权
OAuth 2.1:
- 使用带有受认可机构证书的安全 OAuth 2.1
- 在处理请求前验证访问令牌
- 仅接受专门针对你的服务器的令牌
API 密钥:
- 将 API 密钥存储在环境变量中,切勿写在代码里
- 在服务器启动时验证密钥
- 认证失败时提供清晰的错误消息
输入验证
- 对文件路径进行清理以防止目录遍历攻击
- 验证 URL 和外部标识符
- 检查参数的大小和范围
- 防止系统调用中的命令注入
- 对所有输入使用模式验证(Pydantic/Zod)
错误处理
- 不要向客户端暴露内部错误
- 在服务端记录安全相关的错误
- 提供有帮助但不泄露内部信息的错误消息
- 错误发生后正确清理资源
DNS 重绑定保护
对于本地运行的 streamable HTTP 服务器:
- 启用 DNS 重绑定保护
- 验证所有传入连接的
Origin头 - 绑定到
127.0.0.1而非0.0.0.0
工具注解
提供注解以帮助客户端理解工具行为:
| 注解 | 类型 | 默认值 | 描述 |
|---|---|---|---|
readOnlyHint |
boolean | false | 工具不会修改其运行环境 |
destructiveHint |
boolean | true | 工具可能执行破坏性更新 |
idempotentHint |
boolean | false | 使用相同参数的重复调用不会产生额外影响 |
openWorldHint |
boolean | true | 工具与外部实体交互 |
重要:注解是提示,而非安全保证。客户端不应仅基于注解做出安全关键决策。
错误处理
- 使用标准 JSON-RPC 错误码
- 在结果对象内报告工具错误(而非协议级别的错误)
- 提供有帮助、具体的错误消息,并附带建议的下一步操作
- 不要暴露内部实现细节
- 在发生错误时正确清理资源
错误处理示例:
try {
const result = performOperation();
return { content: [{ type: "text", text: result }] };
} catch (error) {
return {
isError: true,
content: [{
type: "text",
text: `错误:${error.message}。请尝试使用 filter='active_only' 来减少结果数量。`
}]
};
}
测试要求
全面的测试应涵盖:
- 功能测试:验证在有效/无效输入下的正确执行
- 集成测试:测试与外部系统的交互
- 安全测试:验证认证、输入清理、速率限制
- 性能测试:检查在负载和超时情况下的表现
- 错误处理:确保正确的错误报告和清理
文档要求
- 提供所有工具和功能的清晰文档
- 包含可运行的示例(每个主要特性至少 3 个)
- 记录安全注意事项
- 说明所需的权限和访问级别
- 记录速率限制和性能特征