From 8c8423e0a416d4ed0d1cee65efdd727079d600d4 Mon Sep 17 00:00:00 2001 From: lanrtop Date: Fri, 3 Jul 2026 11:29:28 +0900 Subject: [PATCH] =?UTF-8?q?feat(mcp):=20=E6=B2=BB=E7=90=86=E4=B8=89?= =?UTF-8?q?=E4=BB=B6=E5=A5=97=E7=9A=84=20agent=20=E6=8A=95=E5=BD=B1?= =?UTF-8?q?=E2=80=94=E2=80=94=E8=83=BD=E5=8A=9B=E5=8F=8C=E6=8A=95=E5=BD=B1?= =?UTF-8?q?=E9=A6=96=E6=AC=A1=E5=AE=9E=E8=B7=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_blueprint_status / generate_blueprint_prompt / sync_blueprint_rules 注册为 MCP 工具,与 UI 治理按钮共享同一后端函数(统一验证路径空间)。 工具描述声明对应 UI 按钮与使用约束。agent 从此可走正规流程完成 "拿提示词 → 子项目执行 → 验证蓝图"闭环,不再绕道。 Rules-Applied: R01, R10 --- .blueprint/modules/mcp-server.md | 10 ++++- src-tauri/src/mcp_server.rs | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/.blueprint/modules/mcp-server.md b/.blueprint/modules/mcp-server.md index 1cb84af..18a78bc 100644 --- a/.blueprint/modules/mcp-server.md +++ b/.blueprint/modules/mcp-server.md @@ -40,7 +40,15 @@ - files: src-tauri/src/db.rs, src-tauri/src/mcp_server.rs - acceptance: settings 表默认写入 mcp_port=27190,启动时读取该值绑定端口(SettingsPage UI 见 product-group-mcp 模块) -### 💭 能力双投影:UI 治理按钮 ↔ MCP 工具对齐(R12 候选) +### ✅ P1. 治理三件套的 agent 投影(get_blueprint_status / generate_blueprint_prompt / sync_blueprint_rules) +- status: done +- complexity: M +- files: src-tauri/src/mcp_server.rs +- acceptance: 三个治理动作注册为 MCP 工具,与 UI 按钮共享同一后端函数(等价性=同一代码路径);工具描述声明对应的 UI 按钮与使用约束(边界表达);agent 可走完整验证闭环:generate_blueprint_prompt 拿提示词 → 子项目执行 → get_blueprint_status/get_project_blueprint 验证 + +> 完成记录(2026-07-03):能力双投影概念的首次实践。cargo test 全绿;实测验证由 agent 通过 MCP 端点亲自按下这三颗按钮完成。 + +### 💭 能力双投影:后续扩展(R12 规则化 + 更多按钮) - status: concept - notes: 2026-07-03 概念探讨成形(见 memory concept_dual_projection)。现状:治理动作(生成蓝图提示词/同步规则/接入包/腐化扫描等)只有 UI 投影无 agent 投影,agent 验证需绕道。方向:① 为治理 command 补注册 MCP 工具,实现"每个 UI 按钮有对应 agent 按钮";② 落成 R12 规则进 CONVENTIONS(与 R01 同构);③ 三条件检查清单(边界单点实现/投影完整/反馈对称)纳入健康诊断。收益:统一验证路径空间——功能修复后 agent 走正规流程分钟级回归验证。 diff --git a/src-tauri/src/mcp_server.rs b/src-tauri/src/mcp_server.rs index ae55852..08f591a 100644 --- a/src-tauri/src/mcp_server.rs +++ b/src-tauri/src/mcp_server.rs @@ -408,6 +408,53 @@ fn tools_list_result() -> Value { "required": ["project_id", "content"] } }, + { + "name": "get_blueprint_status", + "description": "检测项目蓝图治理状态:CONVENTIONS 规则版本是否最新、蓝图内容是否可能落后于代码(synced/rules_outdated/content_stale/none)。等价于炼境 UI 项目卡片上的蓝图状态徽章。", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "项目 workspace ID(来自 list_group_projects 的返回结果)" + } + }, + "required": ["project_id"] + } + }, + { + "name": "generate_blueprint_prompt", + "description": "生成蓝图初始化/同步提示词(含 CONVENTIONS 规范与 manifest 严格格式约束)。等价于炼境 UI 治理面板的「初始化蓝图/更新蓝图描绘」按钮。拿到提示词后在该项目目录下执行它(或交给该项目的 AI 会话),完成后可用 get_blueprint_status / get_project_blueprint 验证结果。", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "项目 workspace ID(来自 list_group_projects 的返回结果)" + }, + "mode": { + "type": "string", + "enum": ["init", "sync"], + "description": "init=项目尚无蓝图时从零初始化;sync=已有蓝图,对照代码现状同步更新" + } + }, + "required": ["project_id", "mode"] + } + }, + { + "name": "sync_blueprint_rules", + "description": "将最新 CONVENTIONS 规则同步到项目(写入 .blueprint/CONVENTIONS.md + 更新 AGENTS.md/CLAUDE.md 的蓝图管理区块)。等价于炼境 UI 治理面板的「同步规则」按钮。仅当 get_blueprint_status 返回 rules_outdated 时需要调用。", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "项目 workspace ID(来自 list_group_projects 的返回结果)" + } + }, + "required": ["project_id"] + } + }, { "name": "get_project_snapshots", "description": "获取项目的历史健康快照(每日一条),包含 git 提交间隔、蓝图完成率、阻塞任务数、错误情况。用于分析趋势、识别高价值改进变量。", @@ -451,6 +498,34 @@ async fn tools_call(group_id: &str, params: Option<&Value>) -> Value { let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or(""); get_project_blueprint(&gid, pid) } + "get_blueprint_status" => { + let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or(""); + resolve_project_root(&gid, pid).and_then(|root| { + crate::commands::blueprint::get_blueprint_status(root.to_string_lossy().to_string()) + .and_then(|s| serde_json::to_string_pretty(&s).map_err(|e| e.to_string())) + }) + } + "generate_blueprint_prompt" => { + let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or(""); + let mode = args.get("mode").and_then(|v| v.as_str()).unwrap_or("sync"); + if mode != "init" && mode != "sync" { + Err(format!("mode 只允许 init 或 sync,收到: {mode}")) + } else { + resolve_project_root(&gid, pid).and_then(|root| { + crate::commands::blueprint::generate_blueprint_prompt( + root.to_string_lossy().to_string(), + mode.to_string(), + ) + }) + } + } + "sync_blueprint_rules" => { + let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or(""); + resolve_project_root(&gid, pid).and_then(|root| { + crate::commands::blueprint::sync_blueprint_rules(root.to_string_lossy().to_string()) + .and_then(|r| serde_json::to_string_pretty(&r).map_err(|e| e.to_string())) + }) + } "get_project_file" => { let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or(""); let rel = args