Skip to main content
Glama
README.md
# api-mcp

一个本地 stdio MCP Server:使用项目 token 或账号 Cookie 只读查询 YApi 项目、分类和接口文档,并通过“先预览、后应用”按分类生成确定性的 TypeScript/JavaScript 请求函数与类型。

运行时不会修改 YApi;它只会在明确执行 `api_apply_generation` 后写入本地受管文件。每个业务项目在自身根目录保存 YApi 配置和凭据,因此只需在 Codex 全局注册一次 MCP 服务。

## 环境要求

- Node.js 20 或更高版本
- 可访问目标 YApi 实例
- YApi 项目 token,或账号登录态中的 `_yapi_token` 与 `_yapi_uid` Cookie

## 本地使用(Codex)

```bash
cd E:\GitHub\api-mcp
pnpm install
pnpm build
```

在 `~/.codex/config.toml` 中只添加一次下面的全局配置。不要设置 `cwd`、`--config`、环境变量 token 或 `env_vars`:

```toml
[mcp_servers.api-mcp]
command = "node"
args = ['E:\GitHub\api-mcp\dist\cli.js', '--workspace-mode']
startup_timeout_sec = 20
tool_timeout_sec = 120
default_tools_approval_mode = "writes"
```

然后在每个业务项目根目录创建 `api-mcp.config.mjs` 或 `api-mcp.config.json`。从示例复制:

```bash
copy E:\GitHub\api-mcp\api-mcp.config.example.mjs api-mcp.config.mjs
```

每次源码变更后都执行 `pnpm build`,重启 Codex,并用 `/mcp` 确认服务已连接。

使用前检查:


- 只在 `~/.codex/config.toml` 配置一条带 `--workspace-mode`、不含 `cwd` 的 stdio 服务。
- 每个业务项目根目录只放一个 `api-mcp.config.mjs` 或 `.json`。
- 认证可使用 `projects[].token`,或服务器级 Cookie;不支持 `tokenEnv` 或 `env_vars`。
- 实际生成目录固定为 `<output.dir>/<project.id>`。
- 把真实的 `api-mcp.config.*` 加入业务仓库的 `.gitignore`。
- 先执行 preview,再执行 apply;计划有效期为十分钟。

服务只在 stdout 写 MCP 协议消息,诊断信息写 stderr,并对 token、Authorization、Cookie 等敏感值脱敏。

### 固定配置模式(可选)

如果只想将服务绑定到一个固定项目,可直接在终端运行:

```bash
node E:\GitHub\api-mcp\dist\cli.js --config /absolute/path/api-mcp.config.json
```


## 配置

在 `--workspace-mode` 下,服务仅在工具调用给出的业务项目根目录查找以下文件之一:`api-mcp.config.js`、`.mjs`、`.cjs` 或 `.json`。不能缺失,也不能同时存在多个。`.mjs` 和 ESM `.js` 使用 `export default`;`.cjs` 使用 `module.exports`;JSON 不执行代码。

`--workspace-mode` 要求 `workspaceRoot` 保持默认 `"."`(或解析后与调用的项目根目录相同),避免配置将生成目标带出当前项目。固定 `--config` 模式仍可按配置文件解析该字段。所有 `output.dir` 都相对有效 workspace,不能是绝对路径、包含 `..`,或通过符号链接越界。不同项目不能共用输出目录。项目 ID 必须是安全路径段,生成目录自动追加项目 ID。

```ts
type ProjectTokenAuth = {
  projects: Array<{
    id: string
    token: string
    categories?: { include?: 'all' | number[]; exclude?: number[] }
  }>
  auth?: never
  discovery?: never
}

type AccountCookieAuth = {
  projects?: never
  auth: {
    type: 'cookie'
    cookie: string // 必须包含 _yapi_token 与 _yapi_uid
  }
  discovery?: {
    include?: 'all' | number[] // YApi 数字项目 ID,默认 "all"
    exclude?: number[] // 默认 []
  }
}

interface YapiMcpConfig {
  workspaceRoot?: string // 默认 "."
  cacheTtlMs?: number // 默认 60_000
  requestTimeoutMs?: number // 默认 10_000
  servers: Array<({
    id: string
    url: string
    output: {
      dir: string
      language?: 'ts' | 'js' // 默认 "ts"
      aiManifest?: string | false // 默认 "api-manifest.json"
      requestAdapter: { module: string; export?: 'default' | string }
      dataKey?: string | string[]
      queryArrayFormat?: 'brackets' | 'indices' | 'repeat' | 'comma' | 'json'
      barrel?: string | false // TS 默认 "index.ts",JS 默认 "index.js"
    }
  } & (ProjectTokenAuth | AccountCookieAuth))>
}
```

推荐在业务项目根目录新建 `api-mcp.config.mjs`。下面是 TypeScript 输出的完整配置;将 `token` 替换为对应 YApi 项目的项目 token,并将 adapter 模块路径改为业务项目实际路径:

```js
export default {
  workspaceRoot: '.',
  servers: [
    {
      id: 'company-yapi',
      url: 'https://yapi.example.com',
      output: {
        dir: 'src/generated',
        language: 'ts',
        aiManifest: 'api-manifest.json',
        requestAdapter: {
          module: '@/utils/request',
          export: 'default',
        },
        dataKey: ['data'],
        queryArrayFormat: 'brackets',
        barrel: 'index.ts',
      },
      projects: [
        {
          id: 'web-api',
          token: '你的 YApi 项目 token',
          categories: { include: 'all', exclude: [] },
        },
      ],
    },
  ],
}
```

同一个 YApi 服务下的多个项目共用 `output` 与 adapter;每个项目只配置自己的 `id`、`token`、分类筛选。不同 server 可各自配置一套 `output`。`id` 在整个配置文件内必须唯一。

### Cookie 账号发现模式

如果不想逐个维护项目 token,可改为服务器级 Cookie:

```js
export default {
  workspaceRoot: '.',
  servers: [
    {
      id: 'company-yapi',
      url: 'https://yapi.example.com',
      auth: {
        type: 'cookie',
        cookie: '_yapi_token=替换为登录Cookie; _yapi_uid=替换为用户ID',
      },
      discovery: {
        include: 'all',
        exclude: [100, 200],
      },
      output: {
        dir: 'src/generated',
        language: 'ts',
        aiManifest: 'api-manifest.json',
        requestAdapter: { module: '@/utils/request', export: 'default' },
        queryArrayFormat: 'brackets',
        barrel: 'index.ts',
      },
    },
  ],
}
```

Cookie 模式会读取账号可见分组及其项目,并按 `discovery` 过滤。发现出的 MCP 项目标识就是 YApi 数字项目 ID,例如 `291`;输出目录为 `src/generated/project-291`。Cookie 与 `projects` 互斥,不能在同一个 server 中同时配置。

JavaScript 项目只需切换输出语言和 barrel 扩展名:

```js
output: {
  dir: 'src/generated',
  language: 'js',
  aiManifest: 'api-manifest.json',
  requestAdapter: { module: '@/utils/request', export: 'default' },
  queryArrayFormat: 'brackets',
  barrel: 'index.js',
}
```

可以省略 `language`、`aiManifest`、`queryArrayFormat` 和 `barrel`,默认值分别为 `'ts'`、`'api-manifest.json'`、`'brackets'`、TS 的 `'index.ts'` 或 JS 的 `'index.js'`。`barrel: false` 会禁用入口文件,`aiManifest: false` 会禁用清单。barrel 必须使用与语言相同的扩展名,manifest 必须是相对 `.json` 路径。

例如,`output.dir: 'src/generated'` 且项目 `id: 'web-api'` 时,生成位置为 `src/generated/web-api`。一个 server 的 `output` 由其全部 projects 共用,而每个 project 只保留自己的 token 和分类筛选。

token 和 Cookie 都只适合受信任的本地业务仓库;Cookie 代表整个账号权限,风险高于单项目 token。不要将真实配置提交到版本控制。应在 `.gitignore` 中忽略四种 `api-mcp.config.*` 文件名。运行时会对完整 Cookie、token、Authorization 和敏感响应字段脱敏。

`dataKey` 同时影响生成的响应类型和运行时 descriptor。例如 `dataKey: ["data", "items"]` 会让响应类型指向 schema 的 `data.items`,并由 request adapter 在实际响应中解包相同路径。路径无法从 schema 解析时,响应类型回退到 `unknown`,preview 返回警告。

## MCP 工具

- `api_list_projects({ workspaceRoot, checkConnection? })`:列出一个业务项目根目录中的安全项目元数据;可选择检查连接。
- `api_list_categories({ workspaceRoot, project, refresh? })`:列出配置过滤后的分类。
- `api_search_interfaces({ workspaceRoot, project, query?, categoryIds?, methods?, statuses?, limit?, cursor?, refresh? })`:搜索标题、路径、标签、方法和状态;`limit` 为 1–100。
- `api_resolve_url({ workspaceRoot, url, refresh? })`:直接解析已配置项目中的 YApi 分类或接口链接,返回内容及可传给 preview 的 scope。
- `api_get_interface({ workspaceRoot, project, interfaceId, refresh? })`:获取归一化接口详情。
- `api_preview_generation({ workspaceRoot, project, scope, includeDiff? })`:计算目标文件、hash、冲突、警告、过期时间及可选 diff,不写文件。
- `api_apply_generation({ planId })`:重新校验配置、YApi 源数据和目标 hash 后应用计划。

`workspaceRoot` 必须是 Codex 当前任务工作区的绝对路径;apply 根据 preview 创建的计划定位工作区,因此不能也不需要传递 `workspaceRoot`。

### 提问式使用

可以直接对 AI 说:

> 帮我看看 `https://yapi.example.com/project/291/interface/api/cat_4047` 的接口内容。

AI 会调用 `api_resolve_url`。分类 URL(`.../api/cat_<分类ID>`)会返回分类信息、接口摘要及 `{ "kind": "categories", "ids": [分类ID] }`;单接口 URL(`.../api/<接口ID>`)会返回完整请求/响应 schema 及对应的 interfaces scope。随后 AI 应先解释接口内容;只有在你明确确认后,才调用 `api_preview_generation`,并在你确认 preview 后调用 `api_apply_generation`。

URL 的服务地址和 YApi 数字项目 ID 必须匹配某个已配置 token,或属于 Cookie 账号可见且未被 `discovery` 排除的项目。项目不匹配、权限不足、被筛选排除或 URL 格式无效时会返回稳定错误。

`scope` 必须是以下三种形式之一:

```json
{ "kind": "interfaces", "ids": [101, 102] }
{ "kind": "categories", "ids": [10, 11] }
{ "kind": "project" }
```

选择单个接口仍会重新读取并生成它所属分类的完整 `category-<id>.ts`,不会意外覆盖同分类的其他接口。

## 生成与安全写入

默认 TypeScript 模式每个项目会生成:

- `request-types.ts`:受管的 descriptor、adapter 和 `YapiFileData<T>` 类型。
- `category-<id>.ts`:该分类的完整接口类型和请求函数。
- `index.ts`(可配置或禁用):只导出本次生成范围包含的受管文件。
- `api-manifest.json`:供 AI 和业务代码检索的稳定操作清单,可改名或设为 `false` 禁用。

设置 `output.language: 'js'` 时生成 ESM:`request-types.d.ts`、`category-<id>.js`、配套的 `category-<id>.d.ts`、`index.js` 与 `index.d.ts`。JS 不依赖 TypeScript 运行时;类型信息只存在于 `.d.ts`。barrel 的扩展名必须与语言一致。

业务开发时优先读取生成目录的 `api-manifest.json`:它列出每个接口的函数名、导入入口、请求/响应类型、参数部分和 YApi 链接。应导入其中的函数复用,而不是重新手写 HTTP 请求。

文件不包含时间戳;分类、接口与声明顺序稳定。函数名固定以 `api` 开头,并由路径组成:`GET /api/users/{id}` 生成 `apiUsersById`;HTTP method 不参与命名,路径仍冲突时追加 interface ID。

每个生成函数均为箭头函数,接收 descriptor 形式的 `requestData` 及可选 `...args`。基础 descriptor 会保留全部固定字段,随后以 `Object.assign(descriptor, requestData)` 合并调用数据,再将额外参数原样传给 adapter:

```ts
export const apiUsersById = (requestData, ...args) =>
  requestAdapter(Object.assign(descriptor, requestData), ...args)
```

TypeScript 会生成对应的 `ApiUsersByIdRequestData` 类型:它保留 `YapiRequestDescriptor` 的字段,并将 `pathParams`、`query`、`headers`、`body` 收窄到接口文档定义的类型。

preview 生成随机 `planId`,默认 10 分钟有效。apply 不接受未经 preview 的写入,并会拒绝:

- 没有 managed marker 或 marker 不属于同项目/来源的目标文件;
- preview 后变化的 YApi 数据、配置或目标文件;
- workspace 外路径及越界符号链接。

不再出现在当前生成范围的旧分类文件只会列入 `staleFiles`,不会自动删除;barrel 也不再导出它们。TS/JS 切换产生的旧受管文件同样只报告为 stale,绝不自动删除。

常见稳定错误码包括 `CONFIG_INVALID`、`CONFIG_NOT_FOUND`、`CONFIG_AMBIGUOUS`、`WORKSPACE_INVALID`、`YAPI_HTTP_ERROR`、`YAPI_API_ERROR`、`YAPI_TIMEOUT`、`PLAN_EXPIRED`、`PLAN_STALE`、`PATH_OUTSIDE_WORKSPACE`、`UNMANAGED_FILE_CONFLICT` 和 `WRITE_PARTIAL`。

## Request adapter

业务侧 adapter 接收以下 descriptor,并返回解包后的响应:

```ts
interface YapiRequestDescriptor<TBody = unknown> {
  method: string
  path: string
  pathParams?: Record<string, unknown>
  query?: Record<string, unknown>
  headers?: Record<string, unknown>
  body?: TBody
  requestBodyType: string
  responseBodyType: string
  dataKey?: string[]
  queryArrayFormat: string
  metadata: {
    projectId: number
    categoryId: number
    interfaceId: number
    title: string
    yapiUrl: string
  }
  extraInfo: {
    name: string // 接口名称
    creator: string // 创建人
    updatedAt: string // 中国标准时间,格式 YYYY-MM-DD HH:mm:ss
    remark: string // 接口备注
  }
}

type YapiRequestAdapter = <TResponse>(
  descriptor: YapiRequestDescriptor,
  ...args: unknown[]
) => Promise<TResponse>
```

[`examples/request-adapter.ts`](./examples/request-adapter.ts) 提供了基于 `fetch` 的参考实现,包括 path 参数、五种 query 数组格式、JSON、表单、上传以及 `dataKey` 解包。生成器只导入配置指定的 adapter,不执行任何配置回调或外部命令。

## 开发与验证

```bash
pnpm install
pnpm lint
pnpm typecheck
pnpm run typecheck:examples
pnpm test
pnpm build
pnpm run pack:dry-run
```

测试覆盖双认证配置、敏感值脱敏、账号项目发现、YApi 错误/超时/分页/缓存、schema 推断、生成代码编译、preview/apply 冲突与 stale 文件策略,以及真实子进程 stdio 上的全部 MCP 工具。

使用 MCP Inspector 做交互式冒烟测试:

```bash
npx @modelcontextprotocol/inspector node ./dist/cli.js --workspace-mode
```

## 设计参考

- [yapi-to-typescript handbook](https://fjc0k.github.io/yapi-to-typescript/handbook/):多项目配置、类型生成和统一请求适配器设计。
- [YApi OpenAPI](https://github.com/YMFE/yapi/blob/master/docs/openapi-doc.md):只读项目与接口文档端点。
- [MCP TypeScript SDK server guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/server.md):stdio Server 与 tools 接口。

## License

MIT