use std::collections::HashMap;
const PATH_SEP: char = if cfg!(windows) { ';' } else { ':' };
const MIRROR_ENV_KEYS: &[&str] = &[
"npm_config_registry",
"UV_INDEX_URL",
"UV_EXTRA_INDEX_URL",
"UV_INSECURE_HOST",
"PIP_INDEX_URL",
];
pub fn format_path_summary(max_segments: usize) -> String {
match std::env::var("PATH") {
Ok(path) => {
let segments: Vec<&str> = path.split(PATH_SEP).collect();
let preview: String = segments
.iter()
.take(max_segments)
.copied()
.collect::<Vec<_>>()
.join(&PATH_SEP.to_string());
if segments.len() > max_segments {
format!("{} ... ({} entries total)", preview, segments.len())
} else {
preview
}
}
Err(_) => "(unset)".to_string(),
}
}
pub fn collect_mirror_env_vars() -> Vec<(&'static str, String)> {
MIRROR_ENV_KEYS
.iter()
.filter_map(|&key| std::env::var(key).ok().map(|val| (key, val)))
.collect()
}
pub fn format_spawn_error(
mcp_id: &str,
command: &str,
args: &Option<Vec<String>>,
inner: impl std::fmt::Display,
) -> String {
let path_val = std::env::var("PATH").unwrap_or_else(|_| "(unset)".to_string());
format!(
"Failed to spawn child process - MCP ID: {}, command: {}, \
args: {:?}, PATH: {}, error: {}",
mcp_id,
command,
args.as_ref().unwrap_or(&Vec::new()),
path_val,
inner
)
}
pub fn log_stdio_spawn_context(tag: &str, mcp_id: &str, env: &Option<HashMap<String, String>>) {
tracing::debug!(
"[{}] MCP ID: {}, PATH: {}",
tag,
mcp_id,
format_path_summary(3),
);
for (key, val) in collect_mirror_env_vars() {
tracing::debug!("[{}] MCP ID: {}, {}={}", tag, mcp_id, key, val);
}
if let Some(env_vars) = env {
let keys: Vec<&String> = env_vars.keys().collect();
tracing::debug!("[{}] MCP ID: {}, config env keys: {:?}", tag, mcp_id, keys);
}
}
pub fn eprint_env_summary() {
eprintln!(" - PATH: {}", format_path_summary(3));
for (key, val) in collect_mirror_env_vars() {
eprintln!(" - {}={}", key, val);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_path_summary_not_empty() {
let summary = format_path_summary(3);
assert!(!summary.is_empty());
}
#[test]
fn test_collect_mirror_env_vars() {
let _ = collect_mirror_env_vars();
}
#[test]
fn test_format_spawn_error() {
let msg = format_spawn_error(
"test-id",
"npx",
&Some(vec!["-y".into(), "server".into()]),
"file not found",
);
assert!(msg.contains("test-id"));
assert!(msg.contains("npx"));
assert!(msg.contains("file not found"));
}
#[test]
fn test_log_stdio_spawn_context_no_panic() {
let mut env = HashMap::new();
env.insert("FOO".to_string(), "bar".to_string());
log_stdio_spawn_context("Test", "test-id", &Some(env));
log_stdio_spawn_context("Test", "test-id", &None);
}
}