skillhub-013-mcp-builder
250 行
6.7 KiB
Markdown
250 行
6.7 KiB
Markdown
# 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`
|
||
|
||
名称应通用、能够描述所集成的服务、易于从任务描述中推断,且不带版本号。
|
||
|
||
---
|
||
|
||
## 工具命名与设计
|
||
|
||
### 工具命名
|
||
|
||
1. **使用蛇形命名法**:`search_users`、`create_project`、`get_channel_info`
|
||
2. **包含服务前缀**:预见到你的 MCP 服务器可能与其他 MCP 服务器一起使用
|
||
- 使用 `slack_send_message`,而非仅用 `send_message`
|
||
- 使用 `github_create_issue`,而非仅用 `create_issue`
|
||
3. **以动作导向**:以动词开头(get、list、search、create 等)
|
||
4. **保持具体**:避免可能与其他服务器冲突的通用名称
|
||
|
||
### 工具设计
|
||
|
||
- 工具描述必须精确且无歧义地描述功能
|
||
- 描述必须与实际功能完全匹配
|
||
- 提供工具注解(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 条
|
||
|
||
分页响应示例:
|
||
```json
|
||
{
|
||
"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 错误码
|
||
- 在结果对象内报告工具错误(而非协议级别的错误)
|
||
- 提供有帮助、具体的错误消息,并附带建议的下一步操作
|
||
- 不要暴露内部实现细节
|
||
- 在发生错误时正确清理资源
|
||
|
||
错误处理示例:
|
||
```typescript
|
||
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 个)
|
||
- 记录安全注意事项
|
||
- 说明所需的权限和访问级别
|
||
- 记录速率限制和性能特征
|