use noyalib_mcp::{HandleOutcome, handle_message};
use std::fs;
fn send(label: &str, frame: &str) -> Option<String> {
println!("\n── {label} ──");
println!(" -> {frame}");
match handle_message(frame) {
HandleOutcome::Reply(payload) => {
let shown = if payload.len() > 200 {
format!("{}… ({} bytes)", &payload[..200], payload.len())
} else {
payload.clone()
};
println!(" <- {shown}");
Some(payload)
}
HandleOutcome::Silent => {
println!(" <- (silent — notification, per JSON-RPC spec)");
None
}
}
}
fn main() {
println!("noyalib-mcp — a full session, request by request");
let original = "\
# deploy config — DO NOT reformat by hand
# owner: platform-team
server:
host: 0.0.0.0
port: 8080 # bumped for the load test
features:
- tracing
- metrics
";
let path =
std::env::temp_dir().join(format!("noyalib-mcp-example-{}.yaml", std::process::id()));
fs::write(&path, original).expect("write fixture");
let file = path.to_string_lossy().to_string();
let _ = send(
"initialize",
r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}"#,
);
let _ = send(
"tools/list",
r#"{"jsonrpc":"2.0","id":2,"method":"tools/list"}"#,
);
let _ = send(
"tools/call noyalib_get — read server.port",
&format!(
r#"{{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{{"name":"noyalib_get","arguments":{{"file":"{file}","path":"server.port"}}}}}}"#
),
);
let _ = send(
"tools/call noyalib_get — read features[0]",
&format!(
r#"{{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{{"name":"noyalib_get","arguments":{{"file":"{file}","path":"features[0]"}}}}}}"#
),
);
let _ = send(
"tools/call noyalib_set — set server.port to 9090",
&format!(
r#"{{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{{"name":"noyalib_set","arguments":{{"file":"{file}","path":"server.port","value":"9090"}}}}}}"#
),
);
println!("\n── the file on disk, after noyalib_set ──");
let after = fs::read_to_string(&path).expect("read back");
for line in after.lines() {
println!(" | {line}");
}
assert!(
after.contains("# deploy config — DO NOT reformat by hand"),
"header comment must survive"
);
assert!(
after.contains("# bumped for the load test"),
"inline comment on the *edited line* must survive"
);
assert!(after.contains("port: 9090"), "the edit must be applied");
println!("\n ✓ header, inline comment, blank lines and spacing all preserved");
println!("\n── error cases a client must handle ──");
let _ = send(
"unknown tool",
r#"{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"frobnicate","arguments":{}}}"#,
);
let _ = send(
"missing required argument",
&format!(
r#"{{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{{"name":"noyalib_get","arguments":{{"file":"{file}"}}}}}}"#
),
);
let _ = send(
"path that does not exist in the document",
&format!(
r#"{{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{{"name":"noyalib_get","arguments":{{"file":"{file}","path":"server.nope"}}}}}}"#
),
);
let _ = send(
"unknown method",
r#"{"jsonrpc":"2.0","id":9,"method":"no/such/method"}"#,
);
let _ = send("malformed JSON frame", r#"{not json at all"#);
let _ = send(
"wrong jsonrpc version",
r#"{"jsonrpc":"1.0","id":10,"method":"initialize"}"#,
);
let _ = send(
"notification (no id) — must be silent",
r#"{"jsonrpc":"2.0","method":"notifications/initialized"}"#,
);
let _ = fs::remove_file(&path);
println!("\nSession complete.");
}