mcpField
D
design-api

design-api

github.com/paraskakis开发4337 次安装2 周前更新
代码仓库

关于 design-api

一项将需求推进到用户故事、OpenAPI 生成及可选 lint 的 API 设计技能,面向使用 LLM 设计 API 的人员。它区分必需的输入文档与可选故事,并在生成规范前提供审阅环节。

主要功能

  1. 01从需求到规范的工作流
  2. 02生成故事并设置审阅环节
  3. 03生成 OpenAPI 3.1.0
  4. 04验证架构与引用
  5. 05可选自动 lint

适用场景

  1. 01将 API 需求整理为待审阅的故事
  2. 02根据已批准故事生成规范
  3. 03检查 OpenAPI 文档的结构问题
  4. 04在具备 Node.js 时本地预览规范

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/paraskakis/skills --skill design-api

安装后在 Claude Code 中输入/design-api确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/examples/
API-standards.md
conference-domain.md
conference-research.md
OpenAPI-best-practices.md

详细介绍

设计 API

端到端设计 API:从需求到用户故事、OpenAPI 规范、lint 检查和迭代。这是 Emmanuel Paraskakis 使用大模型设计 API 的方法。

四个基础文件(需求、领域、标准、OpenAPI 最佳实践)始终必需。用户故事是可选的——如果提供,它们代表人的设计决策,技能会直接进入规范生成阶段。如果未提供,技能会先生成用户故事,然后暂停以供审核。

范围与路由

触发条件由 frontmatter 描述定义。范围内:从需求到 lint 检查后的规范的完整流程。范围外:用户只需要一种产物时——改用对应的单项技能(design-api-stories、design-api-spec 或 design-api-review)。

在当前流程中执行各阶段。 下文各阶段概述了相关的同级技能(design-api-stories、design-api-spec)。不要将这些技能作为单独的技能调用重新执行——本技能自身的信息收集会取代它们。如果已安装同级技能的 SKILL.md 文件,而某阶段的概要不足以完成任务,请查阅这些文件;否则,此处内容足以执行。

可选依赖项

核心技能(用户故事 + 规范生成)无需额外工具。若要获得完整体验:

  • RateMyOpenAPI API 密钥 — 支持自动 lint 检查和评分。可在 api.ratemyopenapi.com/docs 免费注册。将其设为 RMOA_API_KEY 环境变量。
  • Node.js — 可通过 npx http-server 在本地预览 Swagger UI。
  • curl + python3 — 用于调用 RMOA API。几乎总是已预装。

如果没有这些依赖项,技能仍会生成完整的 OpenAPI 规范——只是会跳过 lint 检查和本地预览。

输入

用户必须提供(或指明)以下文件。文件可以使用任意名称——根据 # 标题 标题或文件内容识别各文件,而不是根据文件名。

始终必需:

  1. 需求 — ICP、需求、痛点、用例、收益。“为什么”以及“是什么”。查找类似 # Requirements、# Research 的标题。
  2. 领域 — 数据对象、属性、枚举、对象关系。作为 schema 的事实来源。查找类似 # Domain、# Domain Model 的标题。
  3. API 标准 — 风格指南、命名、路径、安全性、错误处理。作为约定的事实来源。查找类似 # API Standards、# Style Guide 的标题。
  4. OpenAPI 最佳实践 — 格式规则、DRY 原则、SDK 兼容性、组件结构。作为 OpenAPI 文档质量的事实来源。查找类似 # OpenAPI Best Practices、# OAS Best Practices 的标题。

可选(决定入口): 5. 用户故事 — 包含方法、资源和参数的 API 用户故事。它们体现人的设计决策,包括资源嵌套、端点分组和参数选择。查找类似 # Stories、# User Stories 的标题。如果提供 → 跳到阶段 2(规范)。如果未提供 → 阶段 1 会生成用户故事供审核。

如果缺少任何必需文件,请先请用户提供。

如果用户没有自己的文件,请使用 references/examples/ 中附带的会议示例。不要另行编造领域。 用户没有表达偏好,并不意味着可以自行编造——请端到端运行会议排期 API,让用户看到这套方法如何应用于一组完整且已验证的输入。如果用户明确指定某个领域,则可自行构造该领域。

附带示例

references/examples/ 中包含一套完整的示例输入:

  • conference-research.md — 需求
  • conference-domain.md — 领域模型
  • API-standards.md — API 标准
  • OpenAPI-best-practices.md — OpenAPI 最佳实践

阶段 1:用户故事(如果已提供用户故事,则跳过)

步骤 1:读取输入

完整阅读需求、领域和标准。了解:

  • API 的使用者是谁(来自需求)
  • 存在哪些实体及其关系(来自领域)
  • 应遵循哪些约定(来自标准)

步骤 2:确定主题

将需求中的用例归类为逻辑主题。每个主题对应一组相关的 API 操作。此处不要停下——立即继续执行步骤 3。

例外:如果用户明确要求分步审查,则先展示主题并等待。

步骤 3:生成故事(默认 = 简单)

默认情况下,生成简单故事——内容仅需足以审查 API 结构并验证设计方向。

简单故事格式(默认)

对于每个故事,仅输出:

#### Story [Theme].[Number]: [Name]

**As a** [actor from requirements],
**I want** to [action],
**So that** [business value].

* **Method:** `GET` / `POST` / `PUT` / `PATCH` / `DELETE`
* **Resource:** `/path/{id}/subresource`
* **Parameters:**
  * `paramName` (type, required/optional): Brief description.

简单故事规则:

  • 方法和资源必须遵循 API 标准(命名、嵌套、复数形式)
  • 参数包括路径参数、查询参数和请求正文中的字段
  • 将每个参数标记为必填或可选
  • 严格使用领域对象名称和属性名称
  • 不要包含响应架构、错误代码、标头或实现细节
  • 除非标准规定了特殊要求,否则不要包含身份验证详情
详细故事格式(按需)

如果用户要求提供详细故事(例如 --detailed),则添加:

  • 成功响应摘要(状态代码 + 返回内容)
  • 错误响应(状态代码 + 发生条件)
  • 标头(速率限制、分页游标)
  • 分页方式说明

步骤 4:审查检查点

一次性展示所有故事,然后询问:

“以下是故事。在我生成规范之前,需要修改吗?还是可以继续?”

  • 如果用户提出修改 → 纳入修改,重新展示并再次询问。
  • 如果用户表示可以继续 → 立即将最终故事保存到文件(在单独的回复中,并在进行任何规范生成思考之前),然后进入第 2 阶段。
  • 如果用户传入 --no-pause → 跳过检查点并直接继续。

重要:始终先将最终审查通过的故事保存到文件,再生成规范。保存的故事是第 2 阶段的事实来源——规范必须体现用户在审查期间提出的任何修改。

先保存故事,再生成规范——分开回复。 用户批准故事后,立即通过独立的工具调用将其写入文件。不要将保存故事与生成规范合并处理——规范需要大量推理时间,合并处理会不必要地延迟故事的保存。先保存故事能向用户展示进度,并在规范生成失败时为其提供检查点。

将故事保存到用户指定的文件中;如果用户未指定,则询问他们希望保存到哪里。

直接写入——不要先检查或读取现有文件。 如果写入工具因名称冲突而失败,则在文件名后追加数字并重试。


第 2 阶段:规范

步骤 5:读取所有输入

完整读取故事、领域、标准和 OpenAPI 最佳实践。建立以下内容的整体理解:

  • 故事中的每个端点(方法 + 资源 + 参数)
  • 领域中的每个数据对象和枚举
  • 标准中的每项约定
  • 最佳实践中的每项格式规则

步骤 6:生成 OpenAPI 文档

输出一个有效的 JSON 文件。除非用户指定其他版本,否则默认使用 OpenAPI 3.1.0。

结构

按以下顺序排列文档内容:

  1. openapi — "3.1.0"(或用户指定的版本)
  2. info — 标题、描述、版本、联系信息、许可证(遵循最佳实践)
  3. servers — 至少包含一个带有描述的服务
  4. security — 如果标准要求身份验证,则添加全局安全方案
  5. tags — 每个主题对应一个标签
  6. paths — 故事中的所有端点
  7. components — schemas、parameters、responses、headers、securitySchemes
Schema 生成规则
  • 每个领域对象都要成为一个组件架构。 使用领域文件中的确切属性名称。
  • 每个枚举都要成为一个组件架构。 不要内联枚举。
  • 集合响应需要有自己的架构。 例如,InstrumentCollection 包含 data 数组和分页字段。
  • 错误响应使用 RFC 9457(Problem Details), 除非标准另有规定。
  • 不要使用内联架构。 将所有内容提取到 components/schemas,并通过 $ref 引用。
  • 可复用参数放在 components/parameters 中。 分页参数(cursor、limit)、常用筛选条件等应定义一次,并在各处引用。
  • 可复用响应对象放在 components/responses 中。 错误响应(400、401、404、429、500)应按不同结构各定义一次。
  • 可复用标头放在 components/headers 中。 例如速率限制标头等。
内容规则
  • 每个操作、参数、架构、属性、响应和标头都必须有非空的 description。
  • 每个架构和参数都必须有 example 值。使用领域文件中的真实示例。
  • 每个操作都必须有 summary 和 operationId。
  • 错误示例必须与错误类型相符(429 示例应描述速率限制,而不是“错误请求”)。
  • 积极使用 $ref。文档应遵循 DRY 原则。
命名规则
  • operationId:使用与故事中的操作匹配的 camelCase 动词(例如,searchInstruments、getInstrumentByFigi)
  • 标签:与故事中的主题名称匹配
  • 架构:使用与领域对象名称匹配的 PascalCase
  • 参数:使用与领域属性名称匹配的 camelCase
不要添加的内容
  • 不要添加故事中没有的端点
  • 不要添加领域中没有的属性
  • 不要添加标准中没有的安全方案
  • 如果故事很简单(未列出错误代码),仍需添加标准错误响应(集合端点带筛选条件时添加 400,单资源端点添加 404,要求身份验证时添加 401,添加 429 速率限制,以及 500 服务器错误)

第 7 步:验证 JSON

写入文件之前:

  1. 确保输出是有效的 JSON(没有尾随逗号或重复键)
  2. 确保每个 $ref 都指向一个存在的组件
  3. 确保每个组件至少被引用一次(没有孤立组件)
  4. 确保不使用 nullable 关键字(按照 3.1.0 的规定,改用 "type": ["string", "null"])

第 8 步:保存规范

将规范保存为 JSON,保存到用户指定的位置;或者询问用户希望保存到哪里。无需遵循特定的命名约定。

直接写入——不要先检查或读取现有文件。 如果 Write 工具因名称冲突而失败,则在名称后追加一个数字并重试。

第 9 步:使用 RateMyOpenAPI 进行 Lint 检查(可选)

此步骤需要 RateMyOpenAPI 的 API 密钥。在 api.ratemyopenapi.com/docs 注册免费账户——登录后,API 密钥会显示在 Authentication 部分。将其设为环境变量 RMOA_API_KEY(也可以使用其他名称——只需相应更新下面的 curl 命令)。

如果未设置 RMOA_API_KEY,请先尝试加载用户的 shell 配置文件(source ~/.zshrc、source ~/.bashrc 或同等文件)——密钥可能已在其中定义,只是尚未载入当前会话。如果仍未设置,请告知用户:“未找到 RMOA API 密钥。跳过自动 Lint 检查。你可以在 ratemyopenapi.com 获取免费密钥,并设置 RMOA_API_KEY 以启用此功能。”然后跳到第 10 步。

如果已设置 RMOA_API_KEY,请上传规范并获取评分和完整的问题列表。

先将响应写入文件(不要将 curl 的输出通过管道传给 python3 -c)。RMOA 响应可能在 JSON 字符串中包含原始控制字符,Python 严格的 JSON 解析器从标准输入读取时会拒绝此类字符。先保存到文件,再使用 strict=False 解析,可避免此问题以及 shell 对字节的篡改。

curl -s -X POST \
  -H "Authorization: Bearer $RMOA_API_KEY" \
  -F "apiFile=@[/absolute/path/to/spec.json]" \
  -o /tmp/rmoa-response.json \
  -w "HTTP:%{http_code} SIZE:%{size_download}\n" \
  "https://api.ratemyopenapi.com/sync-report"

解析前,请检查 curl 调用是否返回 HTTP:200 且响应大小非零。如果不是,则问题出在 RMOA 本身——请重试或跳过 Lint 检查。

然后解析已保存的响应:

python3 -c "
import json
data = json.loads(open('/tmp/rmoa-response.json').read(), strict=False)
r = data['results']['simpleReport']
issues = data['results']['fullReport']['issues']
print(f'Score: {r[\"score\"]} | Docs: {r[\"docsScore\"]} | Completeness: {r[\"completenessScore\"]} | SDK: {r[\"sdkGenerationScore\"]} | Security: {r[\"securityScore\"]}')
print(f'Report: https://ratemyopenapi.com/report/{r.get(\"reportId\", \"\")}')
print(f'Total issues: {len(issues)}')
for i, issue in enumerate(issues, 1):
    path = ' > '.join(str(p) for p in issue.get('path', []))
    print(f'{i}. [{\"ERROR\" if issue[\"severity\"]==0 else \"WARN\"}] {issue[\"code\"]}')
    print(f'   {issue[\"message\"]}')
    print(f'   Path: {path}')
    print()
"

根据问题列表完成以下所有修复。

解读结果

对四个类别评分(每项 0-100):

  • 文档 — 描述、摘要、示例
  • 完整性 — 必填字段、响应代码、架构
  • SDK 生成 — operationId、无内联架构、正确的引用
  • 安全性 — 身份验证方案、401/403 响应

目标:所有类别均达到 100/100。

处理问题
  1. 自动修复明显属于格式或完整性的问题(缺少描述、缺少示例、缺少错误响应)。

  2. 就涉及设计决策的问题询问用户。常见示例:

    • 无身份验证/缺少 401:如果 API 有意不使用身份验证(例如训练练习),请告知用户 RMOA 已标记此问题,并询问他们是想忽略问题还是添加身份验证。
    • 缺少错误代码:如果 RMOA 要求 401/403,但相关故事中不包含身份验证,请询问用户。
    • 命名约定:如果 RMOA 标记的命名模式与标准文件冲突,以标准文件为准。请告知用户。
  3. 修复后重新运行。持续迭代,直到满足以下条件之一:

    • 评分达到 100/100,或
    • 剩余问题属于用户已批准忽略的有意设计选择
常见问题
  • 无身份验证的 API 在安全性方面的得分约为 65。 RMOA 会针对缺少 401/403 标记 8 个以上警告。如果 API 有意不使用身份验证,这些警告是预期结果——请与用户确认后继续。
  • nullable 关键字。 OpenAPI 3.1.0 已弃用 nullable。请改用 "type": ["string", "null"]。RMOA 会对此发出标记。
  • 缺少描述/示例。 这是最常见的扣分原因。每个操作、参数、架构属性和响应都需要同时提供描述和示例。也不要遗漏标头。
  • 孤立组件。 RMOA 会标记在 components 中定义但从未被 $ref 引用的架构。请将其删除或接入引用。
  • 内联架构。 直接在路径中定义的任何架构(而非通过 $ref 引用)都会拉低 SDK 生成评分。请将其提取到 components/schemas。

第 10 步:报告

向用户显示:

## OpenAPI Spec Complete

**File:** [path]
**Score:** [Overall] / 100
  - Docs: [score]
  - Completeness: [score]
  - SDK Generation: [score]
  - Security: [score]

**Report:** [RMOA report URL]

**Issues ignored (by design):**
- [list any intentional deviations the user approved]

然后询问:“想在 Swagger UI 中预览吗?”

需要 Node.js(用于运行 npx http-server)和互联网连接(Swagger UI 从 unpkg.com CDN 加载)。如果未安装 Node,请告知用户:“Swagger UI 预览需要 Node.js。你可以将规范粘贴到 https://editor.swagger.io,在那里预览。”然后跳过预览。

如果用户回答是且 Node 可用,请在与规范相同的目录中创建 swagger-preview.html 文件:

<!DOCTYPE html>
<html>
<head>
  <title>[API Name] — Swagger UI</title>
  <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head>
<body>
  <div id="swagger-ui"></div>
  <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    fetch('./[spec-filename].json')
      .then(r => r.json())
      .then(spec => {
        SwaggerUIBundle({
          spec: spec,
          dom_id: '#swagger-ui',
          deepLinking: true,
          presets: [SwaggerUIBundle.presets.apis],
          layout: "BaseLayout"
        });
      });
  </script>
</body>
</html>

然后在本地提供服务并在浏览器中打开:

cd [spec-directory] && npx -y http-server . -p 8090 -c-1 &
sleep 2 && open http://localhost:8090/swagger-preview.html

默认使用端口 8090。如果端口已被占用,请尝试 8091、8092 等。

用户完成审阅后,询问:“预览完了吗?我将清理服务和预览文件。”

如果用户回答是:

  1. 终止 http-server 进程:lsof -ti:8090 | xargs kill 2>/dev/null
  2. 删除预览 HTML 文件:从规范目录中移除 swagger-preview.html
  3. 确认:“服务器已停止,预览文件已移除。”

核心原则

  1. 故事是供人审阅的,不是工程规范。 保持易于浏览。产品经理应能查看方法、资源和参数,然后判断“对,这就是正确的 API”或“不对,我们缺少 X”。

  2. 严格遵循标准。 如果标准规定使用 camelCase,每个参数都必须使用 camelCase。如果标准规定资源名称使用复数,每个资源都必须使用复数。故事是标准落实的第一道关卡。

  3. 准确使用领域语言。 如果领域中使用 exchangeMic,参数就应为 exchangeMic,而不是 exchange_mic 或 exchangeId。领域定义是命名的唯一依据。

  4. 根据领域关系嵌套资源。 如果 Quote 属于 Instrument,资源应为 /instruments/{id}/quotes,而不是 /quotes?instrumentId=X。遵循标准中的嵌套深度限制。

  5. 不要臆造需求。 如果需求中未提到删除或更新操作,就不要添加。如果你认为有所遗漏,应将其标记为问题,而不是擅自添加。

  6. 分页遵循标准。 如果标准指定基于游标的分页,则每个集合端点都应包含 cursor 和 limit 参数。不要混用分页方式。

  7. 领域定义是架构的唯一依据。 属性名称、类型、枚举、关系等都必须与领域文件中的内容完全一致。

  8. 最佳实践决定 OpenAPI 的质量。 DRY、描述、示例、组件提取等方面,应以最佳实践文件为准,而不是为了图省事走捷径。

  9. Lint 是验证手段,不是绝对准则。 RMOA 能发现实际问题,但也会标记有意为之的设计选择。由用户决定修复哪些问题、忽略哪些问题。

  10. 仅输出 JSON。 OpenAPI 使用 JSON,而非 YAML。这样既符合 RMOA 预期的输入格式,也可避免 LLM 输出的 YAML 格式问题。

评论

支持 Markdown