提示词场景库提示词场景库提示词场景库
提示词分类发现
提示词场景库·2026
关于/免责隐私条款
F

Fatih Kadir Akın

@f

587提示词
0个机器人
0个团队
0收到的点赞
0贡献
加入时间 1 天前
587 贡献 过去一年
11月
12月
1月
2月
3月
4月
5月
6月
7月
8月
9月
10月
11月
M
W
F
少
多
视频
超写实美食视频创作者

担任专业美食摄像师。你的任务是制作适用于菜单、广告和宣传材料的超写实美食视频。确保高质量视觉效果,具备逼真的质感、诱人的构图和完美的灯光。

担任专业美食摄像师。你是制作引人入胜且令人垂涎的美食视频的专家。

你的任务是制作一段超写实美食视频,重点关注:
- 逼真的质感
- 诱人的构图
- 完美的灯光条件

你将:
- 以一丝不苟的细节捕捉每道菜品
- 突出食物的视觉吸引力
- 确保视频适用于advertisements或menus

约束条件:
- 保持商业风格
- 使用动态照片技术打造动态视觉效果
MarketingContent Creation
F@f
0
暗黑合成器合成波音乐创作指南
音频

一份用于创作暗黑合成器合成波音乐的详细结构与风格指南,融合氛围感与怀旧元素。非常适合打造具有推进感、层次化制作和1980年代复古氛围的音乐。

风格:暗黑合成器合成波,融合电子与氛围元素,怀旧、神秘、充满希望、能量递进,108 BPM,中速,推进感,合成器,电吉他,以合成器为特色,男声与气声人声,精致,氛围感,层次化制作,1980年代音色,丰富而具有电影感并带有模拟温暖感,A小调,复古波,outrun,80年代怀旧,霓虹,夜间驾驶

结构:
[前奏] 氛围合成器铺底淡入
[主歌] 推进感节拍与人声
[预副歌] 张力递进
[副歌] 完整编曲,高亢旋律
[主歌] 第二段主歌,加入更多元素
[副歌] 重复副歌并带有变化
[桥段] 分解段,精简编排
[高潮] 最终副歌,额外能量
[尾奏] 随混响尾音淡出

歌词:
主题:对一座从未存在过的霓虹之城的回忆
F@f
0
城市中的男人
图片
城市中的男人

一张以城市中的男人为主题的照片。

创建一张捕捉男人在Istanbul的照片,使用以下可自定义变量:

- **地点**:包括标志性的伊斯坦布尔地点,如Galata Tower、Blue Mosque或Bosphorus。
- **一天中的时间**:在sunrise、noon或sunset期间捕捉场景,以营造不同的氛围情绪。
- **着装**:让男人穿着casual、business或traditional服装,以体现不同风格。
- **活动**:男人可以walking、sitting或looking out over the city,以传达不同的叙事。

使用这些变量来打造一个独特的摄影场景,展现Istanbul充满活力的文化和多元氛围。
TravelArtCreative Writing
F@f
0
Sentry 缺陷修复器
文本

从 Sentry 错误跟踪报告中识别并修复缺陷,确保应用程序平稳运行。

担任 Sentry 缺陷修复器。你是使用 Sentry 错误跟踪进行软件问题调试和解决的专家。
你的任务是通过识别并修复 Sentry 报告的缺陷,确保应用程序平稳运行。
你将:
- 分析 Sentry 报告以理解错误
- 根据影响程度对缺陷进行优先级排序
- 实施解决方案以修复已识别的缺陷
- 测试应用程序以确认修复效果
- 记录所做的更改并与开发团队沟通
规则:
- 在进行更改之前始终备份当前状态
- 遵循编码标准和最佳实践
- 在部署前彻底验证解决方案
- 与团队成员保持清晰沟通
变量:
- projectName - 你正在处理的项目名称
- high - 缺陷的严重程度
- production - 缺陷发生的环境
DebuggingCommunication
F@f
0
MCP 构建器
技能

用于创建高质量 MCP(模型上下文协议)服务器的指南,使 LLM 能够通过精心设计的工具与外部服务交互。适用于构建 MCP 服务器以集成外部 API 或服务时使用,无论是 Python(FastMCP)还是 Node/TypeScript(MCP SDK)。

---
name: mcp-builder
description: 创建高质量 MCP(模型上下文协议)服务器的指南,使 LLM 能够通过精心设计的工具与外部服务交互。在构建 MCP 服务器以集成外部 API 或服务时使用,无论是 Python(FastMCP)还是 Node/TypeScript(MCP SDK)。
license: 完整条款见 LICENSE.txt
---

# MCP 服务器开发指南

## 概述

创建 MCP(模型上下文协议)服务器,使 LLM 能够通过精心设计的工具与外部服务交互。MCP 服务器的质量以其帮助 LLM 完成现实世界任务的程度来衡量。

---

# 流程

## 🚀 高层工作流

创建高质量的 MCP 服务器涉及四个主要阶段:

### 阶段 1:深入研究和规划

#### 1.1 理解现代 MCP 设计

**API 覆盖 vs. 工作流工具:**
在全面的 API 端点覆盖与专门的工作流工具之间取得平衡。工作流工具对于特定任务可能更方便,而全面的覆盖则赋予代理组合操作的灵活性。性能因客户端而异——一些客户端受益于结合基本工具的代码执行,而另一些则更适合使用更高层的工作流。当不确定时,优先考虑全面的 API 覆盖。

**工具命名和可发现性:**
清晰、描述性的工具名称有助于代理快速找到正确的工具。使用一致的前缀(例如 `github_create_issue`、`github_list_repos`)和面向操作的命名。

**上下文管理:**
代理受益于简洁的工具描述以及过滤/分页结果的能力。设计返回聚焦、相关数据的工具。一些客户端支持代码执行,这可以帮助代理高效地过滤和处理数据。

**可操作的错误消息:**
错误消息应通过具体的建议和后续步骤引导代理找到解决方案。

#### 1.2 研究 MCP 协议文档

**浏览 MCP 规范:**

从站点地图开始查找相关页面:`https://modelcontextprotocol.io/sitemap.xml`

然后使用 `.md` 后缀获取特定页面的 markdown 格式(例如 `https://modelcontextprotocol.io/specification/draft.md`)。

需要查看的关键页面:
- 规范概述和架构
- 传输机制(可流式 HTTP、stdio)
- 工具、资源和提示定义

#### 1.3 研究框架文档

**推荐技术栈:**
- **语言**:TypeScript(高质量的 SDK 支持,在许多执行环境(如 MCPB)中具有良好的兼容性。此外,AI 模型擅长生成 TypeScript 代码,得益于其广泛使用、静态类型和良好的 linting 工具)
- **传输**:远程服务器使用可流式 HTTP,采用无状态 JSON(与有状态会话和流式响应相比,更易于扩展和维护)。本地服务器使用 stdio。

**加载框架文档:**

- **MCP 最佳实践**:[📋 查看最佳实践](./reference/mcp_best_practices.md) - 核心指南

**对于 TypeScript(推荐):**
- **TypeScript SDK**:使用 WebFetch 加载 `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`
- [⚡ TypeScript 指南](./reference/node_mcp_server.md) - TypeScript 模式和示例

**对于 Python:**
- **Python SDK**:使用 WebFetch 加载 `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`
- [🐍 Python 指南](./reference/python_mcp_server.md) - Python 模式和示例

#### 1.4 规划你的实现

**理解 API:**
查看服务的 API 文档,以识别关键端点、认证要求和数据模型。根据需要利用网络搜索和 WebFetch。

**工具选择:**
优先考虑全面的 API 覆盖。列出要实现的端点,从最常见的操作开始。

---

### 阶段 2:实现

#### 2.1 设置项目结构

请参阅特定语言的指南进行项目设置:
- [⚡ TypeScript 指南](./reference/node_mcp_server.md) - 项目结构、package.json、tsconfig.json
- [🐍 Python 指南](./reference/python_mcp_server.md) - 模块组织、依赖项

#### 2.2 实现核心基础设施

创建共享工具:
- 带认证的 API 客户端
- 错误处理辅助函数
- 响应格式化(JSON/Markdown)
- 分页支持

#### 2.3 实现工具

对于每个工具:

**输入模式:**
- 使用 Zod(TypeScript)或 Pydantic(Python)
- 包含约束和清晰的描述
- 在字段描述中添加示例

**输出模式:**
- 尽可能为结构化数据定义 `outputSchema`
- 在工具响应中使用 `structuredContent`(TypeScript SDK 特性)
- 帮助客户端理解和处理工具输出

**工具描述:**
- 功能的简洁摘要
- 参数描述
- 返回类型模式

**实现:**
- 对 I/O 操作使用异步/等待
- 使用可操作的消息进行适当的错误处理
- 在适用的情况下支持分页
- 使用现代 SDK 时同时返回文本内容和结构化数据

**注解:**
- `readOnlyHint`:true/false
- `destructiveHint`:true/false
- `idempotentHint`:true/false
- `openWorldHint`:true/false

---

### 阶段 3:审查和测试

#### 3.1 代码质量

审查:
- 无重复代码(DRY 原则)
- 一致的错误处理
- 完整的类型覆盖
- 清晰的工具描述

#### 3.2 构建和测试

**TypeScript:**
- 运行 `npm run build` 验证编译
- 使用 MCP Inspector 测试:`npx @modelcontextprotocol/inspector`

**Python:**
- 验证语法:`python -m py_compile your_server.py`
- 使用 MCP Inspector 测试

请参阅特定语言的指南了解详细的测试方法和质量检查清单。

---

### 阶段 4:创建评估

实现 MCP 服务器后,创建全面的评估以测试其有效性。

**加载 [✅ 评估指南](./reference/evaluation.md) 获取完整的评估指南。**

#### 4.1 理解评估目的

使用评估来测试 LLM 是否能有效地使用你的 MCP 服务器回答现实、复杂的问题。

#### 4.2 创建 10 个评估问题

要创建有效的评估,请遵循评估指南中概述的流程:

1. **工具检查**:列出可用工具并了解其能力
2. **内容探索**:使用只读操作探索可用数据
3. **问题生成**:创建 10 个复杂、现实的问题
4. **答案验证**:自己解决每个问题以验证答案

#### 4.3 评估要求

确保每个问题:
- **独立**:不依赖于其他问题
- **只读**:仅需要非破坏性操作
- **复杂**:需要多次工具调用和深入探索
- **现实**:基于人类关心的真实用例
- **可验证**:单一、清晰的答案,可通过字符串比较验证
- **稳定**:答案不会随时间变化

#### 4.4 输出格式

创建具有以下结构的 XML 文件:

```xml
<evaluation>
  <qa_pair>
    <question>查找讨论 ab推出以动物为代号的 AI 模型。其中一个模型需要一个特定的安全标识,该标识使用 ASL-X 格式。对于以斑点野生猫命名的模型,正在确定哪个数字 X?</question>
    <answer>3</answer>
  </qa_pair>
<!-- 更多 qa_pairs... -->
</evaluation>
```

---

# 参考文件

## 📚 文档库

在开发过程中按需加载这些资源:

### 核心 MCP 文档(优先加载)
- **MCP 协议**:从 `https://modelcontextprotocol.io/sitemap.xml` 的站点地图开始,然后获取带有 `.md` 后缀的特定页面
- [📋 MCP 最佳实践](./reference/mcp_best_practices.md) - 通用 MCP 指南,包括:
  - 服务器和工具命名约定
  - 响应格式指南(JSON 与 Markdown)
  - 分页最佳实践
  - 传输选择(可流式 HTTP 与 stdio)
  - 安全和错误处理标准

### SDK 文档(在第 1/2 阶段加载)
- **Python SDK**:从 `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md` 获取
- **TypeScript SDK**:从 `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md` 获取

### 特定语言的实现指南(在第 2 阶段加载)
- [🐍 Python 实现指南](./reference/python_mcp_server.md) - 完整的 Python/FastMCP 指南,包括:
  - 服务器初始化模式
  - Pydantic 模型示例
  - 使用 `@mcp.tool` 注册工具
  - 完整可运行示例
  - 质量检查清单

- [⚡ TypeScript 实现指南](./reference/node_mcp_server.md) - 完整的 TypeScript 指南,包括:
  - 项目结构
  - Zod 模式模式
  - 使用 `server.registerTool` 注册工具
  - 完整可运行示例
  - 质量检查清单

### 评估指南(在第 4 阶段加载)
- [✅ 评估指南](./reference/evaluation.md) - 完整的评估创建指南,包括:
  - 问题创建指南
  - 答案验证策略
  - XML 格式规范
  - 示例问题和答案
  - 使用提供的脚本运行评估
FILE:reference/mcp_best_practices.md
# MCP 服务器最佳实践

## 快速参考

### 服务器命名
- **Python**:`{service}_mcp`(例如 `slack_mcp`)
- **Node/TypeScript**:`{service}-mcp-server`(例如 `slack-mcp-server`)

### 工具命名
- 使用带服务前缀的 snake_case
- 格式:`{service}_{action}_{resource}`
- 示例:`slack_send_message`、`github_create_issue`

### 响应格式
- 同时支持 JSON 和 Markdown 格式
- JSON 用于程序化处理
- Markdown 用于人类可读性

### 分页
- 始终遵守 `limit` 参数
- 返回 `has_more`、`next_offset`、`total_count`
- 默认 20-50 项

### 传输
- **可流式 HTTP**:用于远程服务器、多客户端场景
- **stdio**:用于本地集成、命令行工具
- 避免 SSE(已弃用,改用可流式 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. **使用 snake_case**:`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
}
```

---

## 传输选项

### 可流式 HTTP

**最适合**:远程服务器、Web 服务、多客户端场景

**特性**:
- 通过 HTTP 进行双向通信
- 支持多个同时连接的客户端
- 可作为 Web 服务部署
- 支持服务器到客户端通知

**使用场景**:
- 同时为多个客户端提供服务
- 作为云服务部署
- 与 Web 应用程序集成

### stdio

**最适合**:本地集成、命令行工具

**特性**:
- 标准输入/输出流通信
- 设置简单,无需网络配置
- 作为客户端的子进程运行

**使用场景**:
- 为本地开发环境构建工具
- 与桌面应用程序集成
- 单用户、单会话场景

**注意**:stdio 服务器不应记录到 stdout(使用 stderr 进行日志记录)

### 传输选择

| 标准 | stdio | 可流式 HTTP |
|-----------|-------|-----------------|
| **部署** | 本地 | 远程 |
| **客户端** | 单个 | 多个 |
| **复杂性** | 低 | 中 |
| **实时** | 否 | 是 |

---

## 安全最佳实践

### 身份验证和授权

**OAuth 2.1**:
- 使用来自公认机构的证书的安全 OAuth 2.1
- 在处理请求之前验证访问令牌
- 仅接受专门用于你的服务器的令牌

**API 密钥**:
- 将 API 密钥存储在环境变量中,切勿存储在代码中
- 在服务器启动时验证密钥
- 身份验证失败时提供清晰的错误消息

### 输入验证

- 清理文件路径以防止目录遍历
- 验证 URL 和外部标识符
- 检查参数大小和范围
- 防止系统调用中的命令注入
- 使用 对所有输入进行模式验证(Pydantic/Zod)

### 错误处理

- 不要向客户端暴露内部错误
- 在服务端记录与安全相关的错误
- 提供有帮助但不泄露信息的错误消息
- 在错误发生后清理资源

### DNS 重绑定防护

对于在本地运行的可流式 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: error.message. Try using filter='active_only' to reduce results.`
    }]
  };
}
```

---

## 测试要求

全面的测试应覆盖:

- **功能测试**:验证在有效/无效输入下的正确执行
- **集成测试**:测试与外部系统的交互
- **安全测试**:验证认证、输入清理、速率限制
- **性能测试**:检查负载下的行为、超时
- **错误处理**:确保正确的错误报告和清理

---

## 文档要求

- 为所有工具和功能提供清晰的文档
- 包含可运行的示例(每个主要功能至少 3 个)
- 记录安全注意事项
- 说明所需的权限和访问级别
- 记录速率限制和性能特征
FILE:reference/evaluation.md
# MCP 服务器评估指南

## 概述

本文档提供关于为 MCP 服务器创建全面评估的指导。评估测试 LLM 能否仅使用所提供的工具,有效地使用你的 MCP 服务器来回答现实、复杂的问题。

---

## 快速参考

### 评估要求
- 创建 10 个人类可读的问题
- 问题必须是只读的、独立的、非破坏性的
- 每个问题需要多次工具调用(可能数十次)
- 答案必须是单一、可验证的值
- 答案必须是稳定的(不会随时间变化)

### 输出格式
```xml
<evaluation>
   <qa_pair>
      <question>你的问题写在这里</question>
      <answer>单一可验证的答案</answer>
   </qa_pair>
</evaluation>
```

---

## 评估的目的

衡量 MCP 服务器质量的标准不是服务器实现工具的程度或全面性,而是这些实现(输入/输出模式、文档字符串/描述、功能)在 LLM 没有其他上下文且仅能访问 MCP 服务器的情况下,能在多大程度上帮助 LLM 回答现实且困难的问题。

## 评估概述

创建 10 个人类可读的问题,仅需要只读、独立、非破坏性和幂等的操作来回答。每个问题应:
- 现实
- 清晰简洁
- 无歧义
- 复杂,可能需要数十次工具调用或步骤
- 能用你事先确定的单一、可验证的值来回答

## 问题指南

### 核心要求

1. **问题必须是独立的**
   - 每个问题不应依赖于任何其他问题的答案
   - 不应假设处理另一个问题时进行了先前的写操作

2. **问题必须仅需要非破坏性和幂等的工具使用**
   - 不应指示或要求修改状态才能得出正确答案

3. **问题必须是现实的、清晰的、简洁的和复杂的**
   - 必须要求另一个 LLM 使用多个(可能数十个)工具或步骤来回答

### 复杂性和深度

4. **问题必须要求深入探索**
   - 考虑需要多个子问题和顺序工具调用的多跳问题
   - 每一步都应受益于前面问题中发现的信息

5. **问题可能需要大量分页**
   - 可能需要翻阅多页结果
   - 可能需要查询旧数据(1-2 年前的)以找到小众信息
   - 问题必须是困难的

6. **问题必须要求深入理解**
   - 而非表面知识
   - 可以提出需要证据的复杂真/假问题
   - 可以使用多项选择格式,让 LLM 必须搜索不同的假设

7. **问题必须不能通过直接的关键词搜索解决**
   - 不要包含目标内容中的特定关键词
   - 使用同义词、相关概念或转述
   - 要求多次搜索、分析多个相关项、提取上下文,然后推导出答案

### 工具测试

8. **问题应对工具返回值进行压力测试**
   - 可能引出工具返回大型 JSON 对象或列表,使 LLM 不堪重负
   - 应要求理解多种数据模态:
     - ID 和名称
     - 时间戳和日期时间(月、日、年、秒)
     - 文件 ID、名称、扩展名和 MIME 类型
     - URL、GID 等
   - 应探查工具返回所有有用数据形式的能力

9. **问题应主要反映真实的人类用例**
   - 在 LLM 辅助下,人类会关心的那类信息检索任务

10. **问题可能需要数十次工具调用**
    - 这对上下文有限的 LLM 构成挑战
    - 鼓励 MCP 服务器工具减少返回的信息

11. **包含模糊的问题**
    - 可能模糊或需要就调用哪些工具做出困难决定
    - 迫使 LLM 可能犯错或误解
    - 确保尽管存在模糊性,仍然有单一可验证的答案

### 稳定性

12. **问题的设计必须使答案不会改变**
    - 不要问依赖于动态"当前状态"的问题
    - 例如,不要统计:
      - 帖子的反应数
      - 线程的回复数
      - 频道的成员数

13. **不要让 MCP 服务器限制你创建的问题类型**
    - 创建具有挑战性和复杂的问题
    - 有些可能无法用可用的 MCP 服ver 工具
    - 问题可能要求特定的输出格式(datetime 与 epoch 时间、JSON 与 MARKDOWN)
    - 问题可能需要数十次工具调用才能完成

## 答案指南

### 可验证性

1. **答案必须能通过直接字符串比较进行验证**
   - 如果答案可以用多种格式重写,请在问题中明确指定输出格式
   - 示例:“使用 YYYY/MM/DD。”、“回答 True 或 False。”、“回答 A、B、C 或 D,不要包含其他内容。”
   - 答案应为单个可验证的值,例如:
     - 用户 ID、用户名、显示名称、名字、姓氏
     - 频道 ID、频道名称
     - 消息 ID、字符串
     - URL、标题
     - 数值数量
     - 时间戳、日期时间
     - 布尔值(用于 True/False 问题)
     - 电子邮件地址、电话号码
     - 文件 ID、文件名、文件扩展名
     - 多项选择答案
   - 答案不得要求特殊格式或复杂的结构化输出
   - 答案将使用直接字符串比较进行验证

### 可读性

2. **答案通常应优先采用人类可读的格式**
   - 示例:姓名、名字、姓氏、日期时间、文件名、消息字符串、URL、是/否、真/假、a/b/c/d
   - 而不是不透明的 ID(尽管 ID 也可接受)
   - 绝大多数答案应为人类可读的

### 稳定性

3. **答案必须是稳定/不变的**
   - 查看旧内容(例如已结束的对话、已发布的项目、已回答的问题)
   - 基于“封闭”概念创建问题,这些概念将始终返回相同的答案
   - 问题可以要求考虑固定的时间窗口,以避免非稳定的答案
   - 依赖不太可能改变的上下文
   - 示例:如果查找论文名称,要足够具体,以免答案与后来发表的论文混淆

4. **答案必须清晰且无歧义**
   - 问题的设计必须确保只有一个清晰明确的答案
   - 答案可以通过使用 MCP 服务器工具得出

### 多样性

5. **答案必须多样化**
   - 答案应为不同模态和格式的单个可验证值
   - 用户概念:用户 ID、用户名、显示名称、名字、姓氏、电子邮件地址、电话号码
   - 频道概念:频道 ID、频道名称、频道主题
   - 消息概念:消息 ID、消息字符串、时间戳、月、日、年

6. **答案不得是复杂结构**
   - 不是值列表
   - 不是复杂对象
   - 不是 ID 或字符串列表
   - 不是自然语言文本
   - 除非答案可以通过直接字符串比较直接验证
   - 并且可以真实地复现
   - LLM 不太可能以任何其他顺序或格式返回相同的列表

## 评估流程

### 步骤 1:文档检查

阅读目标 API 的文档以了解:
- 可用的端点和功能
- 如果存在歧义,从网络获取额外信息
- 尽可能并行执行此步骤
- 确保每个子代理仅检查来自文件系统或网络的文档

### 步骤 2:工具检查

列出 MCP 服务器中可用的工具:
- 直接检查 MCP 服务器
- 了解输入/输出模式、文档字符串和描述
- 在此阶段不要调用工具本身

### 步骤 3:加深理解

重复步骤 1 和 2,直到你有良好的理解:
- 多次迭代
- 思考你想创建的任务类型
- 完善你的理解
- 在任何阶段都不应阅读 MCP 服务器实现本身的代码
- 运用你的直觉和理解来创建合理、现实但极具挑战性的任务

### 步骤 4:只读内容检查

在理解 API 和工具后,使用 MCP 服务器工具:
- 仅使用只读和非破坏性操作检查内容
- 目标:识别特定内容(例如用户、频道、消息、项目、任务)以创建现实的问题
- 不应调用任何修改状态的工具
- 不会阅读 MCP 服务器实现本身的代码
- 将此步骤与各个子代理并行执行,进行独立探索
- 确保每个子代理仅执行只读、非破坏性和幂等操作
- 注意:某些工具可能返回大量数据,导致上下文耗尽
- 进行增量、小型且有针对性工具调用以进行探索
- 在所有工具调用请求中,使用 `limit` 参数限制结果(<10)
- 使用分页

### 步骤 5:任务生成

检查内容后,创建 10 个人类可读的问题:
- LLM 应能够使用 MCP 服务器回答这些问题
- 遵循上述所有问题和答案指南

## 输出格式

每个问答对由一个问题和答案组成。输出应为具有以下结构的 XML 文件:

```xml
<evaluation>
   <qa_pair>
      <question>Find the project created in Q2 2024 with the highest number of completed tasks. What is the project name?</question>
      <answer>Website Redesign</answer>
   </qa_pair>
   <qa_pair>
      <question>Search for issues labeled as "bug" that were closed in March 2024. Which user closed the most issues? Provide their username.</question>
      <answer>sarah_dev</answer>
   </qa_pair>
   <qa_pair>
      <question>Look for pull requests that modified files in the /api directory and were merged between January 1 and January 31, 2024. How many different contributors worked on these PRs?</question>
      <answer>7</answer>
   </qa_pair>
   <qa_pair>
      <question>Find the repository with the most stars that was created before 2023. What is the repository name?</question>
      <answer>data-pipeline</answer>
   </qa_pair>
</evaluation>
```

## 评估示例

### 好的问题

**示例 1:需要深度探索的多跳问题(GitHub MCP)**
```xml
<qa_pair>
   <question>Find the repository that was archived in Q3 2023 and had previously been the most forked project in the organization. What was the primary programming language used in that repository?</question>
   <answer>Python</answer>
</qa_pair>
```

这个问题很好,因为:
- 需要多次搜索才能找到已归档的仓库
- 需要确定哪个在归档前拥有最多的 fork
- 需要检查仓库详情以获取语言
- 答案是一个简单、可验证的值
- 基于不会改变的历史(封闭)数据

**示例 2:需要理解上下文而非关键词匹配(项目管理 MCP)**
```xml
<qa_pair>
   <question>Locate the initiative focused on improving customer onboarding that was completed in late 2023. The project lead created a retrospective document after completion. What was the lead's role title at that time?</question>
   <answer>Product Manager</answer>
</qa_pair>
```

这个问题很好,因为:
- 不使用具体的项目名称(“专注于改善客户入职的倡议”)
- 需要从特定时间范围内查找已完成的项目
- 需要识别项目负责人及其角色
- 需要从回顾文档中理解上下文
- 答案是人类可读且稳定的
- 基于已完成的工作(不会改变)

**示例 3:需要多个步骤的复杂聚合(问题跟踪器 MCP)**
```xml
<qa_pair>
   <question>在2024年1月报告的所有被标记为关键优先级的错误中,哪个负责人在其分配的bug中48小时内解决的百分比最高?提供该负责人的用户名。</question>
   <answer>alex_eng</answer>
</qa_pair>
```

这个问题很好,因为:
- 需要按日期、优先级和状态过滤bug
- 需要按负责人分组并计算解决率
- 需要理解时间戳以确定48小时窗口
- 测试分页(可能需要处理许多bug)
- 答案是单个用户名
- 基于特定时间段的历史数据

**示例 4:需要跨多种数据类型综合(CRM MCP)**
```xml
<qa_pair>
   <question>找到在2023年第四季度从Starter升级到Enterprise计划且年度合同价值最高的账户。该账户属于哪个行业?</question>
   <answer>Healthcare</answer>
</qa_pair>
```

这个问题很好,因为:
- 需要理解订阅层级变化
- 需要识别特定时间范围内的升级事件
- 需要比较合同价值
- 必须访问账户行业信息
- 答案简单且可验证
- 基于已完成的历史交易

### 差的问题

**示例 1:答案随时间变化**
```xml
<qa_pair>
   <question>当前有多少个未解决的问题分配给了工程团队?</question>
   <answer>47</answer>
</qa_pair>
```

这个问题很差,因为:
- 答案会随着问题的创建、关闭或重新分配而变化
- 不是基于稳定/静止的数据
- 依赖于“当前状态”,这是动态的

**示例 2:关键词搜索太简单**
```xml
<qa_pair>
   <question>找到标题为“添加身份验证功能”的拉取请求,并告诉我谁创建了它。</question>
   <answer>developer123</answer>
</qa_pair>
```

这个问题很差,因为:
- 可以通过直接关键词搜索精确标题来解决
- 不需要深入探索或理解
- 不需要综合或分析

**示例 3:答案格式模糊**
```xml
<qa_pair>
   <question>列出所有以Python为主要语言的仓库。</question>
   <answer>repo1, repo2, repo3, data-pipeline, ml-tools</answer>
</qa_pair>
```

这个问题很差,因为:
- 答案是一个列表,可能以任何顺序返回
- 难以通过直接字符串比较来验证
- LLM可能格式不同(JSON数组、逗号分隔、换行分隔)
- 最好询问特定的聚合(计数)或最高级(最多星标)

## 验证过程

创建评估后:

1. **检查XML文件**以了解模式
2. **加载每个任务指令**,并使用MCP服务器和工具并行地通过尝试自己解决任务来识别正确答案
3. **标记任何需要WRITE或DESTRUCTIVE操作的操作**
4. **累积所有正确答案**并替换文档中的任何错误答案
5. **移除任何需要WRITE或DESTRUCTIVE操作的`<qa_pair>`**

记住并行化解决任务以避免上下文耗尽,然后累积所有答案并在最后修改文件。

## 创建高质量评估的技巧

1. **在生成任务之前深入思考并提前计划**
2. **在有机会时并行化**以加速过程并管理上下文
3. **专注于现实用例**,即人们实际想要完成的事情
4. **创建具有挑战性的问题**,测试MCP服务器能力的极限
5. **通过使用历史数据和封闭概念确保稳定性**
6. **通过使用MCP服务器工具自己解决问题来验证答案**
7. **根据过程中所学进行迭代和改进**

---

# 运行评估

创建评估文件后,您可以使用提供的评估工具来测试您的MCP服务器。

## 设置

1. **安装依赖**

   ```bash
   pip install -r scripts/requirements.txt
   ```

   或手动安装:
   ```bash
   pip install anthropic mcp
   ```

2. **设置API密钥**

   ```bash
   export ANTHROPIC_API_KEY=your_api_key_here
   ```

## 评估文件格式

评估文件使用XML格式,包含`<qa_pair>`元素:

```xml
<evaluation>
   <qa_pair>
      <question>找到2024年第二季度创建的已完成任务数量最多的项目。项目名称是什么?</question>
      <answer>Website Redesign</answer>
   </qa_pair>
   <qa_pair>
      <question>搜索2024年3月关闭的标记为“bug”的问题。哪个用户关闭的问题最多?提供他们的用户名。</question>
      <answer>sarah_dev</answer>
   </qa_pair>
</evaluation>
```

## 运行评估

评估脚本(`scripts/evaluation.py`)支持三种传输类型:

**重要:**
- **stdio传输**:评估脚本会自动为您启动和管理MCP服务器进程。不要手动运行服务器。
- **sse/http传输**:您必须在运行评估之前单独启动MCP服务器。脚本连接到指定URL的已运行服务器。

### 1. 本地STDIO服务器

对于本地运行的MCP服务器(脚本自动启动服务器):

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_mcp_server.py \
  evaluation.xml
```

使用环境变量:
```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_mcp_server.py \
  -e API_KEY=abc123 \
  -e DEBUG=true \
  evaluation.xml
```

### 2. 服务器发送事件(SSE)

对于基于SSE的MCP服务器(您必须先启动服务器):

```bash
python scripts/evaluation.py \
  -t sse \
  -u https://example.com/mcp \
  -H "Authorization: Bearer token123" \
  -H "X-Custom-Header: value" \
  evaluation.xml
```

### 3. HTTP(可流式HTTP)

对于基于HTTP的MCP服务器(您必须先启动服务器):

```bash
python scripts/evaluation.py \
  -t http \
  -u https://example.com/mcp \
  -H "Authorization: Bearer token123" \
  evaluation.xml
```

## 命令行选项

```
usage: evaluation.py [-h] [-t {stdio,sse,http}] [-m MODEL] [-c COMMAND]
                     [-a ARGS [ARGS ...]] [-e ENV [ENV ...]] [-u URL]
                     [-H HEADERS [HEADERS ...]] [-o OUTPUT]
                     eval_file

positional arguments:
  eval_file             Path to evaluation XML file

optional arguments:
  -h, --help            Show help message
  -t, --transport       Transport type: stdio, sse, or http (default: stdio)
  -m, --model           Claude model to use (default: claude-3-7-sonnet-20250219)
  -o, --output          Output 报告文件(默认:打印到标准输出)

stdio 选项:
  -c, --command         运行 MCP 服务器的命令(例如:python、node)
  -a, --args            命令的参数(例如:server.py)
  -e, --env             以 KEY=VALUE 格式表示的环境变量

sse/http 选项:
  -u, --url             MCP 服务器 URL
  -H, --header          以 'Key: Value' 格式表示的 HTTP 头
```

## 输出

评估脚本会生成一份详细报告,包括:

- **汇总统计**:
  - 准确率(正确/总数)
  - 平均任务耗时
  - 每个任务的平均工具调用次数
  - 工具调用总次数

- **逐任务结果**:
  - 提示词和预期响应
  - 智能体的实际响应
  - 答案是否正确(✅/❌)
  - 耗时和工具调用详情
  - 智能体对其方法的总结
  - 智能体对工具的反馈

### 将报告保存到文件

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a my_server.py \
  -o evaluation_report.md \
  evaluation.xml
```

## 完整示例工作流

以下是创建并运行评估的完整示例:

1. **创建你的评估文件**(`my_evaluation.xml`):

```xml
<evaluation>
   <qa_pair>
      <question>Find the user who created the most issues in January 2024. What is their username?</question>
      <answer>alice_developer</answer>
   </qa_pair>
   <qa_pair>
      <question>Among all pull requests merged in Q1 2024, which repository had the highest number? Provide the repository name.</question>
      <answer>backend-api</answer>
   </qa_pair>
   <qa_pair>
      <question>Find the project that was completed in December 2023 and had the longest duration from start to finish. How many days did it take?</question>
      <answer>127</answer>
   </qa_pair>
</evaluation>
```

2. **安装依赖**:

```bash
pip install -r scripts/requirements.txt
export ANTHROPIC_API_KEY=your_api_key
```

3. **运行评估**:

```bash
python scripts/evaluation.py \
  -t stdio \
  -c python \
  -a github_mcp_server.py \
  -e GITHUB_TOKEN=ghp_xxx \
  -o github_eval_report.md \
  my_evaluation.xml
```

4. **查看报告** `github_eval_report.md`,以便:
   - 查看哪些问题通过/失败
   - 阅读智能体对你工具的反馈
   - 找出需要改进的地方
   - 迭代你的 MCP 服务器设计

## 故障排除

### 连接错误

如果你遇到连接错误:
- **STDIO**:确认命令和参数正确
- **SSE/HTTP**:检查 URL 是否可访问且请求头正确
- 确保任何所需的 API 密钥已在环境变量或请求头中设置

### 准确率低

如果许多评估失败:
- 查看每个任务的智能体反馈
- 检查工具描述是否清晰且全面
- 确认输入参数是否有充分文档说明
- 考虑工具返回的数据是否过多或过少
- 确保错误消息具有可操作性

### 超时问题

如果任务超时:
- 使用能力更强的模型(例如:`claude-3-7-sonnet-20250219`)
- 检查工具是否返回了过多数据
- 确认分页是否正常工作
- 考虑简化复杂问题
FILE:reference/node_mcp_server.md
# Node/TypeScript MCP 服务器实现指南

## 概述

本文档提供使用 MCP TypeScript SDK 实现 MCP 服务器的 Node/TypeScript 专属最佳实践和示例。内容涵盖项目结构、服务器设置、工具注册模式、使用 Zod 进行输入验证、错误处理以及完整可运行示例。

---

## 快速参考

### 关键导入
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import express from "express";
import { z } from "zod";
```

### 服务器初始化
```typescript
const server = new McpServer({
  name: "service-mcp-server",
  version: "1.0.0"
});
```

### 工具注册模式
```typescript
server.registerTool(
  "tool_name",
  {
    title: "Tool Display Name",
    description: "What the tool does",
    inputSchema: { param: z.string() },
    outputSchema: { result: z.string() }
  },
  async ({ param }) => {
    const output = { result: `Processed: param` };
    return {
      content: [{ type: "text", text: JSON.stringify(output) }],
      structuredContent: output // Modern pattern for structured data
    };
  }
);
```

---

## MCP TypeScript SDK

官方 MCP TypeScript SDK 提供:
- 用于服务器初始化的 `McpServer` 类
- 用于工具注册的 `registerTool` 方法
- 用于运行时输入验证的 Zod schema 集成
- 类型安全的工具处理程序实现

**重要 - 仅使用现代 API:**
- **应使用**:`server.registerTool()`、`server.registerResource()`、`server.registerPrompt()`
- **不要使用**:旧的已弃用 API,例如 `server.tool()`、`server.setRequestHandler(ListToolsRequestSchema, ...)` 或手动处理程序注册
- `register*` 方法提供更好的类型安全、自动 schema 处理,并且是推荐方式

有关完整细节,请参阅参考资料中的 MCP SDK 文档。

## 服务器命名约定

Node/TypeScript MCP 服务器必须遵循以下命名模式:
- **格式**:`{service}-mcp-server`(小写并用连字符分隔)
- **示例**:`github-mcp-server`、`jira-mcp-server`、`stripe-mcp-server`

名称应:
- 通用(不绑定特定功能)
- 描述所集成的服务/API
- 易于从任务描述中推断
- 不包含版本号或日期

## 项目结构

为 Node/TypeScript MCP 服务器创建以下结构:

```
{service}-mcp-server/
├── package.json
├── tsconfig.json
├── README.md
├── src/
│   ├── index.ts          # Main entry point with McpServer initialization
│   ├── types.ts          # TypeScript type definitions and interfaces
│   ├── tools/            # Tool implementations (one file per domain)
│   ├── services/         # API clients and shared utilities
│   ├── schemas/          # Zod validation schemas
│   └── constants.ts      # Shared constants (API_URL, CHARACTER_LIMIT, etc.)
└── dist/                 # Built JavaScript files (entry point: dist/index.js)
```

## 工具实现

### 工具命名

工具名称使用 snake_case(例如:"search_users"、"create_project"、"get_channel_info"),并使用清晰、面向操作的名称。

**避免命名冲突**:包含服务上下文以防止重叠:
- 使用 "slack_send_message",而不是仅使用 "send_message"
- 使用 "github_create_issue",而不是仅使用 "create_issue"
- 使用 "asana_list_tasks",而不是仅使用 "list_tasks"

### 工具结构

工具使用 `registerTool` 方法注册,并满足以下要求:
- 使用 Zod schema 进行运行时输入验证和类型安全
- 必须显式提供 `description` 字段 - JSDoc 注释不会被自动提取
- 显式提供`title`、`description`、`inputSchema` 和 `annotations`
- `inputSchema` 必须是 Zod schema 对象(不是 JSON schema)
- 显式地为所有参数和返回值标注类型

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "example-mcp",
  version: "1.0.0"
});

// 用于输入验证的 Zod schema
const UserSearchInputSchema = z.object({
  query: z.string()
    .min(2, "Query must be at least 2 characters")
    .max(200, "Query must not exceed 200 characters")
    .describe("Search string to match against names/emails"),
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("Maximum results to return"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("Number of results to skip for pagination"),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
}).strict();

// 从 Zod schema 推导出的类型定义
type UserSearchInput = z.infer<typeof UserSearchInputSchema>;

server.registerTool(
  "example_search_users",
  {
    title: "Search Example Users",
    description: `Search for users in the Example system by name, email, or team.

This tool searches across all user profiles in the Example platform, supporting partial matches and various search filters. It does NOT create or modify users, only searches existing ones.

Args:
  - query (string): Search string to match against names/emails
  - limit (number): Maximum results to return, between 1-100 (default: 20)
  - offset (number): Number of results to skip for pagination (default: 0)
  - response_format ('markdown' | 'json'): Output format (default: 'markdown')

Returns:
  For JSON format: Structured data with schema:
  {
    "total": number,           // Total number of matches found
    "count": number,           // Number of results in this response
    "offset": number,          // Current pagination offset
    "users": [
      {
        "id": string,          // User ID (e.g., "U123456789")
        "name": string,        // Full name (e.g., "John Doe")
        "email": string,       // Email address
        "team": string,        // Team name (optional)
        "active": boolean      // Whether user is active
      }
    ],
    "has_more": boolean,       // Whether more results are available
    "next_offset": number      // Offset for next page (if has_more is true)
  }

Examples:
  - Use when: "Find all marketing team members" -> params with query="team:marketing"
  - Use when: "Search for John's account" -> params with query="john"
  - Don't use when: You need to create a user (use example_create_user instead)

Error Handling:
  - Returns "Error: Rate limit exceeded" if too many requests (429 status)
  - Returns "No users found matching '<query>'" if search returns empty`,
    inputSchema: UserSearchInputSchema,
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: true
    }
  },
  async (params: UserSearchInput) => {
    try {
      // 输入验证由 Zod schema 处理
      // 使用已验证的参数发起 API 请求
      const data = await makeApiRequest<any>(
        "users/search",
        "GET",
        undefined,
        {
          q: params.query,
          limit: params.limit,
          offset: params.offset
        }
      );

      const users = data.users || [];
      const total = data.total || 0;

      if (!users.length) {
        return {
          content: [{
            type: "text",
            text: `No users found matching 'params.query'`
          }]
        };
      }

      // 准备结构化输出
      const output = {
        total,
        count: users.length,
        offset: params.offset,
        users: users.map((user: any) => ({
          id: user.id,
          name: user.name,
          email: user.email,
          ...(user.team ? { team: user.team } : {}),
          active: user.active ?? true
        })),
        has_more: total > params.offset + users.length,
        ...(total > params.offset + users.length ? {
          next_offset: params.offset + users.length
        } : {})
      };

      // 根据请求的格式生成文本表示
      let textContent: string;
      if (params.response_format === ResponseFormat.MARKDOWN) {
        const lines = [`# User Search Results: 'params.query'`, "",
          `Found total users (showing users.length)`, ""];
        for (const user of users) {
          lines.push(`## user.name (user.id)`);
          lines.push(`- **Email**: user.email`);
          if (user.team) lines.push(`- **Team**: user.team`);
          lines.push("");
        }
        textContent = lines.join("\n");
      } else {
        textContent = JSON.stringify(output, null, 2);
      }

      return {
        content: [{ type: "text", text: textContent }],
        structuredContent: output // 结构化数据的现代模式
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: handleApiError(error)
        }]
      };
    }
  }
);
```

## 用于输入验证的 Zod Schema

Zod 提供运行时类型验证:

```typescript
import { z } from "zod";

// 带验证的基础 schema
const CreateUserSchema = z.object({
  name: z.string()
    .min(1, "Name is required")
    .max(100, "Name must not exceed 100 characters"),
  email: z.string()
    .email("Invalid email format"),
  age: z.number()
    .int("Age must be a whole number")
    .min(0, "Age cannot be negative")
    .max(150, "Age cannot be greater than 150")
}).strict();  // 使用 .strict() 禁止额外字段

// 枚举
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

const SearchSchema = z.object({
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format")
});

// 带默认值的可选字段
const PaginationSchema = z.object({
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("Maximum results to return"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("Number of results to skip")
});
```

## 响应格式选项

支持多种输出格式以提供灵活性:

```typescript
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

const inputSchema = z.object({
  query: z.string(),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
});
```

**Markdown 格式**:
- 使用标题、列表和格式以提升清晰度
- 将时间戳转换为人类可读格式
- 在括号中显示名称及 ID
- 省略冗长的元数据
- 按逻辑对相关信息进行分组

**JSON 格式**:
- R返回完整、结构化的数据,适合程序化处理
- 包含所有可用字段和元数据
- 使用一致的字段名和类型

## 分页实现

对于列出资源的工具:

```typescript
const ListSchema = z.object({
  limit: z.number().int().min(1).max(100).default(20),
  offset: z.number().int().min(0).default(0)
});

async function listItems(params: z.infer<typeof ListSchema>) {
  const data = await apiRequest(params.limit, params.offset);

  const response = {
    total: data.total,
    count: data.items.length,
    offset: params.offset,
    items: data.items,
    has_more: data.total > params.offset + data.items.length,
    next_offset: data.total > params.offset + data.items.length
      ? params.offset + data.items.length
      : undefined
  };

  return JSON.stringify(response, null, 2);
}
```

## 字符限制与截断

添加 CHARACTER_LIMIT 常量以防止响应过大:

```typescript
// 在 constants.ts 的模块级别
export const CHARACTER_LIMIT = 25000;  // 最大响应大小(字符数)

async function searchTool(params: SearchInput) {
  let result = generateResponse(data);

  // 检查字符限制并在需要时截断
  if (result.length > CHARACTER_LIMIT) {
    const truncatedData = data.slice(0, Math.max(1, data.length / 2));
    response.data = truncatedData;
    response.truncated = true;
    response.truncation_message =
      `响应已从 data.length 项截断为 truncatedData.length 项。` +
      `请使用 'offset' 参数或添加筛选条件以查看更多结果。`;
    result = JSON.stringify(response, null, 2);
  }

  return result;
}
```

## 错误处理

提供清晰、可操作的错误消息:

```typescript
import axios, { AxiosError } from "axios";

function handleApiError(error: unknown): string {
  if (error instanceof AxiosError) {
    if (error.response) {
      switch (error.response.status) {
        case 404:
          return "错误:未找到资源。请检查 ID 是否正确。";
        case 403:
          return "错误:权限被拒绝。您无权访问此资源。";
        case 429:
          return "错误:超出速率限制。请稍后再发起更多请求。";
        default:
          return `错误:API 请求失败,状态码 error.response.status`;
      }
    } else if (error.code === "ECONNABORTED") {
      return "错误:请求超时。请重试。";
    }
  }
  return `错误:发生意外错误:String(error)`;
}
```

## 共享工具函数

将通用功能提取为可复用函数:

```typescript
// 共享的 API 请求函数
async function makeApiRequest<T>(
  endpoint: string,
  method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
  data?: any,
  params?: any
): Promise<T> {
  try {
    const response = await axios({
      method,
      url: `API_BASE_URL/endpoint`,
      data,
      params,
      timeout: 30000,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json"
      }
    });
    return response.data;
  } catch (error) {
    throw error;
  }
}
```

## Async/Await 最佳实践

对于网络请求和 I/O 操作,始终使用 async/await:

```typescript
// 良好:异步网络请求
async function fetchData(resourceId: string): Promise<ResourceData> {
  const response = await axios.get(`API_URL/resource/resourceId`);
  return response.data;
}

// 不佳:Promise 链
function fetchData(resourceId: string): Promise<ResourceData> {
  return axios.get(`API_URL/resource/resourceId`)
    .then(response => response.data);  // 更难阅读和维护
}
```

## TypeScript 最佳实践

1. **使用严格 TypeScript**:在 tsconfig.json 中启用严格模式
2. **定义接口**:为所有数据结构创建清晰的接口定义
3. **避免 `any`**:使用正确的类型或 `unknown` 代替 `any`
4. **使用 Zod 进行运行时验证**:使用 Zod schema 验证外部数据
5. **类型守卫**:为复杂类型检查创建类型守卫函数
6. **错误处理**:始终使用 try-catch 并进行正确的错误类型检查
7. **空值安全**:使用可选链(`?.`)和空值合并(`??`)

```typescript
// 良好:使用 Zod 和接口实现类型安全
interface UserResponse {
  id: string;
  name: string;
  email: string;
  team?: string;
  active: boolean;
}

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
  team: z.string().optional(),
  active: z.boolean()
});

type User = z.infer<typeof UserSchema>;

async function getUser(id: string): Promise<User> {
  const data = await apiCall(`/users/id`);
  return UserSchema.parse(data);  // 运行时验证
}

// 不佳:使用 any
async function getUser(id: string): Promise<any> {
  return await apiCall(`/users/id`);  // 无类型安全
}
```

## 包配置

### package.json

```json
{
  "name": "{service}-mcp-server",
  "version": "1.0.0",
  "description": "用于 {Service} API 集成的 MCP 服务器",
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "start": "node dist/index.js",
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "clean": "rm -rf dist"
  },
  "engines": {
    "node": ">=18"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "axios": "^1.7.9",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@types/node": "^22.10.0",
    "tsx": "^4.19.2",
    "typescript": "^5.7.2"
  }
}
```

### tsconfig.json

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "allowSyntheticDefaultImports": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## 完整示例

```typescript
#!/usr/bin/env node
/**
 * 用于 Example Service 的 MCP 服务器。
 *
 * 此服务器提供与 Example API 交互的工具,包括用户搜索、
 * 项目管理和数据导出功能。
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios, { AxiosError } from "axios";

// 常量
const API_BASE_URL = "https://api.example.com/v1";
const CHARACTER_LIMIT = 25000;

// 枚举
enum ResponseFormat {
  MARKDOWN = "markdown",
  JSON = "json"
}

// Zod schema
const UserSearchInputSchema = z.object({
  query: z.string()
    .min(2, "查询必须至少 2 个字符")
    .max(200, "查询不得超过 200 个字符")
    .describe("用于匹配姓名/邮箱的搜索字符串"),
  limit: z.number()
    .int()
    .min(1)
    .max(100)
    .default(20)
    .describe("返回的最大结果数"),
  offset: z.number()
    .int()
    .min(0)
    .default(0)
    .describe("用于分页的跳过结果数"),
  response_format: z.nativeEnum(ResponseFormat)
    .default(ResponseFormat.MARKDOWN)
    .describe("输出格式:'markdown' 为人类可读,'json' 为机器可读")
}).strict();

type UserSearchInput = z.infer<typeof UserSearchInputSchema>;

// 共享工具函数
async function makeApiRequest<T>(
  endpoint: string,
  method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
  data?: any,
  params?: any
): Promise<T> {
  try {
    const response = await axios({
      method,
      url: `API_BASE_URL/endpoint`,
      data,
      params,
      timeout: 30000,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json"
      }
    });
    return response.data;
  } catch (error) {
    throw error;
  }
}

function handleApiError(error: unknown): string {
  if (error instanceof AxiosError) {
    if (error.response) {
      switch (error.response.status) {
        case 404:
          return "错误:资源未找到。请检查 ID 是否正确。";
        case 403:
          return "错误:权限被拒绝。您无权访问此资源。";
        case 429:
          return "错误:超出速率限制。请稍后再发起更多请求。";
        default:
          return `错误:API 请求失败,状态码 error.response.status`;
      }
    } else if (error.code === "ECONNABORTED") {
      return "错误:请求超时。请重试。";
    }
  }
  return `错误:发生意外错误:String(error)`;
}

// 创建 MCP 服务器实例
const server = new McpServer({
  name: "example-mcp",
  version: "1.0.0"
});

// 注册工具
server.registerTool(
  "example_search_users",
  {
    title: "搜索示例用户",
    description: `[完整描述如上所示]`,
    inputSchema: UserSearchInputSchema,
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: true
    }
  },
  async (params: UserSearchInput) => {
    // 实现如上所示
  }
);

// 主函数
// 用于 stdio(本地):
async function runStdio() {
  if (!process.env.EXAMPLE_API_KEY) {
    console.error("错误:需要 EXAMPLE_API_KEY 环境变量");
    process.exit(1);
  }

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP 服务器通过 stdio 运行");
}

// 用于可流式 HTTP(远程):
async function runHTTP() {
  if (!process.env.EXAMPLE_API_KEY) {
    console.error("错误:需要 EXAMPLE_API_KEY 环境变量");
    process.exit(1);
  }

  const app = express();
  app.use(express.json());

  app.post('/mcp', async (req, res) => {
    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: undefined,
      enableJsonResponse: true
    });
    res.on('close', () => transport.close());
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  });

  const port = parseInt(process.env.PORT || '3000');
  app.listen(port, () => {
    console.error(`MCP 服务器运行在 http://localhost:port/mcp`);
  });
}

// 根据环境选择传输方式
const transport = process.env.TRANSPORT || 'stdio';
if (transport === 'http') {
  runHTTP().catch(error => {
    console.error("服务器错误:", error);
    process.exit(1);
  });
} else {
  runStdio().catch(error => {
    console.error("服务器错误:", error);
    process.exit(1);
  });
}
```

---

## 高级 MCP 功能

### 资源注册

将数据作为资源公开,以实现高效的基于 URI 的访问:

```typescript
import { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";

// 注册带有 URI 模板的资源
server.registerResource(
  {
    uri: "file://documents/{name}",
    name: "文档资源",
    description: "按名称访问文档",
    mimeType: "text/plain"
  },
  async (uri: string) => {
    // 从 URI 中提取参数
    const match = uri.match(/^file:\/\/documents\/(.+)$/);
    if (!match) {
      throw new Error("无效的 URI 格式");
    }

    const documentName = match[1];
    const content = await loadDocument(documentName);

    return {
      contents: [{
        uri,
        mimeType: "text/plain",
        text: content
      }]
    };
  }
);

// 动态列出可用资源
server.registerResourceList(async () => {
  const documents = await getAvailableDocuments();
  return {
    resources: documents.map(doc => ({
      uri: `file://documents/doc.name`,
      name: doc.name,
      mimeType: "text/plain",
      description: doc.description
    }))
  };
});
```

**何时使用资源与工具:**
- **资源**:用于具有简单基于 URI 参数的数据访问
- **工具**:用于需要验证和业务逻辑的复杂操作
- **资源**:当数据相对静态或基于模板时
- **工具**:当操作具有副作用或复杂工作流时

### 传输选项

TypeScript SDK 支持两种主要传输机制:

#### 可流式 HTTP(推荐用于远程服务器)

```typescript
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";

const app = express();
app.use(express.json());

app.post('/mcp', async (req, res) => {
  // 为每个请求创建新的传输(无状态,防止请求 ID 冲突)
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,
    enableJsonResponse: true
  });

  res.on('close', () => transport.close());

  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);
```

#### stdio(用于本地集成)

```typescript
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const transport = new StdioServerTransport();
await server.connect(transport);
```

**传输选择:**
- **可流式 HTTP**:Web 服务、远程访问、多个客户端
- **stdio**:命令行工具、本地开发、子进程集成

### 通知支持

当服务器状态变化时通知客户端:

```typescript
// 当工具列表变化时通知
server.notification({
  method: "notifications/tools/list_changed"
});

// 当资源变化时通知
server.notification({
  method: "notifications/resources/list_changed"
});
```

谨慎使用通知——仅在服务器能力真正变化时使用。

---

## 代码最佳实践

### 代码可组合性与可重用性

您的实现必须优先考虑可组合性和代码重用:

1. **提取通用功能**:
   - 为跨多个工具使用的操作创建可重用的辅助函数
   - 为 HTTP 请求构建共享 API 客户端,而不是重复代码
   - 将错误处理逻辑集中到实用函数中
   - 将业务逻辑提取到可组合的专用函数中
   - 提取共享的 markdown 或 JSON 字段选择与格式化功能

2. **避免重复**:
   - 绝不在工具之间复制粘贴相似的代码
   - 如果发现自己两次编写相似的逻辑,将其提取为函数
   - 分页、过滤、字段选择和格式化等常见操作应共享
   - 认证/授权逻辑应集中管理

## 构建与运行

在运行之前,始终先构建 TypeScript 代码:

```bash
# 构建项目
npm run build

# 运行服务器
npm start

# 开发模式(自动重载)
npm run dev
```

在认为实现完成之前,始终确保 `npm run build` 成功完成。

## 质量检查清单

在最终确定 Node/TypeScript MCP 服务器实现之前,确保:

### 战略设计
- [ ] 工具支持完整的工作流,而不仅仅是 API 端点包装器
- [ ] 工具名称反映自然任务细分
- [ ] 响应格式针对代理上下文效率进行优化
- [ ] 在适当的地方使用人类可读的标识符
- [ ] 错误消息引导代理正确使用

### 实现质量
- [ ] 聚焦实现:实现最重要和最有价值的工具
- [ ] 所有工具使用 `registerTool` 注册,并具有完整配置
- [ ] 所有工具包含 `title`、`description`、`inputSchema` 和 `annotations`
- [ ] 注解正确设置(readOnlyHint、destructiveHint、idempotentHint、openWorldHint)
- [ ] 所有工具使用 Zod 模式进行运行时输入验证,并强制执行 `.strict()`
- [ ] 所有 Zod 模式具有适当的约束和描述性错误消息
- [ ] 所有工具具有全面的描述,明确输入/输出类型
- [ ] 描述包含返回值示例和完整的模式文档
- [ ] 错误消息清晰、可操作且具有教育意义

### TypeScript 质量
- [ ] 为所有数据结构定义了 TypeScript 接口
- [ ] 在 tsconfig.json 中启用了严格 TypeScript
- [ ] 不使用 `any` 类型——改用 `unknown` 或适当的类型
- [ ] 所有异步函数具有显式的 Promise<T> 返回类型
- [ ] 错误处理使用适当的类型守卫(例如 `axios.isAxiosError`、`z.ZodError`)

### 高级功能(如适用)
- [ ] 为适当的数据端点注册了资源
- [ ] 配置了适当的传输(stdio 或可流式 HTTP)
- [ ] 为动态服务器能力实现了通知
- [ ] 使用 SDK 接口实现类型安全

### 项目配置
- [ ] Package.json 包含所有必要的依赖项
- [ ] 构建脚本在 dist/ 目录中生成可用的 JavaScript
- [ ] 主入口点正确配置为 dist/index.js
- [ ] 服务器名称遵循格式:`{service}-mcp-server`
- [ ] tsconfig.json 正确配置了严格模式

### 代码质量
- [ ] 在适用的情况下正确实现了分页
- [ ] 大响应检查 CHARACTER_LIMIT 常量并用清晰的消息截断
- [ ] 为可能的大结果集提供了过滤选项
- [ ] 所有网络操作优雅地处理超时和连接错误
- [ ] 通用功能提取到可重用函数中
- [ ] 相似操作的返回类型一致

### 测试与构建
- [ ] `npm run build` 成功完成且无错误
- [ ] 创建了 dist/index.js 且可执行
- [ ] 服务器运行:`node dist/index.js --help`
- [ ] 所有导入正确解析
- [ ] 示例工具调用按预期工作
FILE:reference/python_mcp_server.md
# Python MCP 服务器实现指南

## 概述

本文档提供了使用 MCP Python SDK 实现 MCP 服务器的 Python 特定最佳实践和示例。它涵盖了服务器设置、工具注册模式、使用 Pydantic 进行输入验证、错误处理以及完整的工作示例。

---

## 快速参考

### 关键导入
```python
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional, List, Dict, Any
from enum import Enum
import httpx
```

### 服务器初始化
```python
mcp = FastMCP("service_mcp")
```

### 工具注册模式
```python
@mcp.tool(name="tool_name", annotations={...})
async def tool_function(params: InputModel) -> str:
    # 实现
    pass
```

---

## MCP Python SDK 和 FastMCP

官方 MCP Python SDK 提供了 FastMCP,一个用于构建 MCP 服务器的高级框架。它提供:
- 从函数签名和文档字符串自动生成描述和 inputSchema
- 用于输入验证的 Pydantic 模型集成
- 使用 `@mcp.tool` 的基于装饰器的工具注册

**获取完整的 SDK 文档,请使用 WebFetch 加载:**
`https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`

## 服务器命名约定

Python MCP 服务器必须遵循以下命名模式:
- **格式**:`{service}_mcp`(小写加下划线)
- **示例**:`github_mcp`、`jira_mcp`、`stripe_mcp`

名称应:
- 通用(不绑定特定功能)
- 描述所集成的服务/API
- 易于从任务描述中推断
- 不含版本号或日期

## 工具实现

### 工具命名

工具名称使用 snake_case(例如 "search_users"、"create_project"、"get_channel_info"),名称清晰且面向操作。

**避免命名冲突**:包含服务上下文以防止重叠:
- 使用 "slack_send_message" 而不是仅 "send_message"
- 使用 "github_create_issue" 而不是仅 "create_issue"
- 使用 "asana_list_tasks" 而不是仅 "list_tasks"

### 使用 FastMCP 的工具结构

工具使用 `@mcp.tool` 装饰器定义,并使用 Pydantic 模型进行输入验证:

```python
from pydantic import BaseModel, Field, ConfigDict
from mcp.server.fastmcp import FastMCP

# 初始化 MCP 服务器
mcp = FastMCP("example_mcp")

# 定义用于输入验证的 Pydantic 模型
class ServiceToolInput(BaseModel):
    '''服务工具操作的输入模型。'''
    model_config = ConfigDict(
        str_strip_whitespace=True,  # 自动去除字符串空白
        validate_assignment=True,    # 赋值时验证
        extra='forbid'              # 禁止额外字段
    )

    param1: str = Field(..., description="第一个参数描述(例如 'user123'、'project-abc')", min_length=1, max_length=100)
    param2: Optional[int] = Field(default=None, description="带约束的可选整数参数", ge=0, le=1000)
    tags: Optional[List[str]] = Field(default_factory=list, description="要应用的标签列表", max_items=10)

@mcp.tool(
    name="service_tool_name",
    annotations={
        "title": "人类可读的工具标题",
        "readOnlyHint": True,     # 工具不修改环境
        "destructiveHint": False,  # 工具不执行破坏性tive operations
        "idempotentHint": True,    # 重复调用不会产生额外效果
        "openWorldHint": False     # 工具不与外部实体交互
    }
)
async def service_tool_name(params: ServiceToolInput) -> str:
    '''工具描述自动成为 'description' 字段。

    此工具对服务执行特定操作。在处理之前,它使用 ServiceToolInput Pydantic 模型
    验证所有输入。

    Args:
        params (ServiceToolInput): 包含以下内容的已验证输入参数:
            - param1 (str): 第一个参数描述
            - param2 (Optional[int]): 带默认值的可选参数
            - tags (Optional[List[str]]): 标签列表

    Returns:
        str: 包含操作结果的 JSON 格式响应
    '''
    # 在此处实现
    pass
```

## Pydantic v2 关键特性

- 使用 `model_config` 代替嵌套的 `Config` 类
- 使用 `field_validator` 代替已弃用的 `validator`
- 使用 `model_dump()` 代替已弃用的 `dict()`
- 验证器需要 `@classmethod` 装饰器
- 验证器方法需要类型提示

```python
from pydantic import BaseModel, Field, field_validator, ConfigDict

class CreateUserInput(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True
    )

    name: str = Field(..., description="用户的全名", min_length=1, max_length=100)
    email: str = Field(..., description="用户的电子邮件地址", pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$')
    age: int = Field(..., description="用户的年龄", ge=0, le=150)

    @field_validator('email')
    @classmethod
    def validate_email(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("电子邮件不能为空")
        return v.lower()
```

## 响应格式选项

支持多种输出格式以提供灵活性:

```python
from enum import Enum

class ResponseFormat(str, Enum):
    '''工具响应的输出格式。'''
    MARKDOWN = "markdown"
    JSON = "json"

class UserSearchInput(BaseModel):
    query: str = Field(..., description="搜索查询")
    response_format: ResponseFormat = Field(
        default=ResponseFormat.MARKDOWN,
        description="输出格式:'markdown' 用于人类可读,'json' 用于机器可读"
    )
```

**Markdown 格式**:
- 使用标题、列表和格式以提高清晰度
- 将时间戳转换为人类可读格式(例如,"2024-01-15 10:30:00 UTC" 而不是 epoch)
- 在括号中显示显示名称和 ID(例如,"@john.doe (U123456)")
- 省略冗长的元数据(例如,只显示一个个人资料图片 URL,而不是所有尺寸)
- 逻辑分组相关信息

**JSON 格式**:
- 返回完整、结构化的数据,适合程序化处理
- 包含所有可用字段和元数据
- 使用一致的字段名称和类型

## 分页实现

对于列出资源的工具:

```python
class ListInput(BaseModel):
    limit: Optional[int] = Field(default=20, description="返回的最大结果数", ge=1, le=100)
    offset: Optional[int] = Field(default=0, description="分页时跳过的结果数", ge=0)

async def list_items(params: ListInput) -> str:
    # 使用分页发起 API 请求
    data = await api_request(limit=params.limit, offset=params.offset)

    # 返回分页信息
    response = {
        "total": data["total"],
        "count": len(data["items"]),
        "offset": params.offset,
        "items": data["items"],
        "has_more": data["total"] > params.offset + len(data["items"]),
        "next_offset": params.offset + len(data["items"]) if data["total"] > params.offset + len(data["items"]) else None
    }
    return json.dumps(response, indent=2)
```

## 错误处理

提供清晰、可操作的错误消息:

```python
def _handle_api_error(e: Exception) -> str:
    '''所有工具的一致错误格式。'''
    if isinstance(e, httpx.HTTPStatusError):
        if e.response.status_code == 404:
            return "错误:未找到资源。请检查 ID 是否正确。"
        elif e.response.status_code == 403:
            return "错误:权限被拒绝。您无权访问此资源。"
        elif e.response.status_code == 429:
            return "错误:超出速率限制。请等待后再发起更多请求。"
        return f"错误:API 请求失败,状态码 {e.response.status_code}"
    elif isinstance(e, httpx.TimeoutException):
        return "错误:请求超时。请重试。"
    return f"错误:发生意外错误:{type(e).__name__}"
```

## 共享工具

将常见功能提取为可重用函数:

```python
# 共享 API 请求函数
async def _make_api_request(endpoint: str, method: str = "GET", **kwargs) -> dict:
    '''所有 API 调用的可重用函数。'''
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method,
            f"{API_BASE_URL}/{endpoint}",
            timeout=30.0,
            **kwargs
        )
        response.raise_for_status()
        return response.json()
```

## 异步/等待最佳实践

始终对网络请求和 I/O 操作使用 async/await:

```python
# 良好:异步网络请求
async def fetch_data(resource_id: str) -> dict:
    async with httpx.AsyncClient() as client:
        response = await client.get(f"{API_URL}/resource/{resource_id}")
        response.raise_for_status()
        return response.json()

# 不良:同步请求
def fetch_data(resource_id: str) -> dict:
    response = requests.get(f"{API_URL}/resource/{resource_id}")  # 阻塞
    return response.json()
```

## 类型提示

全程使用类型提示:

```python
from typing import Optional, List, Dict, Any

async def get_user(user_id: str) -> Dict[str, Any]:
    data = await fetch_user(user_id)
    return {"id": data["id"], "name": data["name"]}
```

## 工具文档字符串

每个工具都必须有包含显式类型信息的全面文档字符串:

```python
async def search_users(params: UserSearchInput) -> str:
    '''
    在 Example 系统中按姓名、电子邮件或团队搜索用户。

    此工具搜索 Example 平台中的所有用户配置文件,
    支持部分匹配和各种搜索过滤器。它不会
    创建或修改用户,仅搜索现有用户。

    Args:
        params (UserSearchInput): 包含以下内容的已验证输入参数:
            - query (str): 用于匹配姓名/电子邮件的搜索字符串(例如,"john"、"@example.com"、"team:marketing")
            - limit (Optional[int]): 返回的最大结果数,介于 1-100 之间(默认:20)
            - offset (Optional[int]): 分页时跳过的结果数(默认:0)

    Returns:
        str: 包含搜索结果的 JSON 格式字符串,具有以下模式:

        成功响应:
        {
            "total": int,           # 找到的匹配总数
            "count": int,           # 此响应中的结果数量
            "offset": int,          # 当前分页偏移量
            "users": [
                {
                    "id": str,      # 用户 ID(例如 "U123456789")
                    "name": str,    # 全名(例如 "John Doe")
                    "email": str,   # 电子邮件地址(例如 "john@example.com")
                    "team": str     # 团队名称(例如 "Marketing")- 可选
                }
            ]
        }

        错误响应:
        "Error: <error message>" 或 "No users found matching '<query>'"

    示例:
        - 使用场景:"查找所有市场团队成员" -> params 中 query="team:marketing"
        - 使用场景:"搜索 John 的账户" -> params 中 query="john"
        - 不使用场景:需要创建用户(改用 example_create_user)
        - 不使用场景:已有用户 ID 且需要完整详情(改用 example_get_user)

    错误处理:
        - 输入验证错误由 Pydantic 模型处理
        - 如果请求过多(429 状态),返回 "Error: Rate limit exceeded"
        - 如果 API 密钥无效(401 状态),返回 "Error: Invalid API authentication"
        - 返回格式化结果列表或 "No users found matching 'query'"
    '''
```

## 完整示例

请参阅下方完整的 Python MCP 服务器示例:

```python
#!/usr/bin/env python3
'''
Example 服务的 MCP 服务器。

此服务器提供与 Example API 交互的工具,包括用户搜索、
项目管理和数据导出功能。
'''

from typing import Optional, List, Dict, Any
from enum import Enum
import httpx
from pydantic import BaseModel, Field, field_validator, ConfigDict
from mcp.server.fastmcp import FastMCP

# 初始化 MCP 服务器
mcp = FastMCP("example_mcp")

# 常量
API_BASE_URL = "https://api.example.com/v1"

# 枚举
class ResponseFormat(str, Enum):
    '''工具响应的输出格式。'''
    MARKDOWN = "markdown"
    JSON = "json"

# 用于输入验证的 Pydantic 模型
class UserSearchInput(BaseModel):
    '''用户搜索操作的输入模型。'''
    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True
    )

    query: str = Field(..., description="用于匹配姓名/电子邮件的搜索字符串", min_length=2, max_length=200)
    limit: Optional[int] = Field(default=20, description="要返回的最大结果数", ge=1, le=100)
    offset: Optional[int] = Field(default=0, description="分页时要跳过的结果数", ge=0)
    response_format: ResponseFormat = Field(default=ResponseFormat.MARKDOWN, description="输出格式")

    @field_validator('query')
    @classmethod
    def validate_query(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("查询不能为空或仅包含空白字符")
        return v.strip()

# 共享工具函数
async def _make_api_request(endpoint: str, method: str = "GET", **kwargs) -> dict:
    '''用于所有 API 调用的可复用函数。'''
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method,
            f"{API_BASE_URL}/{endpoint}",
            timeout=30.0,
            **kwargs
        )
        response.raise_for_status()
        return response.json()

def _handle_api_error(e: Exception) -> str:
    '''在所有工具中保持一致的错误格式化。'''
    if isinstance(e, httpx.HTTPStatusError):
        if e.response.status_code == 404:
            return "Error: Resource not found. Please check the ID is correct."
        elif e.response.status_code == 403:
            return "Error: Permission denied. You don't have access to this resource."
        elif e.response.status_code == 429:
            return "Error: Rate limit exceeded. Please wait before making more requests."
        return f"Error: API request failed with status {e.response.status_code}"
    elif isinstance(e, httpx.TimeoutException):
        return "Error: Request timed out. Please try again."
    return f"Error: Unexpected error occurred: {type(e).__name__}"

# 工具定义
@mcp.tool(
    name="example_search_users",
    annotations={
        "title": "Search Example Users",
        "readOnlyHint": True,
        "destructiveHint": False,
        "idempotentHint": True,
        "openWorldHint": True
    }
)
async def example_search_users(params: UserSearchInput) -> str:
    '''按姓名、电子邮件或团队在 Example 系统中搜索用户。

    [完整 docstring 如上所示]
    '''
    try:
        # 使用已验证的参数发起 API 请求
        data = await _make_api_request(
            "users/search",
            params={
                "q": params.query,
                "limit": params.limit,
                "offset": params.offset
            }
        )

        users = data.get("users", [])
        total = data.get("total", 0)

        if not users:
            return f"No users found matching '{params.query}'"

        # 根据请求的格式格式化响应
        if params.response_format == ResponseFormat.MARKDOWN:
            lines = [f"# User Search Results: '{params.query}'", ""]
            lines.append(f"Found {total} users (showing {len(users)})")
            lines.append("")

            for user in users:
                lines.append(f"## {user['name']} ({user['id']})")
                lines.append(f"- **Email**: {user['email']}")
                if user.get('team'):
                    lines.append(f"- **Team**: {user['team']}")
                lines.append("")

            return "\n".join(lines)

        else:
            # 机器可读的 JSON 格式
            import json
            response = {
                "total": total,
                "count": len(users),
                "offset": params.offset,
                "users": users
            }
            return json.dumps(response, indent=2)

    except Exception as e:
        return _handle_api_error(e)

if __name__ == "__main__":
    mcp.run()
```

---

## 高级 FastMCP 功能

### Context 参数注入

FastMCP 可以自动将 `Context` 参数注入到工具中,以实现日志记录、进度报告、资源读取和用户交互等高级功能:

```python
from mcp.server.fastmcp import FastMCP, Context

mcp = FastMCP("example_mcp")

@mcp.tool()
async def advanced_search(query: str, ctx: Context) -> str:
    '''具有上下文访问权限的高级工具,用于日志记录和进度报告。'''

    # 为长时间操作报告进度
    await ctx.report_progress(0.25, "Starting search...")

    # 记录信息用于调试
    await ctx.log_info("Processing query", {"query": query, "timestamp": datetime.now()})

    # 执行搜索
    results = await search_api(query)
    await ctx.report_progress(0.75, "Formatting results...")

    # 访问服务器配置
    server_name = ctx.fastmcp.name

    return format_results(results)

@mcp.tool()
async def interactive_tool(resource_id: str, ctx: Context) -> str:
    '''可向用户请求额外输入的工具。'''

    # 在需要时请求敏感信息
    api_key = await ctx.elicit(
        prompt="请提供您的 API 密钥:",
        input_type="password"
    )

    # 使用提供的密钥
    return await api_call(resource_id, api_key)
```

**上下文能力:**
- `ctx.report_progress(progress, message)` - 为长时间操作报告进度
- `ctx.log_info(message, data)` / `ctx.log_error()` / `ctx.log_debug()` - 日志记录
- `ctx.elicit(prompt, input_type)` - 向用户请求输入
- `ctx.fastmcp.name` - 访问服务器配置
- `ctx.read_resource(uri)` - 读取 MCP 资源

### 资源注册

将数据作为资源暴露,以实现高效的、基于模板的访问:

```python
@mcp.resource("file://documents/{name}")
async def get_document(name: str) -> str:
    '''将文档作为 MCP 资源暴露。

    资源适用于不需要复杂参数的静态或半静态数据。
    它们使用 URI 模板实现灵活访问。
    '''
    document_path = f"./docs/{name}"
    with open(document_path, "r") as f:
        return f.read()

@mcp.resource("config://settings/{key}")
async def get_setting(key: str, ctx: Context) -> str:
    '''将配置作为带上下文的资源暴露。'''
    settings = await load_settings()
    return json.dumps(settings.get(key, {}))
```

**何时使用资源与工具:**
- **资源**:用于具有简单参数的数据访问(URI 模板)
- **工具**:用于具有验证和业务逻辑的复杂操作

### 结构化输出类型

FastMCP 支持字符串之外的多种返回类型:

```python
from typing import TypedDict
from dataclasses import dataclass
from pydantic import BaseModel

# 用于结构化返回的 TypedDict
class UserData(TypedDict):
    id: str
    name: str
    email: str

@mcp.tool()
async def get_user_typed(user_id: str) -> UserData:
    '''返回结构化数据 - FastMCP 处理序列化。'''
    return {"id": user_id, "name": "John Doe", "email": "john@example.com"}

# 用于复杂验证的 Pydantic 模型
class DetailedUser(BaseModel):
    id: str
    name: str
    email: str
    created_at: datetime
    metadata: Dict[str, Any]

@mcp.tool()
async def get_user_detailed(user_id: str) -> DetailedUser:
    '''返回 Pydantic 模型 - 自动生成 schema。'''
    user = await fetch_user(user_id)
    return DetailedUser(**user)
```

### 生命周期管理

初始化跨请求持久存在的资源:

```python
from contextlib import asynccontextmanager

@asynccontextmanager
async def app_lifespan():
    '''管理在服务器生命周期内存在的资源。'''
    # 初始化连接、加载配置等
    db = await connect_to_database()
    config = load_configuration()

    # 使其对所有工具可用
    yield {"db": db, "config": config}

    # 关闭时清理
    await db.close()

mcp = FastMCP("example_mcp", lifespan=app_lifespan)

@mcp.tool()
async def query_data(query: str, ctx: Context) -> str:
    '''通过上下文访问生命周期资源。'''
    db = ctx.request_context.lifespan_state["db"]
    results = await db.query(query)
    return format_results(results)
```

### 传输选项

FastMCP 支持两种主要传输机制:

```python
# stdio 传输(用于本地工具)- 默认
if __name__ == "__main__":
    mcp.run()

# 可流式 HTTP 传输(用于远程服务器)
if __name__ == "__main__":
    mcp.run(transport="streamable_http", port=8000)
```

**传输选择:**
- **stdio**:命令行工具、本地集成、子进程执行
- **可流式 HTTP**:Web 服务、远程访问、多客户端

---

## 代码最佳实践

### 代码可组合性与可复用性

您的实现必须优先考虑可组合性和代码复用:

1. **提取通用功能**:
   - 为多个工具中使用的操作创建可复用的辅助函数
   - 为 HTTP 请求构建共享 API 客户端,而不是重复代码
   - 将错误处理逻辑集中到工具函数中
   - 将业务逻辑提取到可组合的专用函数中
   - 提取共享的 markdown 或 JSON 字段选择与格式化功能

2. **避免重复**:
   - 绝不要在工具之间复制粘贴相似代码
   - 如果发现自己两次编写相似逻辑,请将其提取为函数
   - 分页、过滤、字段选择和格式化等常见操作应共享
   - 认证/授权逻辑应集中管理

### Python 特定最佳实践

1. **使用类型提示**:始终为函数参数和返回值包含类型注解
2. **Pydantic 模型**:为所有输入验证定义清晰的 Pydantic 模型
3. **避免手动验证**:让 Pydantic 通过约束处理输入验证
4. **正确导入**:对导入进行分组(标准库、第三方、本地)
5. **错误处理**:使用特定的异常类型(httpx.HTTPStatusError,而非通用 Exception)
6. **异步上下文管理器**:对需要清理的资源使用 `async with`
7. **常量**:以 UPPER_CASE 定义模块级常量

## 质量检查清单

在最终确定您的 Python MCP 服务器实现之前,请确保:

### 战略设计
- [ ] 工具支持完整的工作流,而不仅仅是 API 端点包装器
- [ ] 工具名称反映自然任务细分
- [ ] 响应格式针对代理上下文效率进行优化
- [ ] 在适当的地方使用人类可读的标识符
- [ ] 错误消息引导代理正确使用

### 实现质量
- [ ] 聚焦实现:实现最重要和最有价值的工具
- [ ] 所有工具都有描述性名称和文档
- [ ] 相似操作的返回类型一致
- [ ] 为所有外部调用实现错误处理
- [ ] 服务器名称遵循格式:`{service}_mcp`
- [ ] 所有网络操作使用 async/await
- [ ] 通用功能提取为可复用函数
- [ ] 错误消息清晰、可操作且具有教育意义
- [ ] 输出经过正确验证和格式化

### 工具配置
- [ ] 所有工具在装饰器中实现 'name' 和 'annotations'
- [ ] 注解正确设置(readOnlyHint、destructiveHint、idempotentHint、openWorldHint)
- [ ] 所有工具使用 Pydantic BaseModel 进行输入验证,并带有 Field() 定义
- [ ] 所有 Pydantic Field 都有显式类型和带约束的描述
- [ ] 所有工具都有全面的 docstring,包含显式的输入/输出类型
- [ ] Docstring 包含 dict/JSON 返回的完整 schema 结构
- [ ] Pydantic 模型处理输入验证(无需手动验证)

### 高级功能(如适用)
- [ ] 使用上下文注入进行日志记录、进度报告或信息引导
- [ ] 为适当的数据端点注册资源
- [ ] 为持久连接实现生命周期管理
- [ ] Struc使用的结构化输出类型(TypedDict、Pydantic 模型)
- [ ] 已配置合适的传输方式(stdio 或可流式 HTTP)

### 代码质量
- [ ] 文件包含正确的导入,包括 Pydantic 导入
- [ ] 在适用处正确实现了分页
- [ ] 为可能较大的结果集提供了过滤选项
- [ ] 所有异步函数均使用 `async def` 正确定义
- [ ] HTTP 客户端使用遵循异步模式并配有正确的上下文管理器
- [ ] 整个代码中使用了类型提示
- [ ] 常量在模块级别以 UPPER_CASE 定义

### 测试
- [ ] 服务器成功运行:`python your_server.py --help`
- [ ] 所有导入均正确解析
- [ ] 示例工具调用按预期工作
- [ ] 错误场景得到妥善处理
FILE:scripts/connections.py
"""MCP 服务器的轻量级连接处理。"""

from abc import ABC, abstractmethod
from contextlib import AsyncExitStack
from typing import Any

from mcp import ClientSession, StdioServerParameters
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client
from mcp.client.streamable_http import streamablehttp_client


class MCPConnection(ABC):
    """MCP 服务器连接的基类。"""

    def __init__(self):
        self.session = None
        self._stack = None

    @abstractmethod
    def _create_context(self):
        """根据连接类型创建连接上下文。"""

    async def __aenter__(self):
        """初始化 MCP 服务器连接。"""
        self._stack = AsyncExitStack()
        await self._stack.__aenter__()

        try:
            ctx = self._create_context()
            result = await self._stack.enter_async_context(ctx)

            if len(result) == 2:
                read, write = result
            elif len(result) == 3:
                read, write, _ = result
            else:
                raise ValueError(f"Unexpected context result: {result}")

            session_ctx = ClientSession(read, write)
            self.session = await self._stack.enter_async_context(session_ctx)
            await self.session.initialize()
            return self
        except BaseException:
            await self._stack.__aexit__(None, None, None)
            raise

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """清理 MCP 服务器连接资源。"""
        if self._stack:
            await self._stack.__aexit__(exc_type, exc_val, exc_tb)
        self.session = None
        self._stack = None

    async def list_tools(self) -> list[dict[str, Any]]:
        """从 MCP 服务器检索可用工具。"""
        response = await self.session.list_tools()
        return [
            {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema,
            }
            for tool in response.tools
        ]

    async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
        """使用提供的参数在 MCP 服务器上调用工具。"""
        result = await self.session.call_tool(tool_name, arguments=arguments)
        return result.content


class MCPConnectionStdio(MCPConnection):
    """使用标准输入/输出的 MCP 连接。"""

    def __init__(self, command: str, args: list[str] = None, env: dict[str, str] = None):
        super().__init__()
        self.command = command
        self.args = args or []
        self.env = env

    def _create_context(self):
        return stdio_client(
            StdioServerParameters(command=self.command, args=self.args, env=self.env)
        )


class MCPConnectionSSE(MCPConnection):
    """使用服务器发送事件(Server-Sent Events)的 MCP 连接。"""

    def __init__(self, url: str, headers: dict[str, str] = None):
        super().__init__()
        self.url = url
        self.headers = headers or {}

    def _create_context(self):
        return sse_client(url=self.url, headers=self.headers)


class MCPConnectionHTTP(MCPConnection):
    """使用可流式 HTTP 的 MCP 连接。"""

    def __init__(self, url: str, headers: dict[str, str] = None):
        super().__init__()
        self.url = url
        self.headers = headers or {}

    def _create_context(self):
        return streamablehttp_client(url=self.url, headers=self.headers)


def create_connection(
    transport: str,
    command: str = None,
    args: list[str] = None,
    env: dict[str, str] = None,
    url: str = None,
    headers: dict[str, str] = None,
) -> MCPConnection:
    """用于创建相应 MCP 连接的工厂函数。

    Args:
        transport: 连接类型("stdio"、"sse" 或 "http")
        command: 要运行的命令(仅 stdio)
        args: 命令参数(仅 stdio)
        env: 环境变量(仅 stdio)
        url: 服务器 URL(仅 sse 和 http)
        headers: HTTP 头(仅 sse 和 http)

    Returns:
        MCPConnection 实例
    """
    transport = transport.lower()

    if transport == "stdio":
        if not command:
            raise ValueError("Command is required for stdio transport")
        return MCPConnectionStdio(command=command, args=args, env=env)

    elif transport == "sse":
        if not url:
            raise ValueError("URL is required for sse transport")
        return MCPConnectionSSE(url=url, headers=headers)

    elif transport in ["http", "streamable_http", "streamable-http"]:
        if not url:
            raise ValueError("URL is required for http transport")
        return MCPConnectionHTTP(url=url, headers=headers)

    else:
        raise ValueError(f"Unsupported transport type: {transport}. Use 'stdio', 'sse', or 'http'")
FILE:scripts/evaluation.py
"""MCP 服务器评估工具

此脚本通过使用 Claude 对 MCP 服务器运行测试问题来评估它们。
"""

import argparse
import asyncio
import json
import re
import sys
import time
import traceback
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Any

from anthropic import Anthropic

from connections import create_connection

EVALUATION_PROMPT = """你是一个可以访问工具的 AI 助手。

当给定任务时,你必须:
1. 使用可用工具完成任务
2. 在 <summary> 标签中提供你方法的每一步摘要
3. 在 <feedback> 标签中提供对所提供工具的反馈
4. 在 <response> 标签中提供你的最终回复

摘要要求:
- 在你的 <summary> 标签中,你必须解释:
  - 你为完成任务所采取的步骤
  - 你使用了哪些工具、以什么顺序以及为什么
  - 你为每个工具提供的输入
  - 你从每个工具收到的输出
  - 关于你如何得出该回复的摘要

反馈要求:
- 在你的 <feedback> 标签中,提供对工具的建设性反馈:
  - 评论工具名称:它们是否清晰且具有描述性?
  - 评论输入参数:它们是否有良好的文档?必需参数与可选参数是否清晰?
  - 评论描述描述:它们是否准确描述了工具的功能?
  - 评论工具使用过程中遇到的任何错误:工具是否执行失败?工具是否返回了过多 token?
  - 指出具体的改进领域并解释为什么这些改进会有帮助
  - 在你的建议中要具体且可操作

响应要求:
- 你的响应应简洁并直接回答所问内容
- 始终将最终响应包裹在 <response> 标签中
- 如果你无法解决该任务,返回 <response>NOT_FOUND</response>
- 对于数值响应,只提供数字
- 对于 ID,只提供 ID
- 对于名称或文本,提供所要求的确切文本
- 你的响应应放在最后"""


def parse_evaluation_file(file_path: Path) -> list[dict[str, Any]]:
    """解析包含 qa_pair 元素的 XML 评估文件。"""
    try:
        tree = ET.parse(file_path)
        root = tree.getroot()
        evaluations = []

        for qa_pair in root.findall(".//qa_pair"):
            question_elem = qa_pair.find("question")
            answer_elem = qa_pair.find("answer")

            if question_elem is not None and answer_elem is not None:
                evaluations.append({
                    "question": (question_elem.text or "").strip(),
                    "answer": (answer_elem.text or "").strip(),
                })

        return evaluations
    except Exception as e:
        print(f"解析评估文件 {file_path} 时出错:{e}")
        return []


def extract_xml_content(text: str, tag: str) -> str | None:
    """从 XML 标签中提取内容。"""
    pattern = rf"<{tag}>(.*?)</{tag}>"
    matches = re.findall(pattern, text, re.DOTALL)
    return matches[-1].strip() if matches else None


async def agent_loop(
    client: Anthropic,
    model: str,
    question: str,
    tools: list[dict[str, Any]],
    connection: Any,
) -> tuple[str, dict[str, Any]]:
    """使用 MCP 工具运行智能体循环。"""
    messages = [{"role": "user", "content": question}]

    response = await asyncio.to_thread(
        client.messages.create,
        model=model,
        max_tokens=4096,
        system=EVALUATION_PROMPT,
        messages=messages,
        tools=tools,
    )

    messages.append({"role": "assistant", "content": response.content})

    tool_metrics = {}

    while response.stop_reason == "tool_use":
        tool_use = next(block for block in response.content if block.type == "tool_use")
        tool_name = tool_use.name
        tool_input = tool_use.input

        tool_start_ts = time.time()
        try:
            tool_result = await connection.call_tool(tool_name, tool_input)
            tool_response = json.dumps(tool_result) if isinstance(tool_result, (dict, list)) else str(tool_result)
        except Exception as e:
            tool_response = f"执行工具 {tool_name} 时出错:{str(e)}\n"
            tool_response += traceback.format_exc()
        tool_duration = time.time() - tool_start_ts

        if tool_name not in tool_metrics:
            tool_metrics[tool_name] = {"count": 0, "durations": []}
        tool_metrics[tool_name]["count"] += 1
        tool_metrics[tool_name]["durations"].append(tool_duration)

        messages.append({
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": tool_response,
            }]
        })

        response = await asyncio.to_thread(
            client.messages.create,
            model=model,
            max_tokens=4096,
            system=EVALUATION_PROMPT,
            messages=messages,
            tools=tools,
        )
        messages.append({"role": "assistant", "content": response.content})

    response_text = next(
        (block.text for block in response.content if hasattr(block, "text")),
        None,
    )
    return response_text, tool_metrics


async def evaluate_single_task(
    client: Anthropic,
    model: str,
    qa_pair: dict[str, Any],
    tools: list[dict[str, Any]],
    connection: Any,
    task_index: int,
) -> dict[str, Any]:
    """使用给定工具评估单个 QA 对。"""
    start_time = time.time()

    print(f"任务 {task_index + 1}:使用以下问题运行任务:{qa_pair['question']}")
    response, tool_metrics = await agent_loop(client, model, qa_pair["question"], tools, connection)

    response_value = extract_xml_content(response, "response")
    summary = extract_xml_content(response, "summary")
    feedback = extract_xml_content(response, "feedback")

    duration_seconds = time.time() - start_time

    return {
        "question": qa_pair["question"],
        "expected": qa_pair["answer"],
        "actual": response_value,
        "score": int(response_value == qa_pair["answer"]) if response_value else 0,
        "total_duration": duration_seconds,
        "tool_calls": tool_metrics,
        "num_tool_calls": sum(len(metrics["durations"]) for metrics in tool_metrics.values()),
        "summary": summary,
        "feedback": feedback,
    }


REPORT_HEADER = """
# 评估报告

## 摘要

- **准确率**:{correct}/{total} ({accuracy:.1f}%)
- **平均任务时长**:{average_duration_s:.2f}s
- **每个任务的平均工具调用次数**:{average_tool_calls:.2f}
- **工具调用总数**:{total_tool_calls}

---
"""

TASK_TEMPLATE = """
### 任务 {task_num}

**问题**:{question}
**标准答案**:`{expected_answer}`
**实际答案**:`{actual_answer}`
**正确**:{correct_indicator}
**时长**:{total_duration:.2f}s
**工具调用**:{tool_calls}

**摘要**
{summary}

**反馈**
{feedback}

---
"""


async def run_evaluation(
    eval_path: Path,
    connection: Any,
    model: str = "claude-3-7-sonnet-20250219",
) -> str:
    """使用 MCP 服务器工具运行评估。"""
    print("🚀 开始评估")

    client = Anthropic()

    tools = await connection.list_tools()
    print(f"📋 已从 MCP 服务器加载 {len(tools)} 个工具")

    qa_pairs = parse_evaluation_file(eval_path)
    print(f"📋 已加载 {len(qa_pairs)} 个评估任务")

    results = []
    for i, qa_pair in enumerate(qa_pairs):
        print(f"正在处理任务 {i + 1}/{len(qa_pairs)}")
        result = await evaluate_single_task(client, model, qa_pair, tools, connection, i)
        results.append(result)

    correct = sum(r["score"] for r in results)
    accuracy = (correct / len(results)) * 100 if results else 0
    average_duration_s = sum(r["total_duration"] for r in results) / len(results) if results else 0
    average_tool_calls = sum(r["num_tool_calls"] for r in results) / len(results) if results else 0
    total_tool_calls = sum(r["num_tool_calls"] for r in results)

    report = REPORT_HEADER.format(
        correct=correct,
        total=len(results),
        accuracy=accuracy,
        average_duration_s=average_duration_s,
        average_tool_calls=average_tool_calls,
        total_tool_calls=total_tool_calls,
    )

    report += "".join([
        TASK_TEMPLATE.format(
            task_num=i + 1,
            question=qa_pair["question"],
            expected_answer=qa_pair["answer"],
            actual_answer=result["actual"] or "N/A",
            correct_indicator="✅" if result["score"] else "❌",
            total_duration=result["total_duration"],
            tool_calls=json.dumps(result["tool_calls"], indent=2),
            summary=result["summary"] or "N/A",
            feedback=result["feedback"] or "N/A",
        )
        for i, (qa_pair, result) in enumerate(zip(qa_pairs, results))
    ])

    return report


def parse_headers(header_list: list[str]) -> dict[str, str]:
    """将格式为 'Key: Value' 的头部字符串解析为字典。"""
    headers = {}
    if not header_list:
        return headers

    for header in header_list:
        if ":" in header:
            key, value = header.split(":", 1)
            headers[key.strip()] = value.strip()
        else:
            print(f"Warning: Ignoring malformed header: {header}")
    return headers


def parse_env_vars(env_list: list[str]) -> dict[str, str]:
    """将格式为 'KEY=VALUE' 的环境变量字符串解析为字典。"""
    env = {}
    if not env_list:
        return env

    for env_var in env_list:
        if "=" in env_var:
            key, value = env_var.split("=", 1)
            env[key.strip()] = value.strip()
        else:
            print(f"Warning: Ignoring malformed environment variable: {env_var}")
    return env


async def main():
    parser = argparse.ArgumentParser(
        description="使用测试问题评估 MCP 服务器",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  # 评估本地 stdio MCP 服务器
  python evaluation.py -t stdio -c python -a my_server.py eval.xml

  # 评估 SSE MCP 服务器
  python evaluation.py -t sse -u https://example.com/mcp -H "Authorization: Bearer token" eval.xml

  # 使用自定义模型评估 HTTP MCP 服务器
  python evaluation.py -t http -u https://example.com/mcp -m claude-3-5-sonnet-20241022 eval.xml
        """,
    )

    parser.add_argument("eval_file", type=Path, help="评估 XML 文件的路径")
    parser.add_argument("-t", "--transport", choices=["stdio", "sse", "http"], default="stdio", help="传输类型(默认:stdio)")
    parser.add_argument("-m", "--model", default="claude-3-7-sonnet-20250219", help="要使用的 Claude 模型(默认:claude-3-7-sonnet-20250219)")

    stdio_group = parser.add_argument_group("stdio 选项")
    stdio_group.add_argument("-c", "--command", help="运行 MCP 服务器的命令(仅 stdio)")
    stdio_group.add_argument("-a", "--args", nargs="+", help="命令的参数(仅 stdio)")
    stdio_group.add_argument("-e", "--env", nargs="+", help="KEY=VALUE 格式的环境变量(仅 stdio)")

    remote_group = parser.add_argument_group("sse/http 选项")
    remote_group.add_argument("-u", "--url", help="MCP 服务器 URL(仅 sse/http)")
    remote_group.add_argument("-H", "--header", nargs="+", dest="headers", help="'Key: Value' 格式的 HTTP 头部(仅 sse/http)")

    parser.add_argument("-o", "--output", type=Path, help="评估报告的输出文件(默认:stdout)")

    args = parser.parse_args()

    if not args.eval_file.exists():
        print(f"Error: Evaluation file not found: {args.eval_file}")
        sys.exit(1)

    headers = parse_headers(args.headers) if args.headers else None
    env_vars = parse_env_vars(args.env) if args.env else None

    try:
        connection = create_connection(
            transport=args.transport,
            command=args.command,
            args=args.args,
            env=env_vars,
            url=args.url,
            headers=headers,
        )
    except ValueError as e:
        print(f"Error: {e}")
        sys.exit(1)

    print(f"🔗 Connecting to MCP server via {args.transport}...")

    async with connection:
        print("✅ Connected successfully")
        report = await run_evaluation(args.eval_file, connection, args.model)

        if args.output:
            args.output.write_text(report)
            print(f"\n✅ Report saved to {args.output}")
        else:
            print("\n" + report)


if __name__ == "__main__":
    asyncio.run(main())
FILE:scripts/example_evaluation.xml
<evaluation>
   <qa_pair>
      <question>计算以 5% 年利率投资 $10,000、按月复利、为期 3 年的复利。最终金额是多少美元(四舍五入到 2 位小数)?</question>
      <answer>11614.72</answer>
   </qa_pair>
   <qa_pair>
      <question>一个抛射体以 45 度角发射,初速度为 50 m/s。假设 g=9.8 m/s²,计算它在 2 秒后从发射点起总共行进的距离(以米为单位)。四舍五入到 2 位小数。</question>
      <answer>87.25</answer>
   </qa_pair>
   <qa_pair>
      <question>一个球体的体积为 500 立方米。计算其表面积(以平方米为单位)。四舍五入到 2 位小数。</question>
      <answer>304.65</answer>
   </qa_pair>
   <qa_pair>
      <question>计算该数据集的总体标准差:[12, 15, 18, 22, 25, 30, 35]。四舍五入到 2 位小数。</question>
      <answer>7.61</answer>
   </qa_pair>
   <qa_pair>
      <question>计算氢离子浓度为 3.5 × 10^-5 M 的溶液的 pH。四舍五入到 2 位小数。</question>
      <answer>4.46</answer>
   </qa_pair>
</evaluation>
FILE:scripts/requirements.txt
anthropic>=0.39.0
mcp>=1.1.0
PythonTypeScriptAPI+2
F@f
0
技能创建器
技能

创建有效技能的指南。当用户想要创建新技能(或更新现有技能)以通过专业知识、工作流程或工具集成扩展 Claude 的能力时,应使用此技能。

---
name: skill-creator
description: 创建有效技能的指南。当用户想要创建新技能(或更新现有技能)以通过专业知识、工作流或工具集成扩展 Claude 的能力时,应使用此技能。
license: 完整条款见 LICENSE.txt
---

# 技能创建器

此技能为创建有效技能提供指导。

## 关于技能

技能是模块化、自包含的包,通过提供专业知识、工作流和工具来扩展 Claude 的能力。可以将它们视为特定领域或任务的“入门指南”——它们将 Claude 从通用代理转变为具备任何模型都无法完全拥有的程序性知识的专业代理。

### 技能提供的内容

1. 专业工作流 - 针对特定领域的多步骤流程
2. 工具集成 - 使用特定文件格式或 API 的说明
3. 领域专业知识 - 公司特定知识、模式、业务逻辑
4. 捆绑资源 - 用于复杂和重复任务的脚本、参考资料和资产

## 核心原则

### 简洁是关键

上下文窗口是一种公共资源。技能与 Claude 所需的其他所有内容共享上下文窗口:系统提示、对话历史、其他技能的元数据以及实际用户请求。

**默认假设:Claude 已经非常聪明。** 只添加 Claude 尚未拥有的上下文。对每条信息提出质疑:“Claude 真的需要这个解释吗?”以及“这段文字是否值得其 token 成本?”

优先使用简洁的示例而非冗长的解释。

### 设置适当的自由度

将具体程度与任务的脆弱性和可变性相匹配:

**高自由度(基于文本的指令)**:当多种方法都有效、决策取决于上下文或启发式方法指导方法时使用。

**中等自由度(伪代码或带参数的脚本)**:当存在首选模式、某些变化可接受或配置影响行为时使用。

**低自由度(特定脚本、少量参数)**:当操作脆弱且容易出错、一致性至关重要或必须遵循特定顺序时使用。

将 Claude 想象为在探索一条路径:悬崖上的窄桥需要特定的护栏(低自由度),而开阔的田野允许许多路线(高自由度)。

### 技能的解剖结构

每个技能由一个必需的 SKILL.md 文件和可选的捆绑资源组成:

```
skill-name/
├── SKILL.md (必需)
│   ├── YAML frontmatter 元数据 (必需)
│   │   ├── name: (必需)
│   │   └── description: (必需)
│   └── Markdown 指令 (必需)
└── 捆绑资源 (可选)
    ├── scripts/          - 可执行代码 (Python/Bash 等)
    ├── references/       - 旨在按需加载到上下文中的文档
    └── assets/           - 输出中使用的文件 (模板、图标、字体等)
```

#### SKILL.md (必需)

每个 SKILL.md 包含:

- **Frontmatter** (YAML):包含 `name` 和 `description` 字段。这些是 Claude 用来确定技能何时被使用的唯一字段,因此清晰全面地描述技能是什么以及何时应使用它非常重要。
- **正文** (Markdown):使用技能的说明和指导。仅在技能触发后加载(如果有的话)。

#### 捆绑资源 (可选)

##### 脚本 (`scripts/`)

用于需要确定性可靠性或反复重写的任务的可执行代码 (Python/Bash 等)。

- **何时包含**:当同一代码被反复重写或需要确定性可靠性时
- **示例**:`scripts/rotate_pdf.py` 用于 PDF 旋转任务
- **优点**:token 高效、确定性、无需加载到上下文中即可执行
- **注意**:Claude 可能仍需要阅读脚本以进行修补或环境特定调整

##### 参考资料 (`references/`)

旨在按需加载到上下文中以告知 Claude 过程和思考的文档和参考材料。

- **何时包含**:用于 Claude 在工作时应参考的文档
- **示例**:`references/finance.md` 用于财务模式,`references/mnda.md` 用于公司 NDA 模板,`references/policies.md` 用于公司政策,`references/api_docs.md` 用于 API 规范
- **用例**:数据库模式、API 文档、领域知识、公司政策、详细工作流指南
- **优点**:保持 SKILL.md 精简,仅在 Claude 确定需要时加载
- **最佳实践**:如果文件较大(>10k 字),在 SKILL.md 中包含 grep 搜索模式
- **避免重复**:信息应存在于 SKILL.md 或 references 文件中,而不是两者中。

##### 资产 (`assets/`)

不打算加载到上下文中,而是在 Claude 产生的输出中使用的文件。

- **何时包含**:当技能需要将在最终输出中使用的文件时
- **示例**:`assets/logo.png` 用于品牌资产,`assets/slides.pptx` 用于 PowerPoint 模板
- **用例**:模板、图像、图标、样板代码、字体、示例文档

### 渐进式披露设计原则

技能使用三级加载系统来高效管理上下文:

1. **元数据(名称 + 描述)** - 始终在上下文中(约 100 字)
2. **SKILL.md 正文** - 当技能触发时(<5k 字)
3. **捆绑资源** - 根据 Claude 的需要

保持 SKILL.md 正文简洁,不超过 500 行,以最小化上下文膨胀。

## 技能创建过程

技能创建涉及以下步骤:

1. 通过具体示例理解技能
2. 规划可重用的技能内容(脚本、参考资料、资产)
3. 初始化技能(运行 init_skill.py)
4. 编辑技能(实现资源并编写 SKILL.md)
5. 打包技能(运行 package_skill.py)
6. 根据实际使用进行迭代

### 步骤 3:初始化技能

从头创建新技能时,始终运行 `init_skill.py` 脚本:

```bash
scripts/init_skill.py <skill-name> --path <output-directory>
```

### 步骤 4:编辑技能

根据技能需求查阅这些有用的指南:

- **多步骤流程**:参见 references/workflows.md 了解顺序工作流和条件逻辑
- **特定输出格式或质量标准**:参见 references/output-patterns.md 了解模板和示例模式

### 步骤 5:打包技能

```bash
scripts/package_skill.py <path/to/skill-folder>
```

打包脚本验证并创建用于分发的 .skill 文件。
FILE:references/workflows.md
# 工作流模式

## 顺序工作流

对于复杂任务,将操作分解为清晰的顺序步骤。在 SKILL.md 开头向 Claude 提供流程概述通常很有帮助:

```markdown
填写 PDF 表单涉及以下步骤:

1. 分析表单(运行 analyze_form.py)
2. 创建字段映射(编辑 fields.json)
3. 验证映射(运行 validate_fields.py)
4. 填写表单(运行 fill_form.py)
5. 验证输出(运行 verify_output.py)
```

## 条件工作流

对于具有分支逻辑的任务,通过决策点引导 Claude:

```markdown
1. 确定修改类型:
   **创建新内容?** → 遵循下方的“创建工作流”
   **编辑现有内容?** → 遵循下方的“编辑工作流”

2. 创建工作流:[步骤]
3. 编辑工作流:[步骤]
```
FILE:references/output-patterns.md
# 输出模式

当技能需要产生一致、高质量的输出时,使用这些模式。

## 模板模式

为输出格式提供模板。根据你的需求匹配严格程度。

**对于严格要求(如 API 响应或数据格式):**

```markdown
## 报告结构

始终使用此精确的模板结构:

# [分析标题]

## 执行摘要
[关键发现的一段概述]

## 关键发现
- 发现 1 及支持数据
- 发现 2 及支持数据
- 发现 3 及支持数据

## 建议
1. 具体的可操作建议
2. 具体的可操作建议
```

**对于灵活指导(当适应有用时):**

```markdown
## 报告结构

这是一个合理的默认格式,但请使用你的最佳判断:

# [分析标题]

## 执行摘要
[概述]

## 关键发现
[根据你的发现调整章节]

## 建议
[针对具体上下文定制]

根据需要为特定分析类型调整章节。
```

## 示例模式

对于输出质量取决于查看示例的技能,提供输入/输出对:

```markdown
## 提交信息格式

按照这些示例生成提交信息:

**示例 1:**
输入:使用 JWT 令牌添加了用户身份验证
输出:
```
feat(auth): 实现基于 JWT 的身份验证

添加登录端点和令牌验证中间件
```

**示例 2:**
输入:修复了报告中日期显示不正确的错误
输出:
```
fix(reports): 修正时区转换中的日期格式

在报告生成中一致使用 UTC 时间戳
```

遵循此风格:类型(范围): 简要描述,然后详细说明。
```

示例比仅描述更能帮助 Claude 理解所需的风格和详细程度。
FILE:scripts/quick_validate.py
#!/usr/bin/env python3
"""
技能快速验证脚本 - 最小版本
"""

import sys
import os
import re
import yaml
from pathlib import Path

def validate_skill(skill_path):
    """技能的基本验证"""
    skill_path = Path(skill_path)

    # 检查 SKILL.md 是否存在
    skill_md = skill_path / 'SKILL.md'
    if not skill_md.exists():
        return False, "未找到 SKILL.md"

    # 读取并验证 frontmatter
    content = skill_md.read_text()
    if not content.startswith('---'):
        return False, "未找到 YAML frontmatter"

    # 提取 frontmatter
    match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
    if not match:
        return False, "无效的 frontmatter 格式"

    frontmatter_text = match.group(1)

    # 解析 YAML frontmatter
    try:
        frontmatter = yaml.safe_load(frontmatter_text)
        if not isinstance(frontmatter, dict):
            return False, "Frontmatter 必须是 YAML 字典"
    except yaml.YAMLError as e:
        return False, f"Frontmatter 中的 YAML 无效:{e}"

    # 定义允许的属性
    ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata'}

    # 检查意外属性(排除 metadata 下的嵌套键)
    unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
    if unexpected_keys:
        return False, (
            f"SKILL.md frontmatter 中的意外键:{', '.join(sorted(unexpected_keys))}。 "
            f"允许的属性为:{', '.join(sorted(ALLOWED_PROPERTIES))}"
        )

    # 检查必填字段
    if 'name' not in frontmatter:
        return False, "frontmatter 中缺少 'name'"
    if 'description' not in frontmatter:
        return False, "frontmatter 中缺少 'description'"

    # 提取名称进行验证
    name = frontmatter.get('name', '')
    if not isinstance(name, str):
        return False, f"名称必须是字符串,得到 {type(name).__name__}"
    name = name.strip()
    if name:
        # 检查命名约定(连字符格式:小写字母和连字符)
        if not re.match(r'^[a-z0-9-]+$', name):
            return False, f"名称 '{name}' 应为连字符格式(仅小写字母、数字和连字符)"
        if name.startswith('-') or name.endswith('-') or '--' in name:
            return False, f"名称 '{name}' 不能以连字符开头/结尾或包含连续连字符"
        # 检查名称长度(根据规范最大 64 个字符)
        if len(name) > 64:
            return False, f"名称太长({len(name)} 个字符)。最大为 64 个字符。"

    # 提取并验证描述
    description = frontmatter.get('description', '')
    if not isinstance(description, str):
        return False, f"描述必须是字符串,得到 {type(description).__name__}"
    description = description.strip()
    if description:
        # 检查尖括号
        if '<' in description or '>' in description:
            return False, "描述不能包含尖括号(< 或 >)"
        # 检查描述长度(根据规范最大 1024 个字符)
        if len(description) > 1024:
            return False, f"描述太长({len(description)} 个字符)。最大为 1024 个字符。"

    return True, "技能有效!"

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法:python quick_validate.py <skill_directory>")
        sys.exit(1)
    
    valid, message = validate_skill(sys.argv[1])
    print(message)
    sys.exit(0 if valid else 1)
FILE:scripts/init_skill.py
#!/usr/bin/env python3
"""
技能初始化器 - 从模板创建新技能

用法:
    init_skill.py <skill-name> --path <path>

示例:
    init_skill.py my-new-skill --path skills/public
    init_skill.py my-api-helper --path skills/private
    init_skill.py custom-skill --path /custom/location
"""

import sys
from pathlib import Path


SKILL_TEMPLATE = """---
name: {skill_name}
description: [TODO: 完整且信息丰富的解释,说明该技能的作用以及何时使用它。包括何时使用此技能 - 触发它的具体场景、文件类型或任务。]
---

# {skill_title}

## 概述

[TODO: 1-2 句话解释此技能能实现什么]

## 资源

此技能包含示例资源目录,演示如何组织不同类型的捆绑资源:

### scripts/
可执行代码(Python/Bash 等),可直接运行以执行特定操作。

### references/
旨在加载到上下文中以告知 Claude 过程和思考的文档和参考资料。

### assets/
不打算加载到上下文中,而是在 Claude 产生的输出中使用的文件。

---

**可以删除任何不需要的目录。** 并非每项技能都需要全部三种类型的资源。
"""

EXAMPLE_SCRIPT = '''#!/usr/bin/env python3
"""
{skill_name} 的示例辅助脚本

这是一个可以直接执行的占位脚本。
请替换为实际实现,或在不需要时删除。
"""

def main():
    print("这是 {skill_name} 的示例脚本")
    # TODO: 在此添加实际的脚本逻辑

if __name__ == "__main__":
    main()
'''

EXAMPLE_REFERENCE = """# {skill_title} 的参考文档

这是详细参考文档的占位符。
请替换为实际的参考内容,或在不需要时删除。
"""

EXAMPLE_ASSET = """# 示例资源文件

此占位符表示资源文件的存储位置。
请替换为实际的资源文件(模板、图片、字体等),或在不需要时删除。
"""


def title_case_skill_name(skill_name):
    """将带连字符的技能名称转换为标题大小写以便显示。"""
    return ' '.join(word.capitalize() for word in skill_name.split('-'))


def init_skill(skill_name, path):
    """使用模板 SKILL.md 初始化新的技能目录。"""
    skill_dir = Path(path).resolve() / skill_name

    if skill_dir.exists():
        print(f"❌ 错误:技能目录已存在:{skill_dir}")
        return None

    try:
        skill_dir.mkdir(parents=True, exist_ok=False)
        print(f"✅ 已创建技能目录:{skill_dir}")
    except Exception as e:
        print(f"❌ 创建目录时出错:{e}")
        return None

    skill_title = title_case_skill_name(skill_name)
    skill_content = SKILL_TEMPLATE.format(skill_name=skill_name, skill_title=skill_title)

    skill_md_path = skill_dir / 'SKILL.md'
    try:
        skill_md_path.write_text(skill_content)
        print("✅ 已创建 SKILL.md")
    except Exception as e:
        print(f"❌ 创建 SKILL.md 时出错:{e}")
        return None

    try:
        scripts_dir = skill_dir / 'scripts'
        scripts_dir.mkdir(exist_ok=True)
        example_script = scripts_dir / 'example.py'
        example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name))
        example_script.chmod(0o755)
        print("✅ 已创建 scripts/example.py")

        references_dir = skill_dir / 'references'
        references_dir.mkdir(exist_ok=True)
        example_reference = references_dir / 'api_reference.md'
        example_reference.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title))
        print("✅ 已创建 references/api_reference.md")

        assets_dir = skill_dir / 'assets'
        assets_dir.mkdir(exist_ok=True)
        example_asset = assets_dir / 'example_asset.txt'
        example_asset.write_text(EXAMPLE_ASSET)
        print("✅ 已创建 assets/example_asset.txt")
    except Exception as e:
        print(f"❌ 创建资源目录时出错:{e}")
        return None

    print(f"\n✅ 技能 '{skill_name}' 已在 {skill_dir} 成功初始化")
    return skill_dir


def main():
    if len(sys.argv) < 4 or sys.argv[2] != '--path':
        print("用法:init_skill.py <skill-name> --path <path>")
        sys.exit(1)

    skill_name = sys.argv[1]
    path = sys.argv[3]

    print(f"🚀 正在初始化技能:{skill_name}")
    print(f"   位置:{path}")
    print()

    result = init_skill(skill_name, path)
    sys.exit(0 if result else 1)


if __name__ == "__main__":
    main()
FILE:scripts/package_skill.py
#!/usr/bin/env python3
"""
技能打包器 - 为技能文件夹创建可分发的 .skill 文件

用法:
    python utils/package_skill.py <path/to/skill-folder> [output-directory]

示例:
    python utils/package_skill.py skills/public/my-skill
    python utils/package_skill.py skills/public/my-skill ./dist
"""

import sys
import zipfile
from pathlib import Path
from quick_validate import validate_skill


def package_skill(skill_path, output_dir=None):
    """将技能文件夹打包为 .skill 文件。"""
    skill_path = Path(skill_path).resolve()

    if not skill_path.exists():
        print(f"❌ 错误:未找到技能文件夹:{skill_path}")
        return None

    if not skill_path.is_dir():
        print(f"❌ 错误:路径不是目录:{skill_path}")
        return None

    skill_md = skill_path / "SKILL.md"
    if not skill_md.exists():
        print(f"❌ 错误:在 {skill_path} 中未找到 SKILL.md")
        return None

    print("🔍 正在验证技能...")
    valid, message = validate_skill(skill_path)
    if not valid:
        print(f"❌ 验证失败:{message}")
        print("   请在打包前修复验证错误。")
        return None
    print(f"✅ {message}\n")

    skill_name = skill_path.name
    if output_dir:
        output_path = Path(output_dir).resolve()
        output_path.mkdir(parents=True, exist_ok=True)
    else:
        output_path = Path.cwd()

    skill_filename = output_path / f"{skill_name}.skill"

    try:
        with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for file_path in skill_path.rglob('*'):
                if file_path.is_file():
                    arcname = file_path.relative_to(skill_path.parent)
                    zipf.write(file_path, arcname)
                    print(f"  已添加:{arcname}")

        print(f"\n✅ 已成功将技能打包至:{skill_filename}")
        return skill_filename

    except Exception as e:
        print(f"❌ 创建 .skill 文件时出错:{e}")
        return None


def main():
    if len(sys.argv) < 2:
        print("用法:python utils/package_skill.py <path/to/skill-folder> [output-directory]")
        sys.exit(1)

    skill_path = sys.argv[1]
    output_dir = sys.argv[2] if len(sys.argv) > 2 else None

    print(f"📦 正在打包技能:{skill_path}")
    if output_dir:
        print(f"   输出目录:{output_dir}")
    print()

    result = package_skill(skill_path, output_dir)
    sys.exit(0 if result else 1)


if __name__ == "__main__":
    main()
AgentSkillMeta+1
F@f
0
创意生成
文本

通过头脑风暴和生成初步想法来启动你的工作流程

你是一位创意头脑风暴助手。帮助用户为他们的项目生成创新想法。

1. 就 topic 提出澄清性问题
2. 生成 5-10 个多样化的想法
3. 评估每个想法的可行性和影响力
4. 推荐最值得追求的 3 个想法

发挥创意,跳出思维定式,鼓励非常规的方法。
F@f
0
第2步:大纲创建
文本

根据生成的想法创建结构化大纲

基于上一步生成的想法,创建详细大纲。

按以下结构组织你的大纲:
- 主要章节和小节
- 需要涵盖的关键点
- 每个章节的预计时间/工作量
- 章节之间的依赖关系

以清晰的分层结构格式化大纲。
F@f
0
步骤 3a:技术深入分析
文本

技术路径:分析技术需求与可行性

对所述项目进行技术分析。

分析:
- 技术需求与依赖关系
- 架构考量
- 潜在技术挑战
- 所需工具与技术
- 性能影响

提供详细的技术评估及建议。
F@f
0
步骤 3b:创意探索
文本

创意路径:探索创意方向与叙事表达

探索所规划项目的创意维度。

重点关注:
- 叙事与故事讲述元素
- 视觉与美学考量
- 情感影响与用户参与度
- 独特的创意角度
- 从其他作品中汲取灵感

生成让项目焕发生机的创意概念。
F@f
0
步骤 4a:实施计划
文本

基于技术分析创建详细的实施计划

创建全面的实施计划。

包括:
- 阶段划分与里程碑
- 带优先级的任务列表
- 资源分配
- 风险缓解策略
- 时间线估算
- 成功指标

格式化为可执行的项目计划。
F@f
0
第4b步:故事开发
文本

基于创意探索开发叙事和内容

基于创意探索开发完整的故事和内容。

开发:
- 完整的叙事弧线
- 角色或元素描述
- 关键场景或时刻
- 对话或文案
- 视觉描述
- 情感节拍

创作引人入胜、富有吸引力的内容。
F@f
0
第5步:最终审核
文本

审核并合并所有工作流以进行最终质量检查

执行全面的最终审核,合并所有工作流。

审核清单:
- 技术可行性已确认
- 创意愿景已对齐
- 所有要求均已满足
- 质量标准已达成
- 所有元素保持一致
- 已准备好发布

提供最终评估及任何最后的建议。
F@f
0
第 6 步:发布
文本

最后一步:准备并执行发布

准备最终交付物以供发布。

最后步骤:
- 针对目标平台进行格式化
- 创建配套材料
- 设置分发
- 准备公告
- 安排发布时间
- 监控初步反馈

恭喜你完成整个工作流程!
F@f
0
视频
使用 Remotion 制作 prompts.chat 宣传视频

本提示词指导使用 Remotion 为 prompts.chat 制作一段 30 秒的宣传视频。它概述了所需的素材、颜色主题、字体样式和场景结构,以有效展示平台的功能和社区。该提示词包含动画技术和转场效果,以确保流畅、引人入胜的观看体验,并强调平台的全球覆盖范围和可用的各种提示词类型。

为 prompts.chat 制作一个 30 秒的宣传视频

所需素材

- https://prompts.chat/logo.svg - Logo SVG
- https://raw.githubusercontent.com/flekschas/simple-world-map/refs/heads/master/world-map.svg - 用于全球社区场景的世界地图 SVG

配色主题(浅色)

- 背景:#ffffff
- 背景替代色:#f8fafc
- 主色:#6366f1(靛蓝)
- 主色浅色:#818cf8
- 强调色:#22c55e(绿色)
- 文字:#0f172a
- 文字弱化色:#64748b

字体

- Inter(字重:400、600、700、800)

---
场景结构(8 个场景)

场景 1:开场(5 秒)

- Logo 出现
- Logo 居中,以弹簧动画缩放进入
- 动画结束后:"prompts.chat" 文字使用 clip-path 在 Logo 下方从左到右显现
- 标语出现:"The Free Social Platform for AI Prompts"

场景 2:全球社区(4 秒)

- 全屏世界地图(25% 不透明度)作为背景
- 16 个脉冲活动圆点位于主要城市(洛杉矶、纽约、多伦多、圣保罗、伦敦、巴黎、柏林、拉各斯、莫斯科、迪拜、孟买、北京、东京、新加坡、悉尼、华沙)
- 每个圆点都有外脉冲环、内脉冲和带光晕的中心圆点
- 标题:"A global community of prompt creators"
- 数据行:8k+ 用户、3k+ 日访客、1k+ 提示词、300+ 贡献者、10+ 种语言
- 底部渐变叠加层以提升文字可读性

场景 3:解决方案(2.5 秒)

- 三个词以弹簧动画依次出现:"Discover." "Share." "Collect."
- 每个词使用不同颜色(主色、强调色、主色浅色)

场景 4:为每个人而建(4 秒)

- 8 个浮动人物图标围绕屏幕边缘,使用正弦/余弦波浪浮动动画
- 人物:学生、教师、研究人员、开发者、艺术家、作家、营销人员、创业者
- 每个都有 130x130 图标容器,带彩色背景/边框
- 中心标题:"Built for everyone"
- 副标题:"One prompt away from your next breakthrough."

场景 5:提示词类型(5 秒)

- 标题:"Prompts for every need"
- 浏览器风格框架(1400x800),带 macOS 交通灯按钮和显示 "prompts.chat" 的 URL 栏
- 一个瀑布流骨架截图以缓动动画(三次缓入缓出)垂直滚动
- 7 个浮动胶囊形标签围绕边缘,带图标:
  - 文本(紫色)、图像(粉色)、视频(琥珀色)、音频(绿色)、工作流(紫罗兰色)、技能(青色)、JSON(红色)

场景 6:功能(4 秒)

- 4 张功能卡片以弹簧动画依次出现:
  - 提示词库(书本图标)- "Thousands of prompts across all categories"
  - 技能与工作流(闪电图标)- "Automate multi-step AI tasks"
  - 社区(用户图标)- "Share and discover from creators"
  - 开源(圆形加号图标)- "Self-host with complete privacy"

场景 7:社会证明(4 秒)

- 动画 GitHub 星标计数器(0 → 143,000+)
- 计数旁有星标图标
- 徽章:"The First Prompt Library — Since December 2022",带奖杯图标
- 文字:"Endorsed by OpenAI co-founders • Used by Harvard, Columbia & more"

场景 8:行动号召(3.5 秒)

- 背景光晕动画(脉冲径向渐变)
- 标题:"Start exploring today"                                         
- 带徽标 + “prompts.chat”文字的大按钮(渐变背景,轻微脉冲)                                                                        
- 副标题:“免费且开源”                                              
                                                                              
---                                                                           
转场(每个0.4秒)                                                       
                                                                              
- 场景1→2:淡入淡出                                                             
- 场景2→3:从右侧滑入                                                 
- 场景3→4:淡入淡出                                                             
- 场景4→5:淡入淡出                                                             
- 场景5→6:从右侧滑入                                                 
- 场景6→7:从底部滑入                                                
- 场景7→8:淡入淡出                                                             
                                                                              
使用的动画技术                                                     
                                                                              
- spring() 用于弹性缩放动画                                        
- interpolate() 用于不透明度、位置和裁剪路径                          
- Easing.inOut(Easing.cubic) 用于平滑滚动                                
- Math.sin()/Math.cos() 用于浮动动画                               
- 交错延迟用于元素顺序出现                         
                                                                              
关键组件                                                                
                                                                              
- 所有图标使用自定义SVG图标组件(无表情符号)                        
- 带有prompts.chat “P”路径的徽标组件                                   
- FeatureCard可复用组件                                              
- 用于场景管理的TransitionSeries
F@f
0
贴纸图像生成器
图片
贴纸图像生成器

生成一张带有透明背景的多彩贴纸图像,文字和图标可自定义,风格类似 Stickermule。

1{
2 "role": "图像设计师",
3 "task": "创建一张带有透明背景的精细贴纸图像。",
...+27行
CreativeDesign
F@f
0
雨中超写实浪漫电影场景
图片
雨中超写实浪漫电影场景

创建一个超写实的电影场景,一对浪漫情侣站在雨中的黄色雨伞下。用高端电影元素捕捉情感联系和氛围,确保面部特征保持不变。

面部必须与参考完全一致,绝对身份锁定:不换脸、不美化、不校正对称、不改变年龄、不磨皮、不改变表情,保持相同的面部比例、眼睛、鼻子、嘴唇、下颌线和自然纹理。雨中的超写实电影夜景,一对浪漫情侣在柔和灯光的花园中紧挨着站在黄色雨伞下。大雨倾盆,被温暖的金色童话灯和路灯照亮,在背景中形成梦幻的散景,潮湿的地面反射着光线。男人撑着伞,用温柔、充满爱意的目光看着女人,而女人则用柔和、温暖、浪漫的微笑抬头看着他。他们从不中断眼神交流,完全沉浸在彼此之中,传达出深厚的情感联系。优雅的外套被雨微微打湿,逼真的面料纹理,微妙的轮廓光勾勒出他们的脸庞,可见的雨滴和薄雾,浅景深,50mm镜头效果,自然的胶片颗粒,高端电影调色。只有光线、氛围和环境可以改变——面部和身份必须保持完全不变并完美保留。
F@f
0
视频
浪漫雨景视频

创建一个浪漫的视频场景,两个人站在雨中,凝视彼此的眼睛。用雨滴声和柔和浪漫的氛围来营造意境。

他们站在雨中,浪漫地注视着彼此。雨滴在他们周围落下,柔和的雨声充满整个氛围。
F@f
0
将主体转化为可爱的毛绒形态
图片
将主体转化为可爱的毛绒形态

此提示词可将任何主体或图像转化为可爱的毛绒形态,强调柔软的质感和圆润的形状。它保留独特特征以确保可辨识度,尤其是人物,同时使用毛毡或抓绒质感营造可爱的毛绒玩具外观。转化包括简单的形状、精心制作的面部细节以及柔和或中性的配色,最终呈现出迷人、值得收藏且适合拥抱的毛绒玩具。非常适合捕捉原始主体的温暖与可爱。

将主体或图像转化为具有柔软质感和圆润形状的可爱毛绒形态。如果图像中包含人物,请保留其独特特征,使主体仍然可辨识。否则,使用毛毡或抓绒质感将物体或动物变成可爱的毛绒玩具。它应具有温暖的毛毡或抓绒外观、简单的形状,以及轻柔制作的眼睛、嘴巴和面部细节。使用温馨的柔和或中性配色、平滑的阴影和细腻的缝线,营造手工毛绒玩具的感觉。赋予它友好可爱的面部表情、略微偏大的头部、短小的四肢以及柔软、适合拥抱的轮廓。最终图像应感觉迷人、值得收藏,像真正的毛绒玩具。它应该可爱、温馨、令人想拥抱,同时仍清晰保留原始主体的可辨识度。
F@f
0
prompts.chat 品味
文本

prompts.chat 的品味与写作约定。

prompts.chat 的品味:提示词应清晰、可复用、结构良好;标题具体;描述说明何时用;正文保留占位符与可执行约束。
F@f
0
带有 WhatsApp 聊天的真实业余手机照片
图片
带有 WhatsApp 聊天的真实业余手机照片
创建一张真实、拍摄不佳的业余照片,展示一部实体智能手机屏幕上显示 WhatsApp 聊天。

手机应单手垂直握持,可见深色边框/外壳,温暖昏暗的室内光线,轻微倾斜、模糊、颗粒感、眩光、反光、对焦不均和构图不完美。它必须看起来像一张糟糕的真实手机屏幕照片,而不是干净的截图。

在手机屏幕上,显示一个 iPhone 风格的土耳其语 WhatsApp 对话,联系人名称为 receiver_name,并附有一张小头像照片(如果未提供,则使用默认 WhatsApp 个人资料图标)。

聊天主题:
talk_subject

根据上述主题自然生成 WhatsApp 对话。联系人的消息应使用 Turkish 语言和 talk_style(例如,带有拼写错误和生硬措辞的不流利 Turkish。我的消息应为正确的 Turkish,无拼写错误)。使用逼真的白色接收气泡、绿色发送气泡、时间戳、蓝色双勾标记,以及底部的 WhatsApp 输入栏。

保持屏幕可读但略微模糊,就像拍摄不佳的手机屏幕。
F@f
0
会议邀请邮件
文本

生成专业会议邀请邮件。

生成专业的会议/大会邀请邮件:主题、时间地点、议程、RSVP 与附件说明,语气得体。
F@f
0
家庭密室逃脱设计师
文本

设计完整可打印的家庭密室:故事、连锁谜题、房间布置、提示阶梯与主持脚本,贴合玩家、空间与预算。

你是「家庭密室逃脱设计师」。请按下列中文说明执行;完整原提示词正文保留于后,供结构、占位符与技术细节对齐。

## 中文任务说明
设计完整可打印的家庭密室:故事、连锁谜题、房间布置、提示阶梯与主持脚本,贴合玩家、空间与预算。

以下保留原提示词正文供结构与占位符对齐:

I want you to act as an experienced escape room designer who builds immersive, low-budget escape games that can be played at home, in a classroom, or in an office.

Design a complete escape room for me using these details:
- Occasion & players: Birthday party for 6 adults
- Theme: A 1920s detective's locked study
- Space available: One living room and a hallway
- Duration: 45 minutes
- Difficulty: Medium — first-timers welcome
- Budget & materials: Under $20, mostly household items, paper and a printer

Deliver the design in this structure:

1. **Story Hook** — A 3–4 sentence intro the host reads aloud, establishing the stakes, the goal, and why the clock is ticking.
2. **Puzzle Flow Map** — A step-by-step chain (5–8 puzzles) showing how each solution unlocks the next clue. Mix puzzle types (logic, observation, ciphers, physical search, wordplay, teamwork). Include at least one parallel branch so the whole group stays busy.
3. **Puzzle Details** — For each puzzle: name, what the players find, how it is solved, the exact answer, required props, and how to make the props cheaply. Provide any cipher text, riddles, or printable content in full.
4. **Room Setup Checklist** — Where every item is hidden, in setup order, so the host can prepare in under 30 minutes.
5. **Hint Ladder** — Three escalating hints per puzzle (nudge → direction → near-solution).
6. **Host Script & Timing** — When to offer hints, how to handle players who are stuck or racing ahead, and a dramatic finale moment.
7. **Accessibility & Safety Notes** — Adaptations for kids, color-blind players, or limited mobility, and a reminder that nothing should require force, climbing, or real locks that cannot be opened.

Rules:
- Every puzzle must be fair and solvable from in-game information only — no outside knowledge.
- Avoid red herrings that waste more than a couple of minutes.
- Keep the story consistent: every clue should feel like it belongs in the world.
- At the end, ask me if I want a printable one-page prop sheet or a harder "expert mode" variant.
F@f
0
复古怀表内的微缩世界
图片
复古怀表内的微缩世界

一张微距照片,展示在打开的古董怀表内部构建的微小、鲜活的风景,齿轮化作地形,指针化作桥梁。更改场景变量即可将任意世界放入其中。

一张超精细的微距照片,展示一只打开的复古黄铜怀表放置在风化的木桌上。在表壳内部,没有表盘,取而代之的是一个微小的鲜活世界:一个雾气缭绕的高山峡谷,蜿蜒的河流、松林和一座小石头村庄。怀表的齿轮融入风景之中,化作梯田山丘和水车,指针在河上形成一座精致的桥梁。村庄的窗户里透出微小的暖光。柔和的黄金时刻光线从左侧照入,浅景深,奶油般的散景背景,光束中漂浮着尘埃颗粒。雕刻的表盖打开着,投下柔和的阴影。照片级真实感,移轴微缩模型质感,划痕黄铜与玻璃的丰富纹理,电影级调色,16:9 构图。
PhotographyMacro
F@f
0
Previous4 / 25Next