fix(mcp-inject): git-bash 路径多位置探测——修复 Scoop 安装 Git 时 claude CLI 调用全挂
All checks were successful
Push & PR Check / check (push) Successful in 1m32s
All checks were successful
Push & PR Check / check (push) Successful in 1m32s
迁移实测发现:CLAUDE_CODE_GIT_BASH_PATH 回退路径硬编码 C:\Program Files\Git\bin\bash.exe,Scoop 安装的 Git 不在该位置, 导致 claude mcp add/remove 全部非零退出(存量 bug,组级时代同样存在)。 - find_git_bash():env(校验存在)→ Program Files / Scoop → where git.exe 推导 - try_register_globally 返回 bool;同名 already exists(exit 1)视为成功, 不再每次 inject 刷 warn 日志 - migrate_to_single_server 标记位改以注册成功为条件——CLI 链路不通时 不落标记,下次启动重试(幂等)
This commit is contained in:
parent
01eb941ab9
commit
0d1427f4e9
@ -112,13 +112,47 @@ fn find_claude_bin() -> Option<String> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn claude_git_bash() -> String {
|
/// 查找 git-bash 路径(claude CLI 在 Windows 上必需)。
|
||||||
std::env::var("CLAUDE_CODE_GIT_BASH_PATH")
|
/// 顺序:环境变量(校验存在)→ 常见安装位置(Program Files / Scoop)→ 从 git.exe 位置推导
|
||||||
.unwrap_or_else(|_| r"C:\Program Files\Git\bin\bash.exe".to_string())
|
fn find_git_bash() -> Option<String> {
|
||||||
|
if let Ok(p) = std::env::var("CLAUDE_CODE_GIT_BASH_PATH") {
|
||||||
|
if std::path::Path::new(&p).exists() {
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let home = std::env::var("USERPROFILE").unwrap_or_default();
|
||||||
|
let scoop = std::env::var("SCOOP").unwrap_or_else(|_| format!(r"{}\scoop", home));
|
||||||
|
let candidates = [
|
||||||
|
r"C:\Program Files\Git\bin\bash.exe".to_string(),
|
||||||
|
format!(r"{}\apps\git\current\bin\bash.exe", scoop),
|
||||||
|
];
|
||||||
|
for c in &candidates {
|
||||||
|
if std::path::Path::new(c).exists() {
|
||||||
|
return Some(c.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 git.exe 推导:<root>\cmd\git.exe / <root>\mingw64\bin\git.exe → <root>\bin\bash.exe
|
||||||
|
if let Ok(out) = std::process::Command::new("where").arg("git.exe").output() {
|
||||||
|
if out.status.success() {
|
||||||
|
for line in String::from_utf8_lossy(&out.stdout).lines() {
|
||||||
|
let git = std::path::Path::new(line.trim());
|
||||||
|
if let Some(root) = git.parent().and_then(|d| d.parent()) {
|
||||||
|
let bash = root.join("bin").join("bash.exe");
|
||||||
|
if bash.exists() {
|
||||||
|
return Some(bash.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 将单一全局 lian-jing 服务器注册到 Claude 全局配置(~/.claude.json),best-effort,失败仅记录日志
|
/// 将单一全局 lian-jing 服务器注册到 Claude 全局配置(~/.claude.json)。
|
||||||
fn try_register_globally(port: u16) {
|
/// best-effort,失败仅记录日志;返回是否成功(同名已存在视为成功,作为 CLI 链路健康信号)
|
||||||
|
fn try_register_globally(port: u16) -> bool {
|
||||||
let server_url = format!("http://127.0.0.1:{}/mcp", port);
|
let server_url = format!("http://127.0.0.1:{}/mcp", port);
|
||||||
|
|
||||||
let Some(claude_bin) = find_claude_bin() else {
|
let Some(claude_bin) = find_claude_bin() else {
|
||||||
@ -128,10 +162,17 @@ fn try_register_globally(port: u16) {
|
|||||||
"claude CLI 未找到,跳过全局 MCP 注册",
|
"claude CLI 未找到,跳过全局 MCP 注册",
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
return;
|
return false;
|
||||||
|
};
|
||||||
|
let Some(git_bash) = find_git_bash() else {
|
||||||
|
db::log_event(
|
||||||
|
"mcp_inject",
|
||||||
|
"warn",
|
||||||
|
"git-bash 未找到(claude CLI 依赖),跳过全局 MCP 注册",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
let git_bash = claude_git_bash();
|
|
||||||
|
|
||||||
match std::process::Command::new(&claude_bin)
|
match std::process::Command::new(&claude_bin)
|
||||||
.args([
|
.args([
|
||||||
@ -149,13 +190,25 @@ fn try_register_globally(port: u16) {
|
|||||||
{
|
{
|
||||||
Ok(out) if out.status.success() => {
|
Ok(out) if out.status.success() => {
|
||||||
db::log_event("mcp_inject", "info", "全局 MCP 注册成功", Some(MCP_KEY));
|
db::log_event("mcp_inject", "info", "全局 MCP 注册成功", Some(MCP_KEY));
|
||||||
|
true
|
||||||
}
|
}
|
||||||
Ok(out) => {
|
Ok(out) => {
|
||||||
let msg = format!(
|
let combined = format!(
|
||||||
"claude mcp add 非零退出: {}",
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&out.stdout),
|
||||||
String::from_utf8_lossy(&out.stderr)
|
String::from_utf8_lossy(&out.stderr)
|
||||||
);
|
);
|
||||||
db::log_event("mcp_inject", "warn", &msg, Some(MCP_KEY));
|
// 同名已存在(exit 1)不是故障——CLI 链路健康,注册目标已达成
|
||||||
|
if combined.contains("already exists") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
db::log_event(
|
||||||
|
"mcp_inject",
|
||||||
|
"warn",
|
||||||
|
&format!("claude mcp add 非零退出: {}", combined),
|
||||||
|
Some(MCP_KEY),
|
||||||
|
);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
db::log_event(
|
db::log_event(
|
||||||
@ -164,6 +217,7 @@ fn try_register_globally(port: u16) {
|
|||||||
&format!("执行 claude mcp add 失败: {}", e),
|
&format!("执行 claude mcp add 失败: {}", e),
|
||||||
Some(MCP_KEY),
|
Some(MCP_KEY),
|
||||||
);
|
);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,6 +229,10 @@ pub fn cleanup_legacy_global_registrations() -> Vec<String> {
|
|||||||
db::log_event("mcp_inject", "warn", "claude CLI 未找到,跳过旧注册清理", None);
|
db::log_event("mcp_inject", "warn", "claude CLI 未找到,跳过旧注册清理", None);
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
|
let Some(git_bash) = find_git_bash() else {
|
||||||
|
db::log_event("mcp_inject", "warn", "git-bash 未找到,跳过旧注册清理", None);
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
let Ok(conn) = db::pool().get() else {
|
let Ok(conn) = db::pool().get() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
@ -186,7 +244,6 @@ pub fn cleanup_legacy_global_registrations() -> Vec<String> {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let git_bash = claude_git_bash();
|
|
||||||
let mut removed = Vec::new();
|
let mut removed = Vec::new();
|
||||||
for gid in &group_ids {
|
for gid in &group_ids {
|
||||||
let mut names = vec![format!("lian-jing-{}", gid)];
|
let mut names = vec![format!("lian-jing-{}", gid)];
|
||||||
@ -316,22 +373,25 @@ pub fn migrate_to_single_server() {
|
|||||||
|
|
||||||
let errors = reinject_all_mcp();
|
let errors = reinject_all_mcp();
|
||||||
let removed = cleanup_legacy_global_registrations();
|
let removed = cleanup_legacy_global_registrations();
|
||||||
|
let register_ok = try_register_globally(crate::mcp_server::read_port());
|
||||||
|
|
||||||
let summary = format!(
|
let summary = format!(
|
||||||
"单一服务器迁移完成:重注入失败 {} 个,移除旧全局注册 {} 条{}",
|
"单一服务器迁移完成:重注入失败 {} 个,移除旧全局注册 {} 条,全局注册 {}{}",
|
||||||
errors.len(),
|
errors.len(),
|
||||||
removed.len(),
|
removed.len(),
|
||||||
|
if register_ok { "成功" } else { "失败" },
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} else {
|
||||||
format!(";失败详情: {}", errors.join("; "))
|
format!(";失败详情: {}", errors.join("; "))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let level = if errors.is_empty() { "info" } else { "warn" };
|
let level = if errors.is_empty() && register_ok { "info" } else { "warn" };
|
||||||
db::log_event("mcp_inject", level, &summary, None);
|
db::log_event("mcp_inject", level, &summary, None);
|
||||||
|
|
||||||
// claude CLI 不在时旧全局注册无法清理——不落标记位,等下次启动重试(整个流程幂等)
|
// 全局注册失败说明 claude CLI 链路不通(CLI 缺失/git-bash 缺失/命令报错),
|
||||||
if find_claude_bin().is_none() {
|
// 此时旧注册也清不掉——不落标记位,等下次启动重试(整个流程幂等)
|
||||||
|
if !register_ok {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Ok(conn) = db::pool().get() {
|
if let Ok(conn) = db::pool().get() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user