use serde_json::{json, Value}; use std::io::{BufRead, BufReader, Write}; use std::process::{Command, Stdio}; fn send_message(stdin: &mut std::process::ChildStdin, msg: Value) { let s = serde_json::to_string(&msg).unwrap(); stdin.write_all(format!("{}\n", s).as_bytes()).unwrap(); stdin.flush().unwrap(); } fn read_message(stdout: &mut std::process::ChildStdout) -> Option { let mut reader = BufReader::new(stdout); let mut line = String::new(); if reader.read_line(&mut line).unwrap_or(0) == 0 { return None; } serde_json::from_str(&line).ok() } #[test] fn test_mcp_initialization_and_tools_list() { let mut nvim_exe = std::env::current_exe().unwrap(); nvim_exe.pop(); nvim_exe.pop(); nvim_exe.push("mcp-memory-win-nvim.exe"); let mut child = Command::new(&nvim_exe) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to start mcp-memory-win-nvim"); let mut stdin = child.stdin.take().expect("Failed to open stdin"); let mut stdout = child.stdout.take().expect("Failed to open stdout"); // 0. Test server/discover (probe) let discover_req = json!({ "jsonrpc": "2.0", "method": "server/discover", "params": {}, "id": 0 }); send_message(&mut stdin, discover_req); let discover_resp = read_message(&mut stdout).expect("Failed to read server/discover response"); assert_eq!(discover_resp["error"]["code"], -32601); // 1. Test Initialize let init_req = json!({ "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "test-client", "version": "1.0" } }, "id": 1 }); send_message(&mut stdin, init_req); let init_resp = read_message(&mut stdout).expect("Failed to read initialize response"); assert_eq!(init_resp["jsonrpc"], "2.0"); assert_eq!(init_resp["id"], 1); // Verify capabilities let capabilities = &init_resp["result"]["capabilities"]; assert_eq!(capabilities["tools"], serde_json::json!({})); // 2. Test tools/list let tools_req = json!({ "jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 2 }); send_message(&mut stdin, tools_req); let tools_resp = read_message(&mut stdout).expect("Failed to read tools/list response"); assert_eq!(tools_resp["jsonrpc"], "2.0"); assert_eq!(tools_resp["id"], 2); let tools = tools_resp["result"]["tools"] .as_array() .expect("result.tools must be an array"); assert!(!tools.is_empty(), "Server must expose at least one tool"); let has_get_active_buffer = tools.iter().any(|t| t["name"] == "nvim_get_active_buffer"); assert!(has_get_active_buffer, "Missing nvim_get_active_buffer tool"); // 3. Test negative scenario: tools/call when Neovim is not running // Since Neovim is not guaranteed to be running on the test agent's system, // calling a Neovim-specific tool should gracefully return a JSON-RPC error. let call_req = json!({ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "nvim_get_active_buffer", "arguments": {} }, "id": 3 }); send_message(&mut stdin, call_req); let call_resp = read_message(&mut stdout).expect("Failed to read tools/call response"); assert_eq!(call_resp["jsonrpc"], "2.0"); assert_eq!(call_resp["id"], 3); assert!( call_resp.get("error").is_some(), "Expected an error response since Neovim shouldn't be running" ); assert_eq!(call_resp["error"]["code"], -32603); // Internal Error child.kill().expect("Failed to kill child"); child.wait().expect("Failed to wait on child"); }