mod mcp_test_helpers;
use anyhow::Result;
use async_trait::async_trait;
use mcp_test_helpers::init_test_tracing;
use serde_json::{json, Value};
use solidmcp::{
handler::{McpContext, McpHandler, ToolDefinition},
McpServer,
};
use std::sync::Arc;
struct TestMcpHandler;
#[async_trait]
impl McpHandler for TestMcpHandler {
async fn list_tools(&self, _context: &McpContext) -> Result<Vec<ToolDefinition>> {
Ok(vec![ToolDefinition {
name: "test_tool".to_string(),
description: "A test tool for trait verification".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"message": {"type": "string"}
}
}),
}])
}
async fn call_tool(
&self,
name: &str,
arguments: Value,
_context: &McpContext,
) -> Result<Value> {
match name {
"test_tool" => {
let message = arguments["message"].as_str().unwrap_or("default");
Ok(json!({
"response": format!("Processed: {}", message)
}))
}
_ => Err(anyhow::anyhow!("Unknown tool: {}", name)),
}
}
}
#[tokio::test]
async fn test_custom_trait_implementation() -> Result<()> {
init_test_tracing();
let handler = Arc::new(TestMcpHandler);
let mut server = McpServer::with_handler(handler).await?;
let port = mcp_test_helpers::find_available_port()
.await
.map_err(|e| anyhow::anyhow!("{}", e))?;
tokio::spawn(async move {
server.start(port).await.unwrap();
});
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
let client = reqwest::Client::new();
let url = format!("http://127.0.0.1:{port}/mcp");
let tools_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
});
let response = client.post(&url).json(&tools_request).send().await?;
assert_eq!(response.status(), 200);
let tools_response: Value = response.json().await?;
assert_eq!(tools_response["jsonrpc"], "2.0");
assert_eq!(tools_response["id"], 1);
let tools = tools_response["result"]["tools"].as_array().unwrap();
assert_eq!(tools.len(), 1);
assert_eq!(tools[0]["name"], "test_tool");
let tool_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "test_tool",
"arguments": {
"message": "Hello trait!"
}
}
});
let response = client.post(&url).json(&tool_request).send().await?;
assert_eq!(response.status(), 200);
let tool_response: Value = response.json().await?;
assert_eq!(tool_response["jsonrpc"], "2.0");
assert_eq!(tool_response["id"], 2);
assert!(tool_response["result"].is_object());
println!("✅ Custom trait implementation test passed!");
Ok(())
}
#[tokio::test]
async fn test_trait_error_handling() -> Result<()> {
init_test_tracing();
let handler = Arc::new(TestMcpHandler);
let mut server = McpServer::with_handler(handler).await?;
let port = mcp_test_helpers::find_available_port()
.await
.map_err(|e| anyhow::anyhow!("{}", e))?;
tokio::spawn(async move {
server.start(port).await.unwrap();
});
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
let client = reqwest::Client::new();
let url = format!("http://127.0.0.1:{port}/mcp");
let tool_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "unknown_tool",
"arguments": {}
}
});
let response = client.post(&url).json(&tool_request).send().await?;
assert_eq!(response.status(), 200);
let tool_response: Value = response.json().await?;
assert_eq!(tool_response["jsonrpc"], "2.0");
assert_eq!(tool_response["id"], 1);
assert!(tool_response["error"].is_object());
assert!(tool_response["result"].is_null());
println!("✅ Trait error handling test passed!");
Ok(())
}