use langchainrust::mcp::{MCPRequest, MCPServer};
use langchainrust::{
Calculator, DateTimeTool, DuckDuckGoSearchTool, SimpleMathTool, URLFetchTool, WikipediaTool,
};
use std::sync::Arc;
use tokio::net::TcpListener;
fn build_server() -> MCPServer {
MCPServer::new()
.with_server_info("langchainrust-mcp-server", env!("CARGO_PKG_VERSION"))
.with_tool(Arc::new(Calculator))
.with_tool(Arc::new(SimpleMathTool))
.with_tool(Arc::new(DateTimeTool))
.with_tool(Arc::new(URLFetchTool::new()))
.with_tool(Arc::new(WikipediaTool::new()))
.with_tool(Arc::new(DuckDuckGoSearchTool::new()))
}
#[tokio::main]
async fn main() {
let host = std::env::var("MCP_SERVER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
let port: u16 = std::env::var("MCP_SERVER_PORT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8788);
let listener = TcpListener::bind((host.as_str(), port))
.await
.unwrap_or_else(|e| {
eprintln!("failed to bind {host}:{port}: {e}");
std::process::exit(1);
});
let bound = listener.local_addr().unwrap();
let public_base = std::env::var("MCP_SERVER_PUBLIC_URL").unwrap_or_else(|_| {
eprintln!("⚠ MCP_SERVER_PUBLIC_URL is not set; remote clients will be unable to reach the POST address.");
eprintln!(" set it to: http://<server-public-ip-or-domain>:<port>");
format!("http://{bound}")
});
let server = Arc::new(build_server());
let names = registered_tool_names(&server).await;
println!("registered {} tools: {}", names.len(), names.join(", "));
let sse_url = server.serve_sse(listener, public_base);
println!("MCP SSE server started ✅");
println!("client connection endpoint: {sse_url}");
println!("press Ctrl+C to stop.");
std::future::pending::<()>().await;
}
async fn registered_tool_names(server: &MCPServer) -> Vec<String> {
let resp = server
.handle_request(MCPRequest::new(1, "tools/list", None))
.await;
resp.result
.as_ref()
.and_then(|r| r.get("tools"))
.and_then(|t| t.as_array())
.map(|arr| {
arr.iter()
.filter_map(|t| t.get("name").and_then(|n| n.as_str()).map(String::from))
.collect()
})
.unwrap_or_default()
}