#![cfg(feature = "mcp")]
use rmcp::ServiceExt;
use rmcp::model::CallToolRequestParams;
use serde_json::json;
#[tokio::test]
async fn initialize_list_and_call_over_jsonrpc() {
let db = std::env::temp_dir().join(format!("llmtrim_mcp_proto_{}.db", std::process::id()));
let (server_transport, client_transport) = tokio::io::duplex(8192);
let config = llmtrim_core::config::DenseConfig::preset("auto").expect("built-in preset");
let server = tokio::spawn(async move {
let service = llmtrim::mcp::test_server(db, config)
.serve(server_transport)
.await
.expect("server handshake");
service.waiting().await.expect("server run");
});
let client = ().serve(client_transport).await.expect("client initialize");
let tools = client.list_all_tools().await.expect("tools/list");
let mut names: Vec<&str> = tools.iter().map(|t| t.name.as_ref()).collect();
names.sort_unstable();
assert_eq!(
names,
["llmtrim_compress", "llmtrim_compress_text", "llmtrim_stats"]
);
assert!(
tools.iter().all(|t| !t.input_schema.is_empty()),
"every tool advertises an input schema"
);
let schema = |name: &str| {
let t = tools.iter().find(|t| t.name == name).unwrap();
t.input_schema["properties"].clone()
};
assert!(schema("llmtrim_compress")["request"].is_object());
assert!(schema("llmtrim_compress")["provider"].is_object());
assert!(schema("llmtrim_compress_text")["text"].is_object());
async fn call(
client: &rmcp::service::RunningService<rmcp::RoleClient, ()>,
name: &'static str,
args: serde_json::Map<String, serde_json::Value>,
) -> serde_json::Value {
let mut params = CallToolRequestParams::new(name);
params.arguments = Some(args);
let result = client.call_tool(params).await.expect("tools/call");
assert_ne!(result.is_error, Some(true), "{name} must not error");
let text = result
.content
.first()
.and_then(|c| c.as_text())
.map(|t| t.text.clone())
.expect("text content");
serde_json::from_str(&text).expect("tool result is JSON")
}
let request_body = json!({
"model": "gpt-4o",
"messages": [
{ "role": "system", "content": "You are a helpful assistant. " },
{ "role": "user", "content": "Hello world\n\n\nwith redundant whitespace." }
]
})
.to_string();
let mut args = serde_json::Map::new();
args.insert("request".into(), json!(request_body));
let payload = call(&client, "llmtrim_compress", args).await;
assert!(payload["request_json"].is_string());
assert!(payload["input_tokens_before"].as_u64().unwrap() > 0);
assert!(payload["stages"].as_array().is_some_and(|s| !s.is_empty()));
assert_eq!(payload["provider"], "openai");
let mut args = serde_json::Map::new();
args.insert(
"text".into(),
json!("repeat me\nrepeat me\ntail words here"),
);
let payload = call(&client, "llmtrim_compress_text", args).await;
assert!(payload["text"].is_string());
assert!(payload["input_tokens_before"].as_u64().unwrap() > 0);
assert!(payload["tokens_saved"].as_i64().is_some());
let payload = call(&client, "llmtrim_stats", serde_json::Map::new()).await;
assert!(payload["requests"].as_u64().is_some());
assert!(payload["by_model"].is_array());
client.cancel().await.ok();
server.abort();
}