use rmcp::{
ErrorData as McpError, ServerHandler, ServiceExt,
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::*,
tool, tool_handler, tool_router,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, schemars::JsonSchema)]
struct EchoParams {
message: String,
}
#[derive(Clone)]
struct EchoServer {
tool_router: ToolRouter<EchoServer>,
}
impl EchoServer {
fn new() -> Self {
Self {
tool_router: Self::tool_router(),
}
}
}
#[tool_router]
impl EchoServer {
#[tool(description = "Echoes back the input message")]
async fn echo(
&self,
Parameters(params): Parameters<EchoParams>,
) -> Result<CallToolResult, McpError> {
Ok(CallToolResult::success(vec![Content::text(format!(
"Echo: {}",
params.message
))]))
}
}
#[tool_handler]
impl ServerHandler for EchoServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(Implementation::new("mcp-echo-server", "1.0.0"))
.with_protocol_version(ProtocolVersion::V_2024_11_05)
.with_instructions("A simple MCP server with an echo tool for testing")
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
tracing::info!("Starting MCP echo server");
let service = EchoServer::new().serve(rmcp::transport::stdio()).await?;
service.waiting().await?;
Ok(())
}