From 2116e54e2986aa73f7e87a865deaf67339f47a64 Mon Sep 17 00:00:00 2001 From: lanrtop Date: Sat, 4 Jul 2026 13:29:40 +0900 Subject: [PATCH] =?UTF-8?q?feat(mcp-inject):=20=E6=B3=A8=E5=85=A5=E6=97=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=B3=A8=E5=86=8C=E5=85=A8=E5=B1=80=20~/.cla?= =?UTF-8?q?ude.json=20+=20liangjing.json=20=E8=A1=A5=20group=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - inject_project_mcp 末尾新增 try_register_globally 调用,通过 find_claude_bin 自动定位 claude CLI 并执行 claude mcp add -s user, 让新接入项目的产品组 MCP 立即对 Claude Code 可用 - write_liangjing_context 增加 group_id 参数,子项目读取后可直接 构造 lian-jing-{group_id} ToolSearch 查询,无需额外推断 - try_register_globally 为 best-effort:CLI 未找到或调用失败时 仅写日志,不影响 settings.json 注入结果 --- src-tauri/src/mcp_inject.rs | 114 ++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/mcp_inject.rs b/src-tauri/src/mcp_inject.rs index 0e0ad56..7662910 100644 --- a/src-tauri/src/mcp_inject.rs +++ b/src-tauri/src/mcp_inject.rs @@ -47,19 +47,120 @@ fn liangjing_path(project_id: &str) -> Result { Ok(project_root(project_id)?.join(".claude").join("liangjing.json")) } -/// 写炼境上下文文件,供子项目 Claude 会话直接读取 project_id(不通过遍历 list_group_projects 匹配) -fn write_liangjing_context(project_id: &str, port: u16) -> Result<(), String> { +/// 写炼境上下文文件,供子项目 Claude 会话直接读取 project_id / group_id +fn write_liangjing_context(project_id: &str, group_id: &str, port: u16) -> Result<(), String> { let path = liangjing_path(project_id)?; if let Some(parent) = path.parent() { std::fs::create_dir_all(parent) .map_err(|e| format!("创建 .claude 目录失败: {}", e))?; } - let ctx = json!({ "project_id": project_id, "mcp_port": port }); + let ctx = json!({ + "project_id": project_id, + "group_id": group_id, + "mcp_port": port + }); let content = serde_json::to_string_pretty(&ctx).map_err(|e| e.to_string())?; std::fs::write(&path, content) .map_err(|e| format!("写入 liangjing.json 失败: {}", e)) } +/// 查找 claude CLI 路径(优先环境变量 CLAUDE_BIN,然后常用安装目录,最后 where 命令) +fn find_claude_bin() -> Option { + if let Ok(path) = std::env::var("CLAUDE_BIN") { + if std::path::Path::new(&path).exists() { + return Some(path); + } + } + + let home = std::env::var("USERPROFILE") + .or_else(|_| std::env::var("HOME")) + .unwrap_or_default(); + + let candidates = [ + format!(r"{}\.local\bin\claude.exe", home), + format!(r"{}\AppData\Local\AnthropicClaude\claude.exe", home), + r"C:\Program Files\AnthropicClaude\claude.exe".to_string(), + ]; + + for c in &candidates { + if std::path::Path::new(c).exists() { + return Some(c.clone()); + } + } + + // 最后尝试 where 命令(GUI 进程 PATH 可能不含用户 bin,but worth trying) + if let Ok(out) = std::process::Command::new("where").arg("claude.exe").output() { + if out.status.success() { + if let Ok(stdout) = String::from_utf8(out.stdout) { + if let Some(line) = stdout.lines().next() { + return Some(line.trim().to_string()); + } + } + } + } + + None +} + +/// 将产品组 MCP 注册到 Claude 全局配置(~/.claude.json),best-effort,失败仅记录日志 +fn try_register_globally(group_id: &str, port: u16) { + let server_name = format!("lian-jing-{}", group_id); + let server_url = format!("http://127.0.0.1:{}/mcp/group/{}", port, group_id); + + let Some(claude_bin) = find_claude_bin() else { + db::log_event( + "mcp_inject", + "warn", + "claude CLI 未找到,跳过全局 Mcp 注册", + None, + ); + return; + }; + + let git_bash = std::env::var("CLAUDE_CODE_GIT_BASH_PATH").unwrap_or_else(|_| { + r"C:\Program Files\Git\bin\bash.exe".to_string() + }); + + match std::process::Command::new(&claude_bin) + .args([ + "mcp", + "add", + "--transport", + "sse", + "-s", + "user", + &server_name, + &server_url, + ]) + .env("CLAUDE_CODE_GIT_BASH_PATH", &git_bash) + .output() + { + Ok(out) if out.status.success() => { + db::log_event( + "mcp_inject", + "info", + "全局 MCP 注册成功", + Some(&server_name), + ); + } + Ok(out) => { + let msg = format!( + "claude mcp add 非零退出: {}", + String::from_utf8_lossy(&out.stderr) + ); + db::log_event("mcp_inject", "warn", &msg, Some(&server_name)); + } + Err(e) => { + db::log_event( + "mcp_inject", + "warn", + &format!("执行 claude mcp add 失败: {}", e), + Some(&server_name), + ); + } + } +} + // ── JSON 读写 ───────────────────────────────────────────────────────────────── fn read_json(path: &PathBuf) -> Result { @@ -118,8 +219,11 @@ pub fn inject_project_mcp(project_id: &str, group_id: &str) -> Result<(), String log_event("mcp_inject", "info", "MCP 配置注入成功", Some(&ctx)); })?; - // 写炼境上下文,让子项目 Claude 会话无需遍历即可知道自己的 project_id - let _ = write_liangjing_context(project_id, port); + // 写炼境上下文(含 group_id),让子项目 Claude 会话无需遍历即可知道 project_id + group_id + let _ = write_liangjing_context(project_id, group_id, port); + + // 同步注册到 ~/.claude.json,让 Claude Code 能加载 MCP 工具(best-effort) + try_register_globally(group_id, port); Ok(()) }