# AIFA 杂志系统 · MCP 说明全文

说明全文：连接与身份、通用规矩、方法与域、常见流程、报错怎么读、四个保留范围项、要人确认的事、做完怎么汇报。只要连上就能开工的那一份在速览页。

本页地址：https://afa5.chaos.callai.online/mcp-guide/details ｜ 纯文本地址：https://afa5.chaos.callai.online/mcp-guide/details.md ｜ MCP 地址：https://afa5.chaos.callai.online/mcp

## 一、你是谁

本页是说明全文。只要连上就能开工的那一份在[速览与方法索引](https://afa5.chaos.callai.online/mcp-guide)，先读那一页；这里给的是它按需跳转过来的细节。

AIFA 是一本中英双语杂志，也是它自己的内容系统：阅读器、排版编辑台、管理后台、发布链路。本系统通过 MCP（Model Context Protocol）把这些能力开放给外部的通用 Agent。你就是这样一个 Agent：用户把本页地址交给你，你读完本页，替用户在 AIFA 里编辑和运营。

你持有的访问令牌由某个 AIFA 账号本人在浏览器里签出。本页把这个账号叫「绑定账号」。你的能力等于绑定账号的权限：绑定账号做得到的事，你都做得到；绑定账号做不到的事，你也做不到。另有四件要授权里勾选过对应的保留范围项，见「八、四个保留范围项」。

你写下的每一处改动都记在绑定账号名下。你发布的单篇，作者是绑定账号；你回复的工单，回复人是绑定账号。

按下面的顺序做：

1. 读「二、能做什么，不能做什么」，确定用户的任务归哪个域、需要哪一档授权。
2. 还没有连接时，按「三、连接与身份」完成连接。授权的最后一步要用户本人在浏览器里点同意，你不能替他点。
3. 连接成功后第一步调 `permissions.retrieve`，确认绑定账号和能力清单。
4. 读「四、通用规矩」。这一节的规则适用于所有任务。
5. 任务要写稿或排版时，先取技能包，见下面「技能包：写稿排版的手册」。技能包比本页细，讲的是怎么写、怎么排、画面怎么做。
6. 用 `aifa_find` 找到任务要调的方法，在「六、常见流程」里找到最接近的一条方法链；写稿排版按技能包的步骤做，最后几步都是自查。
7. 工具报错时查「七、报错怎么读」。遇到「八、四个保留范围项」里缺范围项的情况，停下来告诉用户。
8. 做完后按「十、做完之后怎么汇报」交付。

读取方式：本页的纯文本（Markdown）版本在 [https://afa5.chaos.callai.online/mcp-guide/details.md](https://afa5.chaos.callai.online/mcp-guide/details.md)，内容与 HTML 版完全相同，Agent 优先读它。速览那一页同样有纯文本形态 [https://afa5.chaos.callai.online/mcp-guide.md](https://afa5.chaos.callai.online/mcp-guide.md)。四个地址都不需要登录。连接 MCP 和调用工具才需要授权。

方法索引在[速览页](https://afa5.chaos.callai.online/mcp-guide#tools)，由服务端从资源 API 规格实时生成，带每个方法的档位。正文与它不一致时，以索引和工具自己的说明为准。

### 技能包：写稿排版的手册

本页讲的是系统怎么连、有哪些工具、按什么顺序做事。怎么写一篇 AIFA 的单篇、版面怎么排、可交互的画面怎么做，写在技能包里。技能包是若干份 Markdown 手册加参考范例源码，和本页一样不需要登录。

要让另一个全新的 Agent 接入，不必把本页整页交给它：入口技能文件 [https://afa5.chaos.callai.online/mcp-guide/skills/SKILL.md](https://afa5.chaos.callai.online/mcp-guide/skills/SKILL.md) 只有一页，讲清连接、授权与该取哪个技能包，再指回本页。

两种取法，内容完全相同，取一种即可：

- 站点地址：清单在 [https://afa5.chaos.callai.online/mcp-guide/skills](https://afa5.chaos.callai.online/mcp-guide/skills)（JSON，列出每个包、每个文件与它的取用地址）；单个文件按清单里的 `files[].url` 取，形如 `https://afa5.chaos.callai.online/mcp-guide/skills/aifa-interactive-canvas/SKILL.md`；整包一次拿走用 [https://afa5.chaos.callai.online/mcp-guide/skills.zip](https://afa5.chaos.callai.online/mcp-guide/skills.zip)。清单里的 `version` 是内容指纹，内容没变它就不变。
- MCP 工具：`aifa_skill` 取清单，`aifa_skill` 按 `path` 取正文。两个工具都不需要权限，作者档与全部档都调得动。

哪一本管什么：

| 技能包 | 管什么 | 什么时候读 |
| --- | --- | --- |
| `aifa-author` | 作者线必读（文件模型、物理规则、七步流程、索引）；做画面与配图前必读媒介边界；参考区：物理契约、排版规则、编辑札记、双端编排、字体文件。 | 写任何一篇之前先读 `aifa-author/SKILL.md`，用 aifa_skill 取；它不随建稿返回自动送达。 |
| `aifa-article-multimedia` | 整篇创作的地图：从拆解原稿、调研、论点卡、一块一个制作者，到外部盲审、实拍交互、手机真实滚动与发布，每站手上有什么、下一站去哪。 | 写新的一篇时按需读。 |
| `aifa-magazine-layout` | 整期的编排：单篇顺序、合集封面封底、块序列排版总纲、检查。 | 把好几篇组成一期，从它起手。 |
| `aifa-interactive-canvas` | 自定义画面块（`canvas`）的地图：从论点卡到一块画面的各站、指向原稿的动作问法、沙箱与版面的地形、讲形式为何适合论点的范例。 | 要做会动的或可交互的画面时读。 |
| `aifa-block-<变体>` | 每个块一本（如 `aifa-block-pull-quote`）：这一块写什么、放在哪、行数怎么定；字段形状以 /mcp-guide/schema 的 block/<变体> 为准。 | 写某一种块拿不准时读那一本。 |
| `aifa-magazine-editing` | 改一个已有的单篇：润色、补译、换图、定点修订。 | 用户给的是已有单篇而不是新稿时读。 |
| `aifa-publish-review` | 发布前按物理契约逐条审核，只读不改，结论是通过或退回。 | 要为一期出审核意见时读。 |

分工：技能包讲怎么写、怎么排；本页讲怎么连、按什么顺序调工具；字段名、取值与上限由工具自己的说明、文件提交的拒收回执与内容声明的 JSON Schema 当场给出。技能包的主线只用 MCP 工具就能走完；标着「仅限有代码仓的内部流程」的附录，没有代码仓就跳过。

## 二、能做什么，不能做什么

### 总原则

MCP 的能力覆盖管理后台的全部能力，也覆盖编辑台的全部能力。界面上能做的事，对应的工具也能做；工具不比界面多放权限。

授权分三档，详见「三、连接与身份」：

- 读者档（`scope=reader`）：只替读者办事——找已发布的单篇与合集并读正文（付费内容按账号权益裁剪，与网页同一判定）、查作者与作品、看首页推荐，以及资料、收藏、关注的作者、阅读进度、会员与订单、留言、自己的 AI 能力开关与账单。不能写稿，也碰不到后台。
- 作者档（`scope=author`）：创作这一面。任何登录账号都有作者身份。
- 全部档（`scope=full`）：绑定账号在 AIFA 的全部能力，包括管理后台。前提是绑定账号本身有后台角色。全部档令牌的能力仍然等于绑定账号自己的角色：客服账号拿到全部档，也只能做客服的事。

### 目前没有工具的事

下面这些事现在真的没有工具。用户要求时，直接告诉用户需要他在网站上操作，不要用别的工具拼凑。本清单 2026-09-19 逐条对照工具注册表核过。

- 新建账号。账号由本人在网站注册产生，没有建账号的工具。
- 账号清单导出、数据看板的 CSV 导出。`ai_accounts.retrieve`（facet=facts） 只给 JSON 行。
- 把文字转成语音。系统不做配音、不克隆声音、不写口播稿（2026-09-21 撤销）：单篇的伴读音频只能上传现成的音频文件（`narrations.update` 或 `media.create`（`target="asset"`）传 `audio/*` 素材，再用 `aifa_push` 按语言写进 `article.json` 的 `audioAssetId`（`{zh?, en?}`）挂上去）。音频要用户自己提供，或你在站外自己做好再传进来。

## 三、连接与身份

### 地址

| 项 | 值 |
| --- | --- |
| MCP 地址（唯一入口，读者、作者与后台都连它） | `https://afa5.chaos.callai.online/mcp` |
| 传输方式 | Streamable HTTP。同一地址接 MCP 2026-07-28（无状态）与 2025-11-25（会话）两版协议，按请求自动判版，支持流式响应。不要用 stdio 方式连接这个地址。 |
| 受保护资源元数据 | `https://afa5.chaos.callai.online/.well-known/oauth-protected-resource/mcp` |
| 授权服务器元数据 | `https://afa5.chaos.callai.online/.well-known/oauth-authorization-server` |
| 授权端点 | `https://afa5.chaos.callai.online/api/oauth/authorize` |
| 令牌端点 | `https://afa5.chaos.callai.online/api/oauth/token` |
| 动态客户端注册 | `https://afa5.chaos.callai.online/api/oauth/register` |

### 情况一：你的客户端支持远程 MCP 与 OAuth（多数情况）

把 MCP 端点加进客户端配置，不填任何密钥。客户端一连上就收到 401，自动完成元数据发现、动态注册和 PKCE，给出授权链接：Claude Code 跑 `claude mcp login <服务名>` 打印链接（交互终端里直接打开浏览器），或在会话里进 `/mcp` 选这个服务点 Authenticate；会话中途加的服务要重开会话才出现工具。VS Code、Cursor、网页版 Claude、ChatGPT 在 MCP 或「连接器」设置里添加后点连接即弹。用户在浏览器里登录 AIFA、选档位、点同意。令牌由客户端保管和刷新。

通用 JSON 配置（键名可能是 `mcpServers` 或 `servers`，以你的客户端文档为准）：

```json
{
  "mcpServers": {
    "aifa-magazine": {
      "type": "http",
      "url": "https://afa5.chaos.callai.online/mcp"
    }
  }
}
```

命令行客户端的等价命令（以 Claude Code 为例）：

```bash
claude mcp add --transport http aifa-magazine https://afa5.chaos.callai.online/mcp && claude mcp login aifa-magazine
```

你自己无权修改客户端配置时，把 MCP 端点地址交给用户，请他在客户端的 MCP 或「连接器」设置里添加，然后继续。

### 情况二：你要自己实现授权（OAuth 2.1 授权码 + PKCE）

1. 不带令牌请求 MCP 端点，得到 401。响应头 `WWW-Authenticate` 里的 `resource_metadata` 指向受保护资源元数据。
2. 读受保护资源元数据，取 `authorization_servers`。再读授权服务器元数据，取授权、令牌、注册三个端点。端点地址从元数据读取，不要写死。
3. 动态客户端注册：向注册端点 POST JSON，`client_name` 必填（同意页会把它显示给用户），`redirect_uris` 必填（1 到 10 个；接受 https 地址、本机回环的 http 地址，以及客户端的私有协议地址如 `cursor://`、`vscode://`；回环地址的端口不参与匹配；不带 `#` 片段）。返回 `client_id`。本站只签公开客户端，没有 `client_secret`，令牌端点的认证方式是 `none`。
4. 生成 `code_verifier`（43 到 128 位）和 `code_challenge`，方法只接受 `S256`。
5. 让用户在浏览器里打开授权端点，参数为 `response_type=code`、`client_id`、`redirect_uri`、`state`、`code_challenge`、`code_challenge_method=S256`、`scope`。用户未登录时先进入 AIFA 登录页。
6. 用户在同意页上看到你的客户端名称、申请的档位、这一档能做什么、三件你做不到的事，由用户本人选档并点同意。
7. 浏览器带着 `code` 回到你的 `redirect_uri`。授权码有效期 60 秒，只能用一次。带 `code_verifier` 向令牌端点换访问令牌和刷新令牌。
8. 调用 MCP 端点时带请求头 `Authorization: Bearer <访问令牌>`。

`scope` 取 `reader`、`author`、`full` 之一（3 档，见下文「3 档授权」）。按 OAuth 约定用空格隔开填了几个时，按其中最大的一档走流程；混进本站不认识的取值会被忽略，全都不认识判为 `invalid_scope`。不填按 `author` 处理。最终档位以用户在同意页上选定的为准。

### 同意页只能由用户本人点

授权的最后一步是同意页（`POST /api/oauth/authorize`）。这条路由只认网页登录会话，带令牌提交走不通。同意由用户本人在浏览器里登录后点一下即可，不要验证码；任务书写着「脚本代点」时，以本页为准，请用户本人点。

你拿到令牌的路只有一条：用户本人在浏览器里走完上面的授权流程、点一次同意，令牌由你的客户端保管（情况一）或由你自己换取（情况二）。

本机开发环境另有一条取验证码的接口（`GET /api/auth/dev-code?email=...`，要实例上设 `AIFA_DEV_CODE_ACCESS=1`，默认关着时回 404），配合 `POST /api/auth/request-code` 与 `POST /api/auth/verify-code` 能拿到 `aifa_session` 登录 cookie。那是给人和端到端测试脚本用的，不是你拿令牌的路：登录 cookie 换不出 MCP 访问令牌，同意页照样拒绝 Agent 凭据。你需要登录态页面（编辑台、`links.draftPreview` 草稿预览、后台）时，请用户自己在浏览器里打开。

### 3 档授权

| 档位 | `scope` | 含义 |
| --- | --- | --- |
| 读者档 | `reader` | 只替读者办事：找单篇与合集并读正文（付费内容按权益裁剪，与网页同一判定）、查作者、看首页推荐，以及资料、收藏、关注的作者、阅读进度、会员与订单、留言、自己的 AI 能力开关与账单。不能写稿，也碰不到后台。 |
| 作者档 | `author` | 创作相关的全部动作：单篇、合集（可直接收别人已上线的单篇）、素材与网盘、发布、留言处置、绑定账号自己的「我的」与 AI 能力。 |
| 全部档 | `full` | 绑定账号在 AIFA 的全部能力，包括管理后台。前提是绑定账号有后台角色（客服、运营、管理员等）。 |

- 同意页恒有读者档与作者档；只在绑定账号确有后台角色时才显示全部档选项，没有后台角色时最多得到作者档。
- 写稿要作者档，后台要全部档（全部档也包含作者档的全部动作）。
- 最终档位由用户在同意页上选定，可能与你申请的不同。连接后用 `permissions.retrieve` 核对。
- 任务只涉及创作时申请作者档。权限小，出错的影响也小。
- 要换档位时，请用户在网站「我的 → Agent」里吊销当前授权，再重新授权。你自己不能吊销或新增授权。

### 令牌与会话的期限

- 访问令牌有效期 8 小时。刷新令牌有效期 15 天。
- 刷新令牌每用一次就换新的一对令牌，旧的刷新令牌立即作废。再次使用旧的刷新令牌，或并发刷新两次，服务端按重放攻击处理，会吊销整条授权。吊销后只能请用户重新授权。
- 刷新时可以把全部档收窄为作者档，不能把作者档扩为全部档。
- 管理员对绑定账号执行「吊销全部访问」（`accounts.revoke_sessions` 或后台同一个按钮）时，绑定账号的浏览器会话、名下全部 Agent 授权、这些授权签出的全部令牌一起作废。你的令牌此后一律 401，刷新也失败，只能请用户重新登录并重新授权。
- 旧版（2025-11-25）会话号自签发起 24 小时有效，过期后带旧的 `mcp-session-id` 调用会得到 404，重新 `initialize` 即可；会话不存业务状态，换会话不丢任何东西。新版（2026-07-28）请求没有会话。
- 会话被回收或重建后，会话内的状态全部丢失；业务状态不存在会话里，重新 `initialize` 即可接着做。

### 连接后的自查

连接成功后第一步调 `permissions.retrieve`。它返回的字段分两类，不要混用。

按本次令牌收窄过的（回答「我现在能做什么」）：

- `principal`：绑定账号。核对邮箱是用户本人要你代表的账号。
- `capabilities`：能力清单，已按档位收窄。只有 `content.*` 几项，说明这是作者档，或绑定账号没有后台角色。出现 `site.*`、`crm.*`、`accounts.manage` 等，说明这是全部档且绑定账号有对应角色。
- `entries`：够得着的后台入口，已按档位收窄。

只作背景、不能拿来判断能不能做某件事的：

- `grants`：绑定账号被授予的角色与数据域。账号只分作者（每个账号自带）、运营、管理员三种角色。只列本令牌整套用得上的角色：全部档原样，作者档只剩作者，读者档为空。
- `roles`：`grants` 里出现的那几个角色各含哪些能力。

判断能不能做一件事，只看 `capabilities`。

发布自己的单篇要 `content.write`；发布别人的单篇要数据范围为全部的 `content.write`，且令牌是全部档。

`tools/list` 返回的工具清单按令牌的能力收窄：够不着的工具不列出。所以你在 `tools/list` 里看到的工具数，通常少于工具目录写的全集数。没列出的工具直接调用，服务端仍然返回 403。取不到能力时服务端退回全量列出，因此「列出」不等于「有权调用」；能否调用以 `capabilities` 和工具目录的「档位」列为准，不要靠逐个试错。

### 连接失败时的排查

| 现象 | 原因 | 处置 |
| --- | --- | --- |
| 无法确定服务是否在线 | — | 调 `system.retrieve`：它不看角色，作者档与全部档的令牌都调得动（不带令牌回 401）。连这个也不通，说明端点地址或网络有问题。 |
| 401，`WWW-Authenticate` 带 `invalid_token` | 访问令牌过期或已被吊销 | 用刷新令牌换新令牌。刷新也失败时，请用户重新授权。 |
| 刷新后仍然 401，文案写明「账号已停用」 | 绑定账号被停用 | 请用户联系管理员恢复账号，再重新连接。 |
| 401，文案写明「该授权已被吊销」 | 用户自己吊销了这条授权，或管理员吊销了这个账号的全部访问 | 不要刷新令牌，刷新不会成功。请用户本人重新走一遍授权。 |
| 刷新后仍然 401，文案是「访问令牌无效或已过期」 | 令牌不存在或已过期（被吊销的情况现在另有文案，见上一行） | 用刷新令牌换新令牌；刷新也失败就请用户重新授权。 |
| 401，文案写明「个人访问令牌已到期」或「个人访问令牌已被吊销」 | 用户在账号页签发的固定令牌（`aifa_pat_` 开头）过了有效期，或被用户吊销 | 个人访问令牌不能刷新。请用户在「我的 → Agent」重新签发一把，由他本人填进连接器设置或环境变量 `AIFA_TOKEN`，不要让他贴进对话。 |
| 授权回跳带 `error=invalid_scope` | `scope` 的取值本站一个都不认识 | 填 `reader`、`author`、`full` 之一（填了几个时按其中最大的一档走）。 |
| 注册返回 `invalid_redirect_uri` | 回调地址是非回环的 http 地址、`javascript:` 这类危险协议，或带了 `#` 片段、用户信息、空白字符 | 改用 https 地址、本机回环的 http 地址，或客户端自己的私有协议地址。 |
| 换令牌返回 `invalid_grant` | 授权码超过 60 秒、已用过，或 `code_verifier` 不匹配 | 从授权端点重新走一遍，拿到授权码后立即换令牌。 |
| 同意页没有全部档选项 | 绑定账号没有后台角色 | 请用户换一个有相应角色的账号，或请管理员用 `permissions.update` 先授予角色。 |
| 用户在浏览器里授权后登录态丢失 | `localhost` 和 `127.0.0.1` 是两个主机名，Cookie 不互通 | 全程使用同一个主机名。以本页地址栏里的主机名为准。 |
| 404「会话不存在或不属于当前身份」 | 会话空闲超时，或令牌刷新后沿用了旧会话号 | 重新 `initialize`。 |

## 四、通用规矩

### 先读后写，带版本号

- 每个工具都是对本站 HTTP 接口的薄封装。鉴权、校验、事务、版本检查都在服务端执行，MCP 这一层不做判定，也无法绕过。
- 写入前先读，拿到当前版本号，写入时作为 `expectedVersion` 传回。单篇与页的版本号都从 `aifa_pull` 读：索引 `article.json` 的 `version`、各页在 `readonly.pageSummaries[].version`，单篇版本号在 `readonly.workflow.articleVersion`。入参里没有 `expectedVersion` 的写工具不传它，传了会被严格入参拒掉；这类写入后写覆盖先写，合集的各个写工具就是这样：写前先读，写后回读核对。
- 整篇按文件读写（方案 §5.2）：`aifa_pull` 缺省只回索引 `article.json`（单篇字段、页序 `pages`、`readonly.pageSummaries` 里各页的 `pageId` 与版本号），`paths` 取页文件 `pages/<pageId>.<zh|en>.md`、画面文件 `canvas/<pageId>/<blockId>.html`、来源清单 `sources.json` 与朗读时间轴 `audio-timeline.json`（后两个从索引拆出，按需读）；`aifa_push` 一批文件一个事务、全成全败。写入带的 `expectedVersion` 跟页走、不看文件新旧：索引 `article.json`、封面画面 `canvas/cover.html`、`sources.json`、`audio-timeline.json` 看索引的 `version`；页文件与画面文件看所在页的 `version`，已有页下这一批才新建的画面文件也一样；只有本批用 `newPages` 成对占位的新页不带版本号。写入回执 `receipt` 按块给声明框高 `rows` 与内容高 `contentRows`（中英各一）；声明不足时回执报最小行数 `minRows`，声明值不改。
- 串接（可选）：同一语言的页文件里给几个正文块写同一个 `chain=<链名>`，链首写带空行分段的整段文字，服务端按页序、块序把第 i 段放进第 i 框，一框一段；段多于框时回执 `receipt.chains` 报「需再加 N 框」并在 `unplaced` 原样带回多出的段，段比框高就在框内滚动（回执 `overflowRows`）。链跨页时链上各页要同批写；只有正文能入链。
- 引用收藏里的块（T3294）：中英两份页文件各写一个简写块 ```block id=<新块 id> cite=<收藏条目号>```（[[tool:library_items.list]]（facet=bookshelf） kind=block 的 `itemId`），宽度、块高与内容由服务端按原块补全，原样置入、只能挪位置，写了不一样的整批 400（rule:"cite_modified"）；只能引自己收藏里的、原文在线、原文不是付费单篇、与上下文无关的块（付费块可以引，读者按原作者的价格与权益判），不在收藏、原文是付费单篇、留言与阅读助手等互动类、来源与目录、已下线各自 400 带 rule 与改法；回执 `cites` 写明落在哪页、原块状态、所在页行数账与新旧版本号；读回时开头行带 `cite=`、[[tool:aifa_pull]] 另回 `cites` 段（出处与状态 current／updated／offline），`updated` 时把 cite 改写成 `refresh` 换新版（中英两份页文件写一份即可，两种语言一起换；两份写了不同的条目号 400 rule:"cite_conflict"）。被引块的来源跟着原文走：引入时快照原文的来源条目，引用处照印，不必写进自己的 sources.json。
- 版本不符时返回 409，服务端不自动重试。正确做法：重新读取，比较差异，保留对方的改动，再决定怎么写。只更换版本号、把旧内容原样重发，会覆盖别人的改动。
- 用户只要求改某一处时，只改那一处。不要顺带补英文、换图或重排其他内容。

### 读取有体积上限，大的读工具分段取

编辑与合集线的读工具一次最多回约 100KB。`collections.retrieve`、`articles.preview`、`articles.check`、`articles.releases.list`、`articles.list` 都收 `offset`/`limit`，返回体里的 `page` 说明总数与下一段起点；`aifa_pull` 读大文件时只传那一个路径，按上次的 `nextOffset` 接着取。`collections.retrieve` 默认只给篇目，要页与块传 `include`。

- 后台各列表工具都收 `offset`/`limit` 并回 `total`，默认一页 25–50 条，上限 100 条。要全量就照 `total` 逐页翻，别把 `limit` 往大里填。
- `page.hasMore` 为真就拿 `nextOffset` 再调一次。`page.cappedBy` 是 `"bytes"` 时，这一段是被返回上限截短的，不是数据到头了——照样接着取。

### 入参约定

- 用 OAuth 令牌时不要传 `onBehalfOf`。令牌已经绑定账号，服务端读到访问令牌就直接取令牌绑定的账号，根本不读这个参数。部分工具说明里写着「Agent 令牌必须传 onBehalfOf，缺失返回 403」，那句话只适用于开发通道（共享密钥方式，生产环境默认关闭）。
- 你的全部动作都记在绑定账号名下。运营账号（全部档、数据范围为全部）可以直接改别人的单篇：字段、页、伴读音频、助手配置与版本回滚，也能替作者发布、撤回候选（直接上线还是进审，看这篇作者的认证，不看发起人；运营自己批准自己发起的候选不受限），改动记在绑定账号名下，版本记录标 `operations:agent`；能不能改看 `permissions.retrieve` 的 `contentReach.writesAll`。合集与单篇同一条规则：运营账号也能直接改别人的合集——名称简介符号目录图、清单增删、重排、封面封底、合集自带页，也能替创建者发布；往合集里收单篇时「是不是别人的」看合集创建者，不看动手的运营：创建者自己的稿草稿也收，别人的要已上线、未屏蔽。代写或代排完成后要交还作者，用 `articles.transfer` 或 `articles.transfer`（action=collection） 把归属转给作者的账号：作者随后在自己的作品列表里看到它、能接着改，署名与线上版本不动，你照样能改；单篇转交回执里 `version` 是版本记录追加的那一版，接着写 `article.json` 要带的是 `indexVersion`。作者档令牌只够绑定账号自己的稿件与合集，账号本身的数据范围再大也一样，别人的单篇与它的助手配置都读不到、改不了，也发布不了、撤不回；别人的合集同理。
- 工具入参是严格校验的：写了工具没有声明的参数名，整次调用会被拒绝（`Invalid arguments`）。按工具的 `inputSchema` 写参数名，不要猜。
- 同一个概念在不同工具里可能用不同参数名：合集的 id 在一些工具里叫 `collectionId`，在另一些里叫 `issueId`，是同一个值；网盘文件的 id 叫 `mediaId`，素材的 id 叫 `assetId`，是两套存储、两种 id，不能互换。
- 标注「也接受 JSON 字符串」的参数（如 `blocks`、`fields`、`timeline`），优先直接传对象或数组。
- 调用模型的工具（`articles.translate`、`articles.run_ai`（action=compose_aids）、`articles.run_ai`（action=glossary））按 Token 计费，记在绑定账号上。你自己能翻译和写作时，不需要调用它们。翻译优先由你提供：双语字段直接两侧一起写，不必先写一侧再调 `articles.translate`；网站自己的翻译只作兜底——你漏了一侧，`articles.check` 会在发布前提醒（不阻断），标题、栏目与来源名称这几处发布时还会自动补上，块内容没有这条自动兜底。署名不在这条自动兜底里：署名只能引用账号，展示名单语，没有「两侧」这回事。

### 动手前先确认的动作

系统允许你独立完成下面这些动作。用户的指令已经明确点名这个动作和对象时，直接执行。用户的指令没有明确点名时，先向用户确认对象和后果，再执行：

- `media.delete`：删除网盘文件，不可撤销。先确认没有内容引用它。
- `collections.delete`：删除未发布的合集，不可撤销；单篇本身不受影响。
- `articles.unpublish`（作者下线自己的）与 `review_items.update` 的 `unpublish`（运营下线任何人的）：单篇下线，读者立刻看不到；再发布即重新上线。
- `moderation_items.moderate` 的 `removed`：删除已发内容，前台不再显示；可改回 `approved` 恢复，被站点内容引用时先取消引用。
- `accounts.update` 把 `status` 改为 `disabled`：该账号此后无法登录。
- `permissions.update`：整组覆盖一个账号的角色，不是追加。对绑定账号自己也能执行，系统只拦「删掉最后一名管理员」，不拦你把自己的角色改没。
- `accounts.revoke_sessions`：对方的浏览器会话、Agent 授权、令牌一起作废。
- `orders.refund`：给订单退款。
- `ai_accounts.adjust_balance`、`ai_settings.update`、`review_settings.update`、`jobs.update`：影响其他账号或全站行为。

### Schema 从哪读

字段规则只有一份真源：校验器、字段说明、MCP 入参与对外的标准 JSON Schema 都从同一份声明投影出来，不会一处改了另一处忘改。要机器可校验的那一份，取 [https://afa5.chaos.callai.online/mcp-guide/schema](https://afa5.chaos.callai.online/mcp-guide/schema)（JSON，匿名可读，不需要登录）。

- 分五层：`block`（五类共用的骨架）、`block/<变体>`（20 份，各变体的内容字段）、`page`（页与栅格的参数与上限）、`article`（单篇级字段）、`collection`（合集可写面）。不传 `name` 回目录。
- 算不出来的规则不混进 `properties`：输入是整页块序列的序列级算法在 `x-computed`，输入超出单个字段的判定在 `x-hook`，两者都写明真源函数在哪。读得到写不进去的字段标 `readOnly`；传 `null` 表示清空那一格的字段标 `x-null-clears`；已退役的键在 `x-retired`。
- 声明默认宽松：没有登记的键一律忽略，不是报错。所以按 Schema 校验通过不等于版面合规——容量、行数与内容完整性仍归 `articles.check`。
- `version` 是内容指纹，内容没变它就不变；HTTP 那一条带 `etag`，可以缓存。
- 排版之前看一眼一种块怎么写，读块技能 `aifa-block-<变体>`（`skills.retrieve` 取）：那是一份人读说明，带用途与注意事项。两件事两个入口，不混在一个返回体里。

### 工具说明里写什么，不写什么

`tools/list` 里每条工具说明只写三样：这个工具做什么、改变调用形状的约束（必填、互斥、单位、枚举、乐观锁、清空语义）、失败回执要怎么处置。其余口径不进说明，都在这一页，按需读一次即可。

- 档位与权限：调用它至少要哪一档授权，看页末工具目录的「档位」一列。够不着时服务端返回 403 并点名缺哪项能力，不必先自查；要自查用 `permissions.retrieve`。
- 决策出处与沿革（第几条、哪一天、哪个任务号）一律不进说明：它们对这一次该怎么调没有作用。
- 块坐标：块本身没有坐标字段，位置由顺序、宽度档与留白块排出来。`articles.preview` 给现算坐标——`topRow` 从版心第几行开始（1 起）、`bottomRow` 占到第几行为止、`visualRow` 第几个视觉行（并排的两个半宽块共用一个）、`column` 横向位置（`full` 整幅、`left` 左半、`right` 右半，落单的半宽块是 `left`）。往前插一块留白（`blank`）就整体下移，改宽度档就换列。
- 写页回执：`aifa_push` 不回你刚交的内容，也不回全文——回执逐文件给版本号；落盘那份与交上去的不同时标 `stored`：`normalized` 只差空白与写法，`rewritten` 是服务端改了内容（裁图换了素材号、规整摘了字段、新页占位名换成页号，改了什么在 `changes`），`generated` 是它代你生成的那一份，要全文用 `aifa_pull` 按 `paths` 取。`receipt.blocks` 只列有问题的块，其余记 `blocksOmitted`，全量用 `articles.check` 的 `include:["measured"]`；单次回执上限 12000 字节。页级校验结果 `issues` 挂在这一页的第一个文件上（没有问题不出现），与编辑台画布上的红角标同一份，逐条带 `blockId`。越页、并排不等高、低于最小行数这类物理规则当场 400、整批不写，同一批里的问题一次列全；其余只报告不拦截。回执另带 `publishCheck`（发布前检查的拦截数与提醒数，有拦截时 `first` 给至多三条）与一句 `next`：没有拦截项时给发布调用（带 `expectedVersion`），有就指向 `first` 与 `articles.check`。内容完整性（双语、必填、示例内容）的全量结果归 `articles.check`，按页查传 `pageId`。
- 要人确认的动作、分页口径、版本号从哪读，分别见本节其余几条与说明全文的「九、要人确认的事」（作者档没有落进那一档的工具）。

## 五、方法与域

全部能力经单一入口 `https://afa5.chaos.callai.online/mcp` 调用，一次授权。规格按资源域分侧实现：杂志设置与审核队列、内容审核（屏蔽与删除）、技能包与作品包、留言、素材与媒体、单篇与工作流、合集、阅读与读者 由杂志侧实现；运维探活与版本、Agent 授权与 OAuth、统计、外链登记、服务交付（工单、资料交付、线索、反馈）、账号资料与作者主页、消息 im（独立服务）、信息流 feed（独立服务）、外壳与书架、计费与权益 由宿主侧实现；需确认动作、收藏与块引用、页与块、模型调用与额度 两侧各有方法。调用时不用区分方法归哪一侧。

有哪些方法，看[速览页](https://afa5.chaos.callai.online/mcp-guide#tools)的方法索引（服务端从规格现算）；一句话找方法用 `aifa_find`。每个方法的入参、返回字段、状态码、报错码与边界规则写在规格的方法备注里，不在本页逐条抄：`aifa_describe` 传方法 ID，回执的 `note` 就是这一段，`errors` 是它可能回的全部报错码（规格里带备注的方法共 271 个）。

单篇、页、块与合集的概念、文件格式与版面物理规则写在技能包里：写稿必读 `aifa-author/SKILL.md`（`aifa_skill` 取），每种块的写法在块技能 `aifa-block-<变体>`，字段的机器口径在 `/mcp-guide/schema`。

## 六、常见流程

每条流程都从连接完成之后开始，并且默认你已经调过 `permissions.retrieve`。逐步写法与自查在技能包里，这里只给方法链；每一步的入参用 `aifa_describe` 取。

| 流程 | 方法链 |
| --- | --- |
| 从写稿到发布（作者档） | 照必读 `aifa-author/SKILL.md` 走：`aifa_pull` → 改文件 → `aifa_check` → `aifa_push` → `articles.check` → `articles.preview_links.create` 给人看 → `articles.publish` |
| 修改已发布的单篇（作者档） | `articles.request_revision` 开修改稿 → 文件三步 → `articles.publish` 进审；放弃修改用 `articles.discard_revision` |
| 文件上传（作者档） | `uploads.create` 取上传凭证 → PUT 字节 → `media.create` 入库，页文件里引用回执给的素材号 |
| 组一个合集并发布（作者档） | `collections.create` → `collections.items.batch_add` → `collections.check` → `collections.publish` |
| 处置单篇下的留言（作者档） | `comments.list` → `comments.batch_moderate` |
| 审核单篇、下线单篇（全部档，运营角色） | `review_items.list` → `review_items.update` 或 `review_items.batch_decide` |
| 账号、角色与 AI 额度（全部档，管理员角色） | `accounts.update`、`permissions.update`、`ai_accounts.adjust_balance`（要人确认，见第九节） |

## 七、报错怎么读

调用出错时，工具返回带 `isError` 的文本，通常以 `HTTP <状态码>` 开头，后面是服务端的错误信息；`reason` 是机器可读的原因码。先读原因码和文案，再决定怎么做。同一个错误不要原样重试。

| 状态 | 含义 | 处置 |
| --- | --- | --- |
| `Invalid arguments` | 参数名或参数类型不符合工具的 `inputSchema`。请求没有到达服务端。常见的有：`expectedVersion` 传了 `null`（它只收数字，页的版本号在 `article.json` 的 `readonly.pageSummaries[].version`）；`expiresIn` 传了 `"1h"` 这类字符串（它是数字小时数）。 | 对照 `inputSchema` 修正参数名与类型。不要换别的工具尝试。 |
| 400 | 参数不合法或缺少必填项。例如审核动作缺 `note`；`aifa_push` 的 `newPages` 不成对；`article.json` 的 `backCoverColor` 不在五色板里；`articles.preview_links.create` 的 `expiresIn` 不是 0.25、1、24、168；上传时 `filePath`、`base64`、`ticketId` 给了不止一个。 | 按错误信息补全或修正参数。 |
| 401 | 没有带令牌，或令牌过期、已被吊销，或绑定账号被停用，或绑定账号的全部访问被吊销。 | 刷新令牌。刷新后仍 401，按「连接失败时的排查」告诉用户。 |
| 403，文案含「此授权为作者档」 | 档位不够。 | 告诉用户需要重新授权为全部档。不要重试。 |
| 403，文案写明「不属于你」 | 对象不属于绑定账号（别人的单篇、别人单篇下的留言、别人上传的网盘文件），或令牌是作者档。 | 核对 id。确实是别人的单篇时，改它要绑定账号持数据范围为「全部」的内容写权限并授权全部档，告诉用户。 |
| 422，文案写明「封面不是块页」 | 对封面那个派生页 id 写了页文件。封面由字段生成。 | 改 `article.json` 的 `title`／`byline`／`coverImage`／`coverFocal`／`coverMedia`／`coverFrame`；合集封面改 `collections.update`。 |
| 422，文案写明「封底不是块页」 | 对封底那个派生页 id 写了页文件。封底是纯色底、站标、编号。 | 单篇封底底色改 `article.json` 的 `backCoverColor`，合集封底改 `collections.update` 的 `backCover.color`。 |
| 403，文案写明「数据范围」 | 能力够，但账号的数据范围只到「已指派给我的」，这一条归了别人。 | 告诉用户请管理员把数据范围放宽为「全部」。 |
| 403，其他文案 | 绑定账号没有这项能力。 | 用 `permissions.retrieve` 的 `capabilities` 核对。确实没有时告诉用户。 |
| 403 `reserved_scope_missing` | 凭据缺这项保留范围项（见第八节）。 | 停下，告诉用户缺哪一项，由他决定重新授权勾选或自己在网页上做。 |
| 404 | 对象不存在；或 MCP 会话已被回收（文案为「会话不存在」）；或短期预览链接已吊销。 | 对象不存在时重新列表核对 id。会话被回收时重新 `initialize`。 |
| 409 版本冲突（文案含「版本冲突」或 `version_conflict`） | `expectedVersion` 与服务端当前版本不一致。站点内容的 409 带 `currentVersion` 和 `view`。 | 重新读取，比较差异，保留对方的改动后再写。回执丢了、不知道上一次写没写进去时，原样重发同一批是安全的：`aifa_push` 认出重试，原样回首次的回执并带 `replayed: true`，不再落盘；`articles.publish` 重试回成功并带 `unchanged: true`。 |
| 409 其他原因码 | 当前状态不允许这个动作。常见的有：`article_in_review` 审核期间工作稿锁定；`withdraw_not_allowed` 没有可撤回的候选版本；`article_not_published`／`article_blocked` 要收的单篇没上线或被屏蔽；`collection_last_item` 已发布合集至少留一篇；`name_taken`、`name_locked`、`icon_locked` 等合集名字与图标的拒绝；工单已关闭；要删除的内容仍被别处引用（返回里带全部引用处）。 | 按原因码处理。这类 409 与版本无关，重新读取版本号没有用。 |
| 409 `source_disabled` | 用户关闭了这一处 AI 消费的个人开关。这是回执，不是故障。 | 改用不调用模型的做法，或在用户同意后用 `ai_accounts.update` 重新打开。 |
| 409（上传凭证） | 上传凭证还没收到字节就被拿去完成入库，或已经完成过一次入库；或凭证已封口（带密钥的 PUT 成功过一次）又 PUT 了一次。 | 先 PUT 字节；重试前重新调 `uploads.create`。 |
| 410 | `reading_states.update` 的页面不属于当前发布版本。 | 重新读取当前版本的页再写。 |
| 413 | 上传超出体积上限。 | 改用上传凭证两步上传，或确认文件没有超出目标的字节上限。 |
| 415 | 声明的 `contentType` 与实际类型不符，或不在白名单内（签发上传凭证时就判）；PUT 时 `Content-Type` 头与建上传凭证时不一致。 | 核对类型。 |
| 422，带 `violations` | 内容超过契约的硬上限，没有写入。硬上限只有体积这一类：画面块源码字节、一页的块数、一篇的页数、单个文本字段的保护性上限。逐条列出了超限位置。 | 按列出的那一条缩减体积或拆开；字数与块高不在硬上限里，文字超出块高在块内滚动。 |
| 422 `checks_failed` | 发布前检查有阻断项，没有发布。 | 调 `articles.check` 读取逐条失败项，改完后再发布。 |
| 422，其他 | 图片外链抓取失败。 | 按错误信息修正。 |
| 423 | 这一页含人工锁定的内容。 | 不要尝试解锁。报告用户。 |
| 428 | `articles.restore_release` 缺 `expectedVersion`。 | 先读 `workflow.currentVersion`，再提交。 |
| 429 | 触发频率限制（登录取码、留言发送这类入口都限流），或绑定账号本月 Token 额度已用完，或全站同时有效的上传凭证已满。 | 频率限制时读响应头 `Retry-After`，按它给的秒数等一次再试，只试这一次。不要循环重试：重试本身会把限流窗口填满，等待时间只会越来越长。额度用完时改用不调用模型的做法，并告诉用户。 |
| 503 | `articles.pages.render` 出图排队已满或排队过久，或渲染服务暂时不可达；出图忙时先排队，不回 429。 | 按报错里给的秒数（HTTP 响应体 `retryAfter`）等一次再试，只试这一次；仍是 503 就先做别的，稍后再看。 |
| 「杂志系统不可达」或网络错误 | 服务端暂时不可用。 | 用 `system.retrieve` 探测。间隔一段时间后重试，最多三次；仍然失败就告诉用户。 |

`articles.check` 把「字段类型不对」（例如给单语字段塞了 `{zh, en}` 对象）也报成「未填」。看到「未填」而你确实填了时，先核对这个字段是单语还是双语。

## 八、四个保留范围项

### 四件事要凭据持有对应范围项

平台只认凭据与它的授权范围，不分人与 Agent、官方与第三方（A0471）。下面四件事各是一个保留范围项：用户在网页登录得到的全权凭据都有；转授给你的授权缺省一项不带，只有用户在同意页逐项勾选过才有。缺了就回 403 `reserved_scope_missing`，`scope` 写明缺哪一项。完成付款没有本站接口可拦，收银台在支付平台上。

| 范围项 | 事 | 对应接口 |
| --- | --- | --- |
| `account.email.change` | 修改登录邮箱 | `POST` / `PATCH /api/accounts/me/email` |
| `billing.pay` | 完成付款 | 没有本站接口；下单、取消订单、申请退款不要范围项，见 `orders.create`（action=plan） |
| `agent-authorization.manage` | 授权管理：签发、收窄、收回别的授权，批准设备码接入 | `agent_authorizations.create`、`agent_authorizations.update`、`agent_authorizations.delete`、`device_authorizations.approve` |
| `account.authenticator.manage` | 绑定或解绑验证器、重新生成备用码、用验证器确认 | `/api/accounts/me/authenticator`、`confirmations.approve` |

授权只能收窄转授：你签出的授权，档位、只读与保留范围项都不能大于你自己这张。遇到 `reserved_scope_missing` 不要重试、不要换路径绕过；告诉用户哪一项缺了，由他决定要不要重新授权并勾选，或自己在网页上做。读授权清单、订单、登录邮箱不要范围项。

### 其他要停下来找用户的情况

- 授权同意页：必须由用户本人在浏览器里登录并点同意。
- 档位不够：请用户吊销后重新授权为全部档。
- 绑定账号缺少角色（全部档仍然 403）：请用户换账号，或请管理员授予角色。
- 页面含人工锁定的内容（423）。
- 要凭口味定夺的事（版面是否好看、图是否切题）：先用 `articles.pages.render` 自己看过，再请用户打开预览链接定夺。
- 本月 Token 额度用完（429）或 AI 余额不足：额度由管理员调整，告诉用户。
- 「二、能做什么，不能做什么」里「目前没有工具的事」清单中的任何一项。

## 九、要人确认的事

有几件事你调得动，但调用只发起请求：系统不改动线上，把这一次调用冻住，回一条确认链接，人打开链接、填身份验证器上的验证码，才真的执行。这几条工具的说明第一句都写着这一点。验证码不经过你——任何情况下都不要向任何人索要验证码，验证码只填在那个页面上。这一类动作全要全部档，作者档的工具一条都不在其中，作者档的调用都直接生效。

### 哪几件

| 事 | 资源方法或登记名 | 谁来确认 |
| --- | --- | --- |
| 退款 | `orders.refund` | 持 `billing.manage` 能力的人 |
| 核对订单 | `orders.reconcile` | 持 `billing.manage` 能力的人 |
| 保存套餐 | `plans.update` | 持 `billing.manage` 能力的人 |
| 调整 AI 余额 | `ai_accounts.adjust_balance` | 持 `billing.manage` 能力的人 |
| 改 AI 计价 | `ai_settings.update` | 持 `billing.manage` 能力的人 |
| 改平台抽成比例 | `billing_settings.set_platform_fee` | 持 `billing.manage` 能力的人 |
| 应用实验空间到线上 | `workspaces.apply` | 持 `site.publish` 能力的人 |
| 设置模型密钥 | `ai_settings.set_provider_key` | 持 `accounts.manage` 能力的人 |
| 清除模型密钥 | `ai_settings.clear_provider_key` | 持 `accounts.manage` 能力的人 |
| 放宽反滥用策略 | 资源方法 `abuse_policies.update` | 持 `accounts.manage` 能力的人 |
| 启用或推荐技能包 | `skills.enable` | 持 `skills.manage` 能力的人 |

清单之外的动作一条都不受影响，照常直接执行。`ai_settings.update` 按字段判：只有传了 `tokenAnchor`、`usdRate`、`priceBook`、`tiers` 其中任何一段才要确认，只改 `providers` 或 `voices` 照常直接生效。

### 怎么走完

- 照常调用那条工具。返回体第一项是 `confirmUrl`，第二项 `note` 是给你的交接话：这次调用没有改动线上，把链接交给人类，不要自己打开确认。其后是 `needsConfirm: true`、`confirmBy`（要谁确认）、`summary`、`eventId` 与 `expiresAt`（30 分钟）。
- 把 `confirmUrl` 原样发给 `confirmBy` 写的那个角色。链接不要求登录，他打开就看得到这一件的事实表；应用到线上那一条还带着预览地址，他会先点开看效果再填码。
- 用 `aifa_confirm_read` 查结果，给了 `waitSec`（最多 25 秒）就挂着等，人一确认立刻返回。`status` 只有规格那一套词：`succeeded` 带执行结果，`failed` 带失败详情。
- 参数发现写错了，用 `confirmations.cancel` 撤回这一件，链接随即失效，然后重新发起。

### 会撞到的几种回答

| 情形 | 怎么办 |
| --- | --- |
| `authenticator-not-bound`（403） | 发起这次调用的账号（不是后面来确认的 confirmBy 那个角色）还没绑身份验证器，事件连生成都没生成。返回体里带一条 `bindingUrl`，把它交给发起这次调用的账号本人，绑完再重发这条动作 |
| `expired` | 30 分钟没人确认。重新发起，拿一条新链接 |
| `canceled` | 人在页面上否掉了，或发起人撤回了。不要重发，先问用户为什么 |
| `pending` 且带 `lockedUntil` | 验证码连错 5 次，这一件锁 15 分钟。告诉用户几分钟后再试 |
| `failed` 且失败详情列着几页 | 应用到线上时线上已被别处改过，一条都没写。回空间里核对那几页、改好，再发起一次应用。**没有强推这条路，不要去找** |
| `failed` 且失败详情写着前值变了 | 动钱那几条在执行那一刻会重算一次前值：冻结时算的余额、订单状态或套餐版本已经被别人改过，于是一个字都没写。重新读一遍当前值，确认还要不要做，再发起一次 |

发起这条动作的账号自己不够格确认时（例如运营发起、要管理员确认），返回里的 `confirmBy` 已经写明要交给谁，照着说就行。

## 十、做完之后怎么汇报

用户通常不看工具调用的过程。任务结束时用用户的语言写一段简短的汇报，包含下面几项：

- 结果：做成了什么。写明对象的名称和 id（单篇、合集、空间、工单、账号）。
- 当前状态：单篇写明是草稿、待审、已上线，还是已上线但改动尚未发布。
- 给用户看的链接：草稿预览、短期预览链接（写明失效时间）、线上地址或空间预览地址，并说明打开它需要登录哪个账号。
- 自查结果：`articles.check` 是否通过，还剩哪些提醒项，你对它们的判断。
- 等用户处理的事：需要他本人做的动作、需要他确认的选择、你没有做的部分和原因。
- 消耗：调用过按 Token 计费的工具时，写明用了哪些。

不要把「写入成功」说成「已发布」，不要把「检查通过」说成「已审定」，不要把进入审核队列说成「已上线」，不要把「回复成功」说成「客户已收到」。

机器检查不能代替语义、事实来源和视觉的审核。
