status: done created: 2026-07-11
/apply 前先跑 AI 插件和 Admin 当前测试基线,区分已有失败与本变更回归;execution-log.md,禁止只写“已通过”;code-copilot/rules/automated-testing-standard.md 复用本变更四份文档与已归档 AI 变更基线;| 项目 | 值 |
|---|---|
| JUnit 版本 | JUnit Jupiter 5(由 spring-boot-starter-test 管理) |
| Mock 框架 | Mockito + AssertJ + Spring Boot Test/ApplicationContextRunner |
| 协议测试 | Spring MockMvc/WebTestClient 或 MCP SDK 客户端,最终由 Task 1 选择与 WebMVC transport 匹配的最小工具 |
| 架构/静态检查 | Maven dependency tree、ApplicationContext Bean 断言、rg、必要时 ArchUnit/jdeps |
| 已有测试数量 | AI 插件 84;本变更新增 Capability 9、MCP 8(均为最终实际执行数) |
| 已有测试风格 | JUnit 5 + Mockito,AI 插件已有 Service/Client/路由治理单测 |
| 场景 | 输入/配置 | 预期结果 |
|---|---|---|
| Streamable 装配 | protocol=STREAMABLE, WebMVC starter |
只有 WebMvcStreamableServerTransportProvider |
| 默认值防护 | 不显式提供 protocol | 配置约束测试失败,禁止依赖默认 SSE |
| initialize | 认证上下文 + /mcp |
协商 SDK 支持协议并返回 forge-ai-hub server info |
| tools/list | 初始化后的有效会话 | 只返回 capability.ping,名称和 Schema 正确 |
| tools/call | capability.ping, {} |
success,requestId 关联,status=ok |
| 非法输入 | ping 传额外字段 | INVALID_ARGUMENT/isError,不执行 Executor 副作用 |
| 未知工具 | 未注册 toolName | 安全 NOT_FOUND,不泄露内部列表或类名 |
| 无 SSE transport | 完整 Context | 无 SSE provider/route/config Bean,无 /sse 双端点 |
| 版本失败关闭 | 版本冲突或无 Streamable provider | 构建/启动失败,不回退 SSE |
| 容器共存 | Admin + Undertow + WebMVC + 现有 WebFlux 依赖 | 主应用类型不变,现有流式接口装配不被覆盖 |
| 方法/组件 | 场景 | 输入 | 预期结果 |
|---|---|---|---|
CapabilityToolNameMapper |
合法 | capability.ping |
恒等映射且多次结果相同 |
| 同上 | 非法字符/大写/空分段/数字开头 | 多组非法 code | 定义异常,带稳定错误码 |
| 同上 | 超长 | 129 字符 | 拒绝,不截断、不哈希 |
| Registry | 重复键 | 相同 code+version 两次 | 启动失败,不覆盖 |
| Registry | 名称冲突 | 不同定义同 toolName | 启动失败 |
| Registry | 缺 Executor | 定义存在、无 supports | 启动失败 |
| Registry | 多 Source 顺序变化 | 相同定义集合不同加载顺序 | 最终快照和列表顺序一致 |
| 场景 | Schema 特征 | 预期结果 |
|---|---|---|
| 必填字段 | required |
缺字段拒绝 |
| 封闭对象 | additionalProperties=false |
额外字段拒绝 |
| enum | 合法/非法值 | 合法通过、非法拒绝 |
| 嵌套对象 | object 内 object | 路径和校验正确 |
| 数组 | array/items | 每个元素校验 |
| nullable | type:[string,null] |
公共投影支持则保留;不支持则注册失败,不删 null 语义 |
| 不支持关键字 | $ref/条件/动态关键字 |
SCHEMA_UNSUPPORTED,报告安全 JSON path |
| 输出 Schema | Executor 返回非法结构 | INTERNAL/OUTPUT_SCHEMA_INVALID,不把非法 data 发给客户端 |
| 敏感数据 | 非法 arguments 含 secret | 日志不打印完整参数和值 |
| 场景 | 输入 | 预期结果 |
|---|---|---|
| 缺身份 | 无可信 principal | 401/UNAUTHENTICATED,Registry 不执行 |
| 缺 Resolver | MCP enabled,生产 Context 无 Resolver | 启动失败或端点保持禁用 |
| 裸 Header 伪造 | X-Tenant-Id/X-User-Id 无已验证主体 |
不构造 caller,失败关闭 |
| arguments 伪造 | tenantId/userId/activeOrgId/machineClientId | 因封闭 Schema 拒绝,caller 不变 |
| 跨租户 | caller tenant 与定义授权不匹配 | 不可发现、不可调用 |
| 跨组织 | activeOrgId 不允许 | 不可发现、不可调用 |
| scope 不足 | discover 可见、invoke 不允许 | list 可按策略处理,call 必须 FORBIDDEN |
| 安全日志 | 任意失败 | 仅 requestId、安全主体标识、code/errorCode/耗时,无 Token/Header/arguments/data |
Capability 内核必须完整覆盖:
| 场景 | 预期结果 |
|---|---|
| 不同 caller | 只返回各自 canDiscover 的定义 |
| pageSize=1 | 返回一项和稳定 nextCursor |
| 使用 nextCursor | 无重复、无遗漏,顺序稳定 |
| 非法/过期游标 | 失败关闭,不从头静默重放 |
| 注册顺序不同 | 同一快照的分页结果一致 |
MCP 端到端按客户端动态裁剪与 nextCursor 是 Spike 断言:
nextCursor=null,必须有失败关闭测试阻止注册第二个差异授权工具,并把“仅统一授权 ping”写入执行日志;| 检查 | 预期结果 |
|---|---|
| DataSource/Mapper/Redis | Executor 无依赖、无调用 |
| WebClient/HTTP | 无依赖、无网络 |
| ChatModel/ChatClient | 无依赖、无模型费用 |
| 文件/进程 | 无读写、无 Shell |
| 用户回显 | 不接受任意文本,不把客户端内容带回响应 |
| 时间 | 使用 Clock,可确定性断言 |
forge-plugin-capability 编译依赖不出现 Spring AI、MCP SDK、forge-plugin-ai;forge-plugin-mcp 只依赖 capability,不反向形成循环;spring-ai-starter-mcp-server-webmvc:1.1.2 与 mcp-spring-webmvc:0.17.0 仅出现一个解析版本;/mcp;forge-plugin-ai 而出现 MCP endpoint;extraConfig.mcpTools 值只读标记预留,不被页面加载/保存静默删除;enabled=false,显式 protocol=STREAMABLE;git diff --check,回填命令、警告、跳过项、服务清理和 Review 结论。| 时间 | 范围 | 命令 | 结果 | 备注 |
|---|---|---|---|---|
| 2026-07-11 | Proposal 文档静态检查 | 对四份未跟踪文档逐一执行 git diff --no-index --check /dev/null <file> 并检查末尾换行/尾随空格 |
通过 | 不代表业务代码测试 |
| 2026-07-11 | Maven 坐标研究 | mvn dependency:get -Dartifact=org.springframework.ai:spring-ai-starter-mcp-server-webmvc:1.1.2 等三个坐标 |
三种 server starter 均可解析 | 只写本地 Maven 缓存 |
| 2026-07-11 | MCP SDK 结构研究 | javap/jar tf |
WebMVC Streamable provider 存在;SDK tools/list 静态且 cursor 为 null | 不是 Forge Runtime 装配测试 |
| 时间 | 变更范围 | 必跑项 | 实际命令 | 结果 | 跳过/警告 |
|---|---|---|---|---|---|
| Proposal | 仅四份变更文档 | 文件存在、状态、范围、SSE 禁令、路径、diff check | 见 execution-log.md |
通过 | 不编译、不启动服务 |
| 2026-07-11 Apply Task 1 | MCP transport Spike | Context、initialize、dependency tree、无 SSE | mvn -Penable-tests -pl .../forge-plugin-mcp -am test、dependency:tree |
通过:MCP 8、Capability 9,0 失败;协议 2025-06-18 |
上游无配置默认 SSE,Forge Admin 必须保留显式 enabled=false 与 protocol=STREAMABLE |
| 2026-07-11 Apply Task 2–8 | Capability/MCP 实现 | P0/P1/P2 与回归 | AI 84 测试、Admin 37 模块 package、目标 ESLint、pnpm build、静态扫描、git diff --check |
全部通过 | 前端存在仓库既有构建警告;未启动真实服务,未访问外部系统 |
| 2026-07-11 Review Fix | 协议、身份、授权、游标、Schema、审计与 P0 回归 | Java 17 Capability/MCP 完整测试、AI 回归、Admin 聚合、静态安全扫描、diff check | mvn -Penable-tests -pl .../forge-plugin-mcp -am test;mvn -Penable-tests -pl .../forge-plugin-ai test;mvn -pl forge-admin-server -am package -DskipTests |
PASS:Capability 13、MCP 13、AI 84,均 0 failure/error/skip;Admin 37/37 SUCCESS | 前端未修改,复用 Apply 阶段目标 ESLint 与生产 build 成功基线;未启动真实服务 |
execution-log.md:必须包含每次 Red/Green 的命令、Tests run、失败原因和修复后输出;