use std::env;
use std::io::{BufRead, BufReader, Write};
use std::process::{Command, Stdio};
#[test]
fn test_mcp_autocomplete_via_stdio() {
if std::env::var("RUN_MCP_STDIO_TEST").ok().as_deref() != Some("1") {
eprintln!("Skipping: set RUN_MCP_STDIO_TEST=1 to run");
return;
}
unsafe {
env::set_var(
"TERRAPHIM_SETTINGS_PATH",
"../terraphim_settings/default/settings_local_dev.toml",
);
}
let mut command = Command::new("cargo");
command.arg("run");
if std::env::var_os("CI").is_some() {
command.arg("--features").arg("zlob");
}
let mut child = command
.args(["--"])
.current_dir(".")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to start MCP server");
let mut stdin = child.stdin.take().expect("Failed to get stdin");
let stdout = child.stdout.take().expect("Failed to get stdout");
let mut reader = BufReader::new(stdout);
let init_request = serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"clientInfo": {
"name": "MCP Test Client",
"version": "1.0.0"
}
}
});
println!("1. Sending initialization request...");
writeln!(stdin, "{}", init_request).expect("Failed to write to stdin");
stdin.flush().expect("Failed to flush stdin");
let mut response = String::new();
reader
.read_line(&mut response)
.expect("Failed to read response");
println!("Response: {}", response.trim());
let tools_request = serde_json::json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
});
println!("2. Listing available tools...");
writeln!(stdin, "{}", tools_request).expect("Failed to write to stdin");
stdin.flush().expect("Failed to flush stdin");
response.clear();
let mut got_tools = false;
for _ in 0..20 {
response.clear();
if reader.read_line(&mut response).is_err() {
break;
}
if response.trim().is_empty() {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
println!("Tools list response: {}", response.trim());
if let Ok(tools_response) = serde_json::from_str::<serde_json::Value>(&response)
&& tools_response.get("result").is_some()
{
println!("Parsed tools response: {:#?}", tools_response);
got_tools = true;
break;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
assert!(
got_tools,
"Did not receive tools/list response from MCP server"
);
let build_index_request = serde_json::json!({
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "build_autocomplete_index",
"arguments": {
"role": "Terraphim Engineer"
}
}
});
println!("3. Building autocomplete index...");
writeln!(stdin, "{}", build_index_request).expect("Failed to write to stdin");
stdin.flush().expect("Failed to flush stdin");
response.clear();
reader
.read_line(&mut response)
.expect("Failed to read response");
println!("Build index response: {}", response.trim());
assert!(
child
.try_wait()
.expect("Failed to check child status")
.is_none(),
"MCP server exited early"
);
let autocomplete_request = serde_json::json!({
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "autocomplete_with_snippets",
"arguments": {
"query": "terraphim",
"limit": 5,
"role": "Terraphim Engineer"
}
}
});
println!("4. Testing autocomplete with snippets...");
writeln!(stdin, "{}", autocomplete_request).expect("Failed to write to stdin");
stdin.flush().expect("Failed to flush stdin");
response.clear();
reader
.read_line(&mut response)
.expect("Failed to read response");
println!("Autocomplete response: {}", response.trim());
let terms_request = serde_json::json!({
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "autocomplete_terms",
"arguments": {
"query": "terraphim",
"limit": 5,
"role": "Terraphim Engineer"
}
}
});
println!("5. Testing autocomplete terms...");
writeln!(stdin, "{}", terms_request).expect("Failed to write to stdin");
stdin.flush().expect("Failed to flush stdin");
response.clear();
reader
.read_line(&mut response)
.expect("Failed to read response");
println!("Terms response: {}", response.trim());
println!("Test completed successfully!");
child.kill().expect("Failed to kill child process");
child.wait().expect("Failed to wait for child");
}