Files
qinglong/docs/adr/ADR-0350-bounded-local-mcp-task-discovery.md
T

50 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0350:有界、低敏的本机 MCP Task 发现
- 状态:Accepted
- 日期:2026-08-10
- 关联 RFCQL-RFC-0001 D-05、D-06、D-08、D-13、D-17、D-28、D-75、D-85、D-87、D-157、D-257、D-259、D-260、D-261、D-262
- 关联 ADRADR-0052、ADR-0087、ADR-0090、ADR-0272、ADR-0345、ADR-0346、ADR-0347、ADR-0348、ADR-0349
## 背景
本机 MCP 已能发现 Run、读取 Run 快照并分页诊断事件,但 Agent 仍必须由用户提供 `taskId`,无法先回答“当前 Project 有哪些 Task 可以运行”。现有 TaskDefinition 双方言 source 已提供 Project-scoped、按 `taskId` 严格递增的 keyset 分页和 `limit+1` 截断判断,不需要增加 migration、表或索引。
TaskDefinition 完整记录包含命令/spec config、description、labels、mutation 和 digest。这些字段既可能含敏感执行细节,也会把未来定义格式直接冻结成 MCP 公共协议,因此不能透传。
## 决策
在既有可选 stdio sidecar 增加第四个只读 Tool`qinglong.task.list@1.0.0`。输入固定为 `{after?, limit?}``limit` 默认 32、最大 64`after` 只能使用服务端返回的 `{taskId}`。Project 继续来自私有 MCP 配置,不接受客户端选择。
输出固定为 `{tasks, hasMore, next?}`,每项只包含:
- `taskId`、当前 `revision` 与显示 `name`
- `kind``specSchema``enabled`
- `updatedAtMs`
不得返回 `projectId`、description、spec config/command、labels、mutationId、contentDigest、Secret reference 或数据库 row。当前页只表示 current Task headsdisabled Task 保留在结果中并显式标记 `enabled=false`,避免通过过滤造成无界扫描或游标歧义。projection 必须拒绝跨 Project、乱序、越过 cursor、超量或 continuation 不一致的 adapter 结果。
调用顺序固定为 authentication → exact `tool.call:qinglong.task.list` + `task.read` Policy → durable audit → credential `confirm()` → Project-scoped bounded TaskDefinition read。MCP 仍使用单 SQLite authority,不缓存 Task,不增加连接、timer、watcher、listener、后台服务或写 authority。
实现放入既有 `@qinglong/runtime-core/tool-execution``@qinglong/local-sqlite/task-definition``@qinglong/local-mcp-server/application-runtime` owner。只增加精确 Runtime Core subpath,不增加 root export 或 workspace package。PostgreSQL `TaskDefinitionSource` 契约保持可复用,但本批 MCP 只装配本机 SQLite sidecar,不修改 Cluster runtime。
## 被否决方案
1. 返回完整 TaskDefinition:会泄露命令和配置,并把内部 schema 固化为外部协议。
2. 只返回 enabled Task:存储按 `taskId` 分页,投影层过滤会导致页大小不确定、潜在无界补读和游标跳跃。
3. 客户端传 Project:会把租户边界从可信部署配置移到不可信参数。
4. 新建 Task discovery package:没有独立部署、authority 或依赖边界,只会增加 importer、SBOM 和低配制品成本。
5. 把 MCP SDK 合入默认 application:会让未启用 AI Client 的路由设备承担约 9.81 MB/194 modules 的可选闭包。
## 验证
- Runtime Core 458/458exact Tool、默认/最大页、cursor、字段脱敏、跨 Project/乱序/超量/continuation fail-closed。
- Local SQLite 204/204:复用 current TaskDefinition head、Project keyset 与单 authority close fence。
- Local MCP 9/9:四 Tool 静态发布、`task.read` admission/audit 顺序、真实 stdio/API Credential/SQLite Task discovery E2E。
- package boundary/dependency/edge import compatible17 package、993 source 中 967 nested、26 root97.4%),无单文件或浅层 package。
- 十二档 artifact compatible。最紧 Standalone Application AI 为 6,270,988 bytes/638 files,距 6 MiB 上限 20,468 bytesMCP Edge/Standalone 为 9,809,892/9,810,000 bytes、942 files、194 loaded modulesRSS 增量 36,405,248/37,011,456 bytes。
- 未新增 production dependency、migration、表、索引、数据库连接或后台资源。
## 后续约束
Task point read、按 kind/label 搜索、Task spec/command 展示、Task start 和任何写 Tool 必须另立 ADR。尤其 Task start 必须绑定当前 revision、输入 schema、approval、Run admission、幂等与 durable audit,不能把本只读 discovery cursor 或 content-free projection 当作执行授权。