use std::path::PathBuf;
use std::process::Command;
fn build_and_locate_example() -> PathBuf {
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
let output = Command::new(&cargo)
.args(["build", "--example", "make-mcp", "--message-format=json"])
.output()
.expect("invoke cargo build --example make-mcp");
assert!(
output.status.success(),
"cargo build --example make-mcp failed: {}",
String::from_utf8_lossy(&output.stderr)
);
for line in output.stdout.split(|&b| b == b'\n') {
if line.is_empty() {
continue;
}
let Ok(msg) = serde_json::from_slice::<serde_json::Value>(line) else {
continue;
};
if msg.get("reason").and_then(|v| v.as_str()) != Some("compiler-artifact") {
continue;
}
let target = msg.get("target");
let kinds = target
.and_then(|t| t.get("kind"))
.and_then(|v| v.as_array());
let name = target.and_then(|t| t.get("name")).and_then(|v| v.as_str());
let is_make_mcp_example = kinds
.is_some_and(|ks| ks.iter().any(|k| k.as_str() == Some("example")))
&& name == Some("make-mcp");
if !is_make_mcp_example {
continue;
}
if let Some(exe) = msg.get("executable").and_then(|v| v.as_str()) {
return PathBuf::from(exe);
}
}
panic!(
"cargo build did not report an executable for the make-mcp example; \
stdout={}",
String::from_utf8_lossy(&output.stdout)
);
}
#[test]
fn mcp_tools_emits_build_tool_with_required_directory_flag() {
let exe = build_and_locate_example();
let workdir = tempfile::tempdir().expect("create tmpdir");
let output = Command::new(&exe)
.args(["mcp", "tools"])
.current_dir(workdir.path())
.output()
.expect("invoke make-mcp mcp tools");
assert!(
output.status.success(),
"`mcp tools` exited non-zero: stdout={} stderr={}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let tools_path = workdir.path().join("mcp-tools.json");
let raw = std::fs::read(&tools_path)
.unwrap_or_else(|e| panic!("expected mcp-tools.json at {}: {e}", tools_path.display()));
let tools: serde_json::Value =
serde_json::from_slice(&raw).expect("mcp-tools.json must be valid JSON");
let tools = tools
.as_array()
.expect("mcp-tools.json must be a JSON array of tool descriptors");
let build_tool = tools
.iter()
.find(|t| t.get("name").and_then(|n| n.as_str()) == Some("make-mcp_build"))
.unwrap_or_else(|| {
let names: Vec<&str> = tools
.iter()
.filter_map(|t| t.get("name").and_then(|n| n.as_str()))
.collect();
panic!("expected a tool named `make-mcp_build`, got names: {names:?}");
});
let required = build_tool
.pointer("/inputSchema/properties/flags/required")
.and_then(|v| v.as_array())
.expect("inputSchema.properties.flags.required must be an array");
let names: Vec<&str> = required.iter().filter_map(|v| v.as_str()).collect();
assert!(
names.contains(&"directory"),
"`directory` must appear in the required flag list for make-mcp_build; got {names:?}"
);
}