Model Context Protocol
The Model Context Protocol (MCP) is a standard for exposing tools to LLM applications over a common interface. Rig integrates with MCP through the rmcp crate: connect to an MCP server, fetch its tools, and hand them to an agent with a single builder call.
An MCP server hosts tools and answers invocations; an MCP client discovers those tools and calls them. Rig agents act as clients. You can also build your own server with rmcp to expose custom tools.
Enable the rmcp feature on rig:
cargo add rig -F rmcpAnd add rmcp itself with the client features you need:
cargo add rmcp -F client,macros,transport-streamable-http-client-reqwestUsing an MCP server’s tools in an agent
Section titled “Using an MCP server’s tools in an agent”The flow has three steps: connect a client, list the tools, then pass them to the agent via rmcp_tools. The complete, runnable version lives in the rmcp example.
1. Connect a client
Section titled “1. Connect a client”Build a transport pointing at your server, then serve a ClientInfo over it to obtain a running client:
use rmcp::ServiceExt;use rmcp::model::{ClientCapabilities, ClientInfo, Implementation};use rmcp::transport::StreamableHttpClientTransport;
let transport = StreamableHttpClientTransport::from_uri("http://localhost:8080");
let client_info = ClientInfo { protocol_version: Default::default(), capabilities: ClientCapabilities::default(), client_info: Implementation { name: "rig".to_string(), version: "0.39.0".to_string(), },};
let client = client_info.serve(transport).await.inspect_err(|e| { tracing::error!("client error: {e:?}");})?;2. List the available tools
Section titled “2. List the available tools”use rmcp::model::Tool;
let tools: Vec<Tool> = client.list_tools(Default::default()).await?.tools;3. Add the tools to an agent
Section titled “3. Add the tools to an agent”Pass the tool list and the client’s peer handle to rmcp_tools. Rig wraps each MCP tool so the agent can call it like any native tool:
use rig::providers;
let openai_client = providers::openai::Client::from_env()?;
let agent = openai_client .agent("gpt-5.5") .rmcp_tools(tools, client.peer().to_owned()) .build();
let response = agent.prompt("Add 10 + 10").await?;tracing::info!("Agent response: {response:?}");rmcp_tools(tools, client) takes the Vec<rmcp::model::Tool> you listed and the client’s ServerSink (obtained via client.peer().to_owned()). Use rmcp_tools_with_timeout if you want to bound how long each tool call may run before it resolves to a recoverable error.
Building your own MCP server
Section titled “Building your own MCP server”rmcp also lets you expose custom tools as a server that any MCP client can use. Enable the server features:
cargo add rmcp -F server,macros,transport-streamable-http-servercargo add tokio -F fullDefine the tools
Section titled “Define the tools”Tools live on a struct that carries a ToolRouter. Annotate the impl block with #[tool_router] and each tool method with #[tool(...)]. Arguments are extracted from a schemars::JsonSchema type via the Parameters wrapper, and each tool returns a CallToolResult:
use rmcp::{ ServerHandler, handler::server::{router::tool::ToolRouter, wrapper::Parameters}, model::{CallToolResult, Content, ErrorData, ServerInfo}, schemars, tool, tool_handler, tool_router,};use serde::Deserialize;
#[derive(Debug, Deserialize, schemars::JsonSchema)]struct AddRequest { a: f64, b: f64,}
#[derive(Clone)]struct CalculatorServer { tool_router: ToolRouter<CalculatorServer>,}
#[tool_router]impl CalculatorServer { fn new() -> Self { Self { tool_router: Self::tool_router(), } }
#[tool(description = "Add two numbers together")] fn add( &self, Parameters(AddRequest { a, b }): Parameters<AddRequest>, ) -> Result<CallToolResult, ErrorData> { Ok(CallToolResult::success(vec![Content::text( (a + b).to_string(), )])) }}Then implement ServerHandler for the same struct. The #[tool_handler] macro wires the tool_router field into the handler so tool discovery and invocation work automatically:
#[tool_handler]impl ServerHandler for CalculatorServer { fn get_info(&self) -> ServerInfo { ServerInfo::default() }}Serve it
Section titled “Serve it”Wrap the server in a StreamableHttpService and mount it behind an HTTP server (for example hyper or axum) bound to a port:
use rmcp::transport::streamable_http_server::{ StreamableHttpService, session::local::LocalSessionManager,};
let service = StreamableHttpService::new( || Ok(CalculatorServer::new()), LocalSessionManager::default().into(), Default::default(),);The rmcp example shows the full server, including the HTTP accept loop and how to run the server and an MCP-enabled agent in the same program.
See also
Section titled “See also”- Tools — how Rig’s native tools work.
- Agents — building and prompting agents.
rmcpon docs.rs — the underlying MCP crate.
