use agent_client_protocol::{Proxy, mcp_server::McpServer};
use agent_client_protocol_rmcp::McpServerExt;
use rmcp::{
ErrorData as McpError, ServerHandler,
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::*,
tool, tool_handler, tool_router,
};
use serde::{Deserialize, Serialize};
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
#[derive(Debug, Serialize, Deserialize, schemars::JsonSchema)]
struct EchoParams {
message: String,
}
#[derive(Clone, Debug)]
pub struct ExampleMcpServer {
#[allow(dead_code)]
tool_router: ToolRouter<ExampleMcpServer>,
}
impl ExampleMcpServer {
#[must_use]
pub fn new() -> Self {
Self {
tool_router: Self::tool_router(),
}
}
}
impl Default for ExampleMcpServer {
fn default() -> Self {
Self::new()
}
}
#[tool_router]
impl ExampleMcpServer {
#[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 ExampleMcpServer {
fn get_info(&self) -> ServerInfo {
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(Implementation::new("example-mcp-server", "0.1.0"))
.with_protocol_version(ProtocolVersion::V_2024_11_05)
.with_instructions("A simple example MCP server with an echo tool")
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_target(true)
.with_writer(std::io::stderr)
.init();
tracing::info!("MCP server proxy starting");
let mcp_server = McpServer::from_rmcp("example", ExampleMcpServer::new);
Proxy
.builder()
.name("mcp-server-proxy")
.with_mcp_server(mcp_server)
.connect_to(agent_client_protocol::ByteStreams::new(
tokio::io::stdout().compat_write(),
tokio::io::stdin().compat(),
))
.await?;
Ok(())
}