🔴 P0 feat(mcp): MCP 有契约有用例,**中间那层没接** —— 8 条契约操作零 controller、零 UI、零断言
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 969
Description
## 人类扩了范围
原八步不含 MCP。人类 2026-08-05:**「chat + agent + skill + MCP 要全部跑通」**。
⇒ MCP 正式纳入范围,打 `sprint-2`。**这不是越界,是范围被明确扩了。**
## 实测(SHA `5740f17f`,grep 前先跑正样本)
由 coord-chat-e2e 报出。**它的结论对,但有一格诊断是错的,我复核时纠正:**
| 层 | 它报的 | **实测** |
|---|---|---|
| 契约 | ❌ 无专属契约 | ✅ **有,8 条操作**,在 `agent-runtime.ts` 里(不是以 `mcp` 命名的文件) |
| application | ✅ 6 用例 | ✅ **10 个文件** |
| controller | ❌ | ❌ **确认为 0** |
| 前端 | ❌ | ❌ 确认为 0 |
| 八步板 | ❌ | ❌ 确认为 0 |
⚠ 它查的是**文件名**(`packages/contracts/src/*mcp*` → 0),没查内容。`agent-runtime.ts` 里 **89 处**命中 mcp。**尺子只验了一半。**
八条契约操作(`agent-runtime.ts:1050-1355`):
```
POST/GET /mcp-servers GET /mcp-tools
POST /mcp-servers/:serverId/discover POST /mcp-servers/:serverId/auth-scope
POST /mcp-tools/:toolFullName/auth-scope
POST /mcp-servers/:serverId/review POST /mcp-servers/:serverId/isolate
POST /tool-calls/authorize
```
**每一条的 controller 命中都是 0**(正样本 `/agents/:agentId/skill-mounts` 命中 `skill-mount.controller.ts`,尺子有效)。
## 这与今早的 skills 是同一个形状
今早 skills 也是「契约有、application 有、controller 无」,接通用了 #518 + #520 两个 PR。**MCP 现在站在同一个位置。**
⇒ **好消息**:不需要新契约面,不走 design-delta,不用等人类补签。**这一条比 #496/#552 都简单。**
## 🔴 裁决:**先接线,后写断言 —— 不先写 `test.fail`**
coord-chat-e2e 建议「先在八步板开一条 `test.fail` 命名缺口」。**我否掉这一半,理由是它自己给的:**
> ⚠ 那条断言**必须锚在真实出口上** —— 我今天在这个文件里写坏过五条锚点,别来第六条。
**现在没有 controller、没有 UI ⇒ 任何 testid 都只能是编的。** 先写就是第六条坏锚点,而且这次是明知故犯。
⇒ **缺口靠这条 issue 可见、有名字、有 owner,这已经满足「缺口要可见」。** 断言与实现**同一个 PR 落地**,锚点从真实组件里读出来。
## 范围(最小可证明的一条链)
按它读那 6 个用例给出的语义,我复验后收窄为**一条端到端链**:
1. **注册一个 MCP server**(`POST /mcp-servers`)→ 列表可见 → 刷新仍在;
2. **discover 工具**(`POST /mcp-servers/:serverId/discover`)→ `GET /mcp-tools` 列得出来;
3. **授权层 1 放行/拒绝**(`POST /tool-calls/authorize`)→ **拒绝路径必须也被断言**,否则一个「什么都放行」的实现全绿;
4. **调用被记账**(`record-customer-data-call` / `record-quarantine-call` 两个用例已在)。
⚠ **review / isolate 两条本波次不做** —— 它们是治理动作,不在「跑通」的最小语义里。**明确划出,不是遗漏。**
## 验收(含反证)
1. `core-loop.spec.ts` 新增 **步骤 9:MCP 工具发现 → 授权 → 调用记账**,真断言不是 `test.fail`;
2. **反证 A**:把 `POST /tool-calls/authorize` 的拒绝分支改成恒放行 ⇒ 断言必须变红(证明拒绝那一半真的被测到);
3. **反证 B**:把记账写入摘掉 ⇒ 必须红,**且红在 reload 之后**(区分「写进库」与「只写进 React state」);
4. **反证 C**:断言必须红在目标那一步之后、更早的步骤仍绿。
## ⚠ 三条硬约束
- **动 controller 前先核 `next.config.mjs` 的两条 rewrite**(裸路径 + `:path*`)——**这个坑今天已第五次**(`/capabilities`、`/canvas/templates`、`/skills`、`/agent-runs`);
- **testid 跟随既有前缀**,不造第二套命名;
- **写下任何标识符前先在源码里定位**,grep 前跑正样本验尺子 —— 今天全队因此栽过八次。
**Owner**:coord-chat-e2e(chat/agent/skills 本来就是它的 area)
**承诺**:22:00
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with core-loop.spec.ts and the eight MCP operations in agent-runtime.ts:1050-1355, then inspect the existing application and controller patterns. Before changing controllers, verify the two rewrites in next.config.mjs and locate existing testid prefixes in the source. Done means the MCP registration, discovery, authorization allow/deny, and call-recording flow passes, including reload persistence and the stated negative checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, full-stack, testing-qa
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100