Merge branch 'master' into fix/account-info-corner-mask
Project CI / Frontend tests (pull_request) Successful in 3m41s
Project CI / Native shell tests (pull_request) Successful in 14m5s
Project CI / Backend tests (pull_request) Successful in 4m21s
Project CI / Repository checks (pull_request) Successful in 1m5s

This commit is contained in:
2026-08-07 19:59:40 +08:00
6 changed files with 675 additions and 37 deletions
@@ -365,13 +365,36 @@
"ExternalApiKey": []
}
],
"parameters": [
{
"name": "view",
"in": "query",
"required": false,
"description": "返回视图。full 返回完整项目、画布、图层与资源;summary 只返回项目选择所需元数据和封面稳定引用。MCP 的 list_editor_projects 工具固定使用 summary。",
"schema": {
"type": "string",
"enum": [
"full",
"summary"
],
"default": "full"
}
}
],
"responses": {
"200": {
"description": "项目列表",
"description": "项目列表。view=full 返回完整项目列表;view=summary 返回紧凑项目摘要列表。",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ExternalEditorProjectListResponse"
"anyOf": [
{
"$ref": "#/components/schemas/ExternalEditorProjectListResponse"
},
{
"$ref": "#/components/schemas/ExternalEditorProjectSummaryListResponse"
}
]
}
}
}
@@ -2431,6 +2454,87 @@
}
}
},
"ExternalEditorProjectSummaryListResponse": {
"type": "object",
"required": [
"projects"
],
"properties": {
"projects": {
"type": "array",
"description": "用于展示、查找、同名确认和安全选择目标的紧凑项目摘要;不包含 canvas、viewport、layers 或 resources。",
"items": {
"$ref": "#/components/schemas/EditorProjectSummary"
}
}
},
"additionalProperties": false
},
"EditorProjectSummary": {
"type": "object",
"required": [
"projectId",
"title",
"updatedAt",
"cover"
],
"properties": {
"projectId": {
"type": "string"
},
"title": {
"type": "string"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"cover": {
"description": "项目最新封面快照的稳定引用;项目没有封面时为 null。需要展示时使用 objectKey 调用 /assets/read-url 获取临时签名 URL。",
"anyOf": [
{
"$ref": "#/components/schemas/EditorProjectSummaryCover"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"EditorProjectSummaryCover": {
"type": "object",
"required": [
"resourceId",
"objectKey",
"width",
"height",
"updatedAt"
],
"properties": {
"resourceId": {
"type": "string"
},
"objectKey": {
"type": "string",
"description": "封面对象的稳定引用,不是图片正文、Data URL 或临时签名 URL。"
},
"width": {
"type": "integer",
"minimum": 1
},
"height": {
"type": "integer",
"minimum": 1
},
"updatedAt": {
"type": "string",
"format": "date-time"
}
},
"additionalProperties": false
},
"ExternalEditorProjectDeleteResponse": {
"type": "object",
"required": [
@@ -4207,6 +4207,13 @@
- 验证:覆盖“服务端已入队但提交响应丢失”后两次 POST 的 endpoint、正文 bytes 与 `Idempotency-Key` 完全相同,原键重试仍返回同一 operation,最终只出现一份 completed result 和一次计费 / 写回;恢复再次 transport 失败或临时鉴权失败仍保留同一账本;换 owner 不可见;MCP 与 REST 对同一 owner、同一请求和同一键必须命中同一 operation。
- 关联:`server-rs/crates/api-server/src/external_generation.rs``server-rs/crates/api-server/src/external_mcp.rs``docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md`
## MCP 列表不能透传完整项目快照(2026-08-07)
- 现象:账号项目数量增长后,`list_editor_projects` 把每个项目的 `canvas / layers / resources` 全量透传,REST 响应超过 MCP 4 MiB 上限,Agent 因整批失败而无法展示、查重或安全选择项目;缺少必填请求体时,内部 Axum JSON extractor 的文本 `415` 又会被泛化成“非 JSON 响应”。
- 处理:项目列表 REST 保持默认 `view=full` 兼容,并提供 `view=summary`MCP 固定使用 summary 且不向 Agent 暴露或接受 `view=full`。摘要只返回 `projectId / title / updatedAt / cover`,封面取最新且存在稳定 `objectKey``project-cover-snapshot`,展示时再调用 `/assets/read-url`,不在列表内嵌图片或签名 URL。MCP 在构造内部 REST 请求前按 OpenAPI schema 校验 required body;缺正文和缺字段分别返回结构化错误,不进入写入、上传票据或计费路径。
- 验证:用 19 个完整序列化后超过 4 MiB 的项目 fixture 证明摘要仍低于上限且不含大型布局;覆盖四个历史 `415` 工具的缺正文、空对象和非对象输入,并断言项目列表工具固定 summary、调用方不能通过 query 覆盖。
- 关联:`server-rs/crates/api-server/src/external_mcp.rs``server-rs/crates/api-server/src/external_editor_api.rs``docs/openapi/genarrative-external-v1.openapi.json``docs/【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md`
## api-server 嵌入仓库外资源时必须同步容器构建上下文(2026-07-31)
- 现象:本地 `cargo test` 可以编译 MCP 与 Skill 下载模块,但 api-server 镜像在 Rust 编译阶段报 `include_str!` 找不到 OpenAPI 或 Skill 文件。
@@ -17,7 +17,7 @@ v1 只开放以下能力:
- `POST /api/external/v1/assets/direct-upload-tickets`:创建素材直传 OSS 凭证。
- `POST /api/external/v1/assets/objects/confirm`:确认已上传素材对象,`ownerUserId` 固定为 API Key 所属账号。
- `GET /api/external/v1/assets/read-url`:获取私有素材读取签名 URL。
- `GET /api/external/v1/editor/projects`:列出当前 API Key 所属账号的图片画布项目。
- `GET /api/external/v1/editor/projects`:列出当前 API Key 所属账号的图片画布项目`view=full|summary`REST 默认 `full`MCP 固定使用 `summary`
- `POST /api/external/v1/editor/projects`:创建图片画布项目。
- `GET /api/external/v1/editor/projects/recent`:读取当前账号最近图片画布项目。
- `GET /api/external/v1/editor/projects/{projectId}`:读取项目与默认画布。
@@ -83,6 +83,8 @@ MCP transport 的 DNS rebinding 防护必须同时允许正式入口 `www.genarr
MCP tools 从同一份 OpenAPI operation 自动形成 snake_case 名称,并在进程内复用 External REST router,因此鉴权、scope、owner、入参、幂等、计费和结果查询契约只有一份。生成 tools 把 `idempotencyKey` 显式放进参数,因为 MCP transport 的 Authorization 头不能代替逐次业务幂等键。工具结果使用 `structuredContent`;业务失败使用 `isError=true` 的结构化安全错误,协议不可路由时才返回 JSON-RPC error。
`list_editor_projects` 是项目选择工具,服务端固定以 `view=summary` 调用项目列表,不允许因 OpenAPI 的 REST 默认值退回完整视图。摘要逐项目只返回 `projectId``title``updatedAt` 和可空 `cover`,不携带 `canvas``viewport``layers``resources` 或图片正文;选定目标后再用 `get_editor_project` 读取完整权威状态。`cover` 只包含最新项目封面快照的 `resourceId`、稳定 `objectKey`、尺寸与 `updatedAt`,没有封面时为 `null`。需要展示封面时,以 `objectKey` 调用 `/api/external/v1/assets/read-url` 获取短期签名 URL;列表不得内嵌 Data URL、图片二进制或临时签名 URL,也不得把签名 URL 当作持久引用。
MCP 暴露下列稳定文本资源:
- `genarrative://external-editor/usage`:关键工作流和异步轮询规则。
@@ -257,6 +259,8 @@ docs/openapi/genarrative-external-v1.openapi.json
- 角色图、图标 spritesheet 和 UI 素材提取的 completed result 允许携带 `EditorGenerationWarning`;provider 原图保留降级与自动拆分降级必须保持成功状态,并分别使用通用 `warning` 与兼容 `sliceWarning` 表达。
- 外部视频、角色动画、音效和音乐接口使用站内编辑器相同的请求校验、模型限制和价格校验。
- OpenAPI JSON 能被 `serde_json` 解析,且 security scheme 为 Bearer API Key。
- 项目列表 REST 默认 `view=full` 并保持完整响应兼容;`view=summary` 只返回项目选择元数据和可空封面稳定引用,MCP `list_editor_projects` 固定使用该摘要视图,不因完整项目数据量增长触发返回体上限。
- 摘要封面不内嵌图片或签名 URL;使用 `cover.objectKey``/assets/read-url` 后才能临时展示。
- OpenAPI JSON 不包含 `/api/profile/api-keys``UserAccessToken` 或 API Key 管理 schema。
- `agent-integration.json` 能发现 MCP、OpenAPI、Skill entry/archive;下载 archive 的 SHA-256 与 manifest 一致,ZIP 包含 `SKILL.md`、四篇 references、Python helper 和 `agents/openai.yaml` 七个声明文件且不含凭据。
- MCP 在无 Bearer、Bearer 格式错误或 Key 无效时返回相同的 `401 + WWW-Authenticate + details.guide` 鉴权引导,且不暴露 tools/resources/owner;合法 Key 可完成 initialize、tools/list、resources/list/read 和生成提交/查询;resource catalog 必须包含 usage、OpenAPI、`skill` 主入口和当前全部 Skill references,当前精确为 `skill/references/capability-routing.md``skill/references/api-operations.md``skill/references/authentication-and-safety.md``skill/references/requests-and-outputs.md`,且不包含 CLI 脚本、测试或 workflow;多实例不依赖 sticky session,不暴露内部 SpacetimeDB MCP 或 worker 控制面。