use fastmcp_rs::{
FastMcpServer, ToolDefinition, ToolResponse,
prompt::{PromptMessage, PromptMessageContent, PromptTemplate},
run_stdio_with_io,
};
use serde_json::{Value, json};
use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader};
#[tokio::test]
async fn stdio_transport_processes_commands() {
let server = FastMcpServer::builder().name("stdio").build().into_shared();
server
.register_tool(ToolDefinition::new(
"echo",
|_, payload: Value| async move {
Ok(ToolResponse::new(vec![json!({
"type": "text",
"text": payload.to_string()
})]))
},
))
.unwrap();
server
.register_prompt(PromptTemplate::new(
"welcome",
vec![PromptMessage {
role: "system".into(),
content: PromptMessageContent::Text {
text: "Welcome {{ user }}".into(),
},
}],
))
.unwrap();
let (mut client_writer, server_reader) = io::duplex(4096);
let (server_writer, client_reader) = io::duplex(4096);
let server_task = tokio::spawn({
let server = server.clone();
async move {
run_stdio_with_io(server, BufReader::new(server_reader), server_writer)
.await
.unwrap();
}
});
client_writer
.write_all(b"{\"command\":\"metadata\"}\n")
.await
.unwrap();
client_writer
.write_all(
b"{\"command\":\"call_tool\",\"name\":\"echo\",\"arguments\":{\"value\":\"hello\"}}\n",
)
.await
.unwrap();
client_writer
.write_all(b"{\"command\":\"shutdown\"}\n")
.await
.unwrap();
client_writer.flush().await.unwrap();
let mut reader = BufReader::new(client_reader);
let mut response = String::new();
reader.read_line(&mut response).await.unwrap();
let metadata: Value = serde_json::from_str(response.trim()).unwrap();
assert_eq!(metadata["type"], "metadata");
response.clear();
reader.read_line(&mut response).await.unwrap();
let tool_call: Value = serde_json::from_str(response.trim()).unwrap();
assert_eq!(tool_call["type"], "tool_invocation");
assert_eq!(
tool_call["data"]["content"][0]["text"],
"{\"value\":\"hello\"}"
);
response.clear();
reader.read_line(&mut response).await.unwrap();
let ack: Value = serde_json::from_str(response.trim()).unwrap();
assert_eq!(ack["type"], "ack");
assert_eq!(ack["message"], "shutdown");
server_task.await.unwrap();
}