A high-performance, asynchronous toolkit for building MCP servers and clients in Rust.
Documentation
Rust MCP SDK
[
](https://github.com/rust-mcp-stack/rust-mcp-sdk/actions/workflows/ci.yml)
[
](examples/hello-world-mcp-server-stdio)
A high-performance, asynchronous toolkit for building MCP servers and clients.
Focus on your app's logic while rust-mcp-sdk takes care of the rest!
rust-mcp-sdk provides the necessary components for developing both servers and clients in the MCP ecosystem.
Leveraging the rust-mcp-schema crate simplifies the process of building robust and reliable MCP servers and clients, ensuring consistency and minimizing errors in data handling and message processing.
rust-mcp-sdk supports all three official versions of the MCP protocol.
By default, it uses the 2025-06-18 version, but earlier versions can be enabled via Cargo features.
This project supports following transports:
- Stdio (Standard Input/Output)
- Streamable HTTP
- SSE (Server-Sent Events)
🚀 The rust-mcp-sdk includes a lightweight Axum based server that handles all core functionality seamlessly. Switching between stdio and Streamable HTTP is straightforward, requiring minimal code changes. The server is designed to efficiently handle multiple concurrent client connections and offers built-in support for SSL.
MCP Streamable HTTP Support
- ✅ Streamable HTTP Support for MCP Servers
- ✅ DNS Rebinding Protection
- ✅ Batch Messages
- ✅ Streaming & non-streaming JSON response
- ✅ Streamable HTTP Support for MCP Clients
- ✅ Resumability
- ⬜ Oauth Authentication
⚠️ Project is currently under development and should be used at your own risk.
Table of Contents
- Usage Examples
- MCP Server (stdio)
- MCP Server (Streamable HTTP)
- MCP Client (stdio)
- MCP Client (Streamable HTTP))
- MCP Client (sse)
- Macros
- Getting Started
- HyperServerOptions
- Security Considerations
- Cargo features
- Available Features
- MCP protocol versions with corresponding features
- Default Features
- Using Only the server Features
- Using Only the client Features
- Choosing Between Standard and Core Handlers traits
- Choosing Between **ServerHandler** and **ServerHandlerCore**
- Choosing Between **ClientHandler** and **ClientHandlerCore**
- Projects using Rust MCP SDK
- Contributing
- Development
- License
Usage Examples
MCP Server (stdio)
Create a MCP server with a tool that will print a Hello World! message:
#[tokio::main]
async fn main() -> SdkResult {
// STEP 1: Define server details and capabilities
let server_details = InitializeResult {
// server name and version
server_info: Implementation {
name: "Hello World MCP Server".to_string(),
version: "0.1.0".to_string(),
title: Some("Hello World MCP Server".to_string()),
},
capabilities: ServerCapabilities {
// indicates that server support mcp tools
tools: Some(ServerCapabilitiesTools { list_changed: None }),
..Default::default() // Using default values for other fields
},
meta: None,
instructions: Some("server instructions...".to_string()),
protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
};
// STEP 2: create a std transport with default options
let transport = StdioTransport::new(TransportOptions::default())?;
// STEP 3: instantiate our custom handler for handling MCP messages
let handler = MyServerHandler {};
// STEP 4: create a MCP server
let server: ServerRuntime = server_runtime::create_server(server_details, transport, handler);
// STEP 5: Start the server
server.start().await
}See hello-world-mcp-server-stdio example running in MCP Inspector :

MCP Server (Streamable HTTP)
Creating an MCP server in rust-mcp-sdk with the sse transport allows multiple clients to connect simultaneously with no additional setup.
Simply create a Hyper Server using hyper_server::create_server() and pass in the same handler and HyperServerOptions.
💡 By default, both Streamable HTTP and SSE transports are enabled for backward compatibility. To disable the SSE transport , set the sse_support to false in the HyperServerOptions.
// STEP 1: Define server details and capabilities
let server_details = InitializeResult {
// server name and version
server_info: Implementation {
name: "Hello World MCP Server".to_string(),
version: "0.1.0".to_string(),
title: Some("Hello World MCP Server".to_string()),
},
capabilities: ServerCapabilities {
// indicates that server support mcp tools
tools: Some(ServerCapabilitiesTools { list_changed: None }),
..Default::default() // Using default values for other fields
},
meta: None,
instructions: Some("server instructions...".to_string()),
protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
};
// STEP 2: instantiate our custom handler for handling MCP messages
let handler = MyServerHandler {};
// STEP 3: instantiate HyperServer, providing `server_details` , `handler` and HyperServerOptions
let server = hyper_server::create_server(
server_details,
handler,
HyperServerOptions {
host: "127.0.0.1".to_string(),
sse_support: false,
event_store: Some(Arc::new(InMemoryEventStore::default())), // enable resumability
..Default::default()
},
);
// STEP 4: Start the server
server.start().await?;
Ok(())The implementation of MyServerHandler is the same regardless of the transport used and could be as simple as the following:
// STEP 1: Define a rust_mcp_schema::Tool ( we need one with no parameters for this example)
#[mcp_tool(name = "say_hello_world", description = "Prints \"Hello World!\" message")]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SayHelloTool {}
// STEP 2: Implement ServerHandler trait for a custom handler
// For this example , we only need handle_list_tools_request() and handle_call_tool_request() methods.
pub struct MyServerHandler;
#[async_trait]
impl ServerHandler for MyServerHandler {
// Handle ListToolsRequest, return list of available tools as ListToolsResult
async fn handle_list_tools_request(&self, request: ListToolsRequest, runtime: Arc) -> Result {
Ok(ListToolsResult {
tools: vec![SayHelloTool::tool()],
meta: None,
next_cursor: None,
})
}
/// Handles requests to call a specific tool.
async fn handle_call_tool_request( &self, request: CallToolRequest, runtime: Arc ) -> Result {
if request.tool_name() == SayHelloTool::tool_name() {
Ok( CallToolResult::text_content( vec![TextContent::from("Hello World!".to_string())] ))
} else {
Err(CallToolError::unknown_tool(request.tool_name().to_string()))
}
}
}---
👉 For a more detailed example of a Hello World MCP Server that supports multiple tools and provides more type-safe handling of CallToolRequest, check out: **examples/hello-world-mcp-server**
See hello-world-server-streamable-http example running in MCP Inspector :

---
MCP Client (stdio)
Create an MCP client that starts the @modelcontextprotocol/server-everything server, displays the server's name, version, and list of tools, then uses the add tool provided by the server to sum 120 and 28, printing the result.
// STEP 1: Custom Handler to handle incoming MCP Messages
pub struct MyClientHandler;
#[async_trait]
impl ClientHandler for MyClientHandler {
// To check out a list of all the methods in the trait that you can override, take a look at https://github.com/rust-mcp-stack/rust-mcp-sdk/blob/main/crates/rust-mcp-sdk/src/mcp_handlers/mcp_client_handler.rs
}
#[tokio::main]
async fn main() -> SdkResult {
// Step2 : Define client details and capabilities
let client_details: InitializeRequestParams = InitializeRequestParams {
capabilities: ClientCapabilities::default(),
client_info: Implementation {
name: "simple-rust-mcp-client".into(),
version: "0.1.0".into(),
},
protocol_version: LATEST_PROTOCOL_VERSION.into(),
};
// Step3 : Create a transport, with options to launch @modelcontextprotocol/server-everything MCP Server
let transport = StdioTransport::create_with_server_launch(
"npx",
vec![ "-y".to_string(), "@modelcontextprotocol/server-everything".to_string()],
None, TransportOptions::default()
)?;
// STEP 4: instantiate our custom handler for handling MCP messages
let handler = MyClientHandler {};
// STEP 5: create a MCP client
let client = client_runtime::create_client(client_details, transport, handler);
// STEP 6: start the MCP client
client.clone().start().await?;
// STEP 7: use client methods to communicate with the MCP Server as you wish
// Retrieve and display the list of tools available on the server
let server_version = client.server_version().unwrap();
let tools = client.list_tools(None).await?.tools;
println!("List of tools for {}@{}", server_version.name, server_version.version);
tools.iter().enumerate().for_each(|(tool_index, tool)| {
println!(" {}. {} : {}",
tool_index + 1,
tool.name,
tool.description.clone().unwrap_or_default()
);
});
println!("Call \"add\" tool with 100 and 28 ...");
// Create a `Map` to represent the tool parameters
let params = json!({"a": 100,"b": 28}).as_object().unwrap().clone();
let request = CallToolRequestParams { name: "add".to_string(),arguments: Some(params)};
// invoke the tool
let result = client.call_tool(request).await?;
println!("{}",result.content.first().unwrap().as_text_content()?.text);
client.shut_down().await?;
Ok(())
}Here is the output :

your results may vary slightly depending on the version of the MCP Server in use when you run it.
MCP Client (Streamable HTTP)
// STEP 1: Custom Handler to handle incoming MCP Messages
pub struct MyClientHandler;
#[async_trait]
impl ClientHandler for MyClientHandler {
// To check out a list of all the methods in the trait that you can override, take a look at https://github.com/rust-mcp-stack/rust-mcp-sdk/blob/main/crates/rust-mcp-sdk/src/mcp_handlers/mcp_client_handler.rs
}
#[tokio::main]
async fn main() -> SdkResult {
// Step2 : Define client details and capabilities
let client_details: InitializeRequestParams = InitializeRequestParams {
capabilities: ClientCapabilities::default(),
client_info: Implementation {
name: "simple-rust-mcp-client-sse".to_string(),
version: "0.1.0".to_string(),
title: Some("Simple Rust MCP Client (SSE)".to_string()),
},
protocol_version: LATEST_PROTOCOL_VERSION.into(),
};
// Step 3: Create transport options to connect to an MCP server via Streamable HTTP.
let transport_options = StreamableTransportOptions {
mcp_url: MCP_SERVER_URL.to_string(),
request_options: RequestOptions {
..RequestOptions::default()
},
};
// STEP 4: instantiate the custom handler that is responsible for handling MCP messages
let handler = MyClientHandler {};
// STEP 5: create the client with transport options and the handler
let client = client_runtime::with_transport_options(client_details, transport_options, handler);
// STEP 6: start the MCP client
client.clone().start().await?;
// STEP 7: use client methods to communicate with the MCP Server as you wish
// Retrieve and display the list of tools available on the server
let server_version = client.server_version().unwrap();
let tools = client.list_tools(None).await?.tools;
println!("List of tools for {}@{}", server_version.name, server_version.version);
tools.iter().enumerate().for_each(|(tool_index, tool)| {
println!(" {}. {} : {}",
tool_index + 1,
tool.name,
tool.description.clone().unwrap_or_default()
);
});
println!("Call \"add\" tool with 100 and 28 ...");
// Create a `Map` to represent the tool parameters
let params = json!({"a": 100,"b": 28}).as_object().unwrap().clone();
let request = CallToolRequestParams { name: "add".to_string(),arguments: Some(params)};
// invoke the tool
let result = client.call_tool(request).await?;
println!("{}",result.content.first().unwrap().as_text_content()?.text);
client.shut_down().await?;
Ok(())👉 see examples/simple-mcp-client-streamable-http for a complete working example.
MCP Client (sse)
Creating an MCP client using the rust-mcp-sdk with the SSE transport is almost identical to the stdio example , with one exception at step 3. Instead of creating a StdioTransport, you simply create a ClientSseTransport. The rest of the code remains the same:
- let transport = StdioTransport::create_with_server_launch(
- "npx",
- vec![ "-y".to_string(), "@modelcontextprotocol/server-everything".to_string()],
- None, TransportOptions::default()
-)?;
+ let transport = ClientSseTransport::new(MCP_SERVER_URL, ClientSseTransportOptions::default())?;👉 see examples/simple-mcp-client-sse for a complete working example.
Macros
rust-mcp-sdk includes several helpful macros that simplify common tasks when building MCP servers and clients. For example, they can automatically generate tool specifications and tool schemas right from your structs, or assist with elicitation requests and responses making them completely type safe.
To use these macros, ensure the
macrosfeature is enabled in your Cargo.toml.
mcp_tool
mcp_tool is a procedural macro attribute that helps generating rust_mcp_schema::Tool from a struct.
Usage example:
#[mcp_tool(
name = "move_file",
title="Move File",
description = concat!("Move or rename files and directories. Can move files between directories ",
"and rename them in a single operation. If the destination exists, the ",
"operation will fail. Works across different directories and can be used ",
"for simple renaming within the same directory. ",
"Both source and destination must be within allowed directories."),
destructive_hint = false,
idempotent_hint = false,
open_world_hint = false,
read_only_hint = false
)]
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, JsonSchema)]
pub struct MoveFileTool {
/// The source path of the file to move.
pub source: String,
/// The destination path to move the file to.
pub destination: String,
}
// Now we can call `tool()` method on it to get a Tool instance
let rust_mcp_sdk::schema::Tool = MoveFileTool::tool();💻 For a real-world example, check out any of the tools available at: https://github.com/rust-mcp-stack/rust-mcp-filesystem/tree/main/src/tools
tool_box
tool_box generates an enum from a provided list of tools, making it easier to organize and manage them, especially when your application includes a large number of tools.
It accepts an array of tools and generates an enum where each tool becomes a variant of the enum.
Generated enum has a tools() function that returns a Vec , and a TryFrom trait implementation that could be used to convert a ToolRequest into a Tool instance.
Usage example:
// Accepts an array of tools and generates an enum named `FileSystemTools`,
// where each tool becomes a variant of the enum.
tool_box!(FileSystemTools, [ReadFileTool, MoveFileTool, SearchFilesTool]);
// now in the app, we can use the FileSystemTools, like:
let all_tools: Vec = FileSystemTools::tools();💻 To see a real-world example of that please see :
tool_boxmacro usage: https://github.com/rust-mcp-stack/rust-mcp-filesystem/blob/main/src/tools.rs- using
tools()in list tools request : https://github.com/rust-mcp-stack/rust-mcp-filesystem/blob/main/src/handler.rs - using
try_fromin call tool_request: https://github.com/rust-mcp-stack/rust-mcp-filesystem/blob/main/src/handler.rs
mcp_elicit
The mcp_elicit macro generates implementations for the annotated struct to facilitate data elicitation. It enables struct to generate ElicitRequestedSchema and also parsing a map of field names to ElicitResultContentValue values back into the struct, supporting both required and optional fields. The generated implementation includes:
- A
message()method returning the elicitation message as a string. - A
requested_schema()method returning anElicitRequestedSchemabased on the struct’s JSON schema. - A
from_content_map()method to convert a map ofElicitResultContentValuevalues into a struct instance.
Attributes
message- An optional string (orconcat!(...)expression) to prompt the user or system for input. Defaults to an empty string if not provided.
Usage example:
// A struct that could be used to send elicit request and get the input from the user
#[mcp_elicit(message = "Please enter your info")]
#[derive(JsonSchema)]
pub struct UserInfo {
#[json_schema(
title = "Name",
description = "The user's full name",
min_length = 5,
max_length = 100
)]
pub name: String,
/// Is user a student?
#[json_schema(title = "Is student?", default = true)]
pub is_student: Option,
/// User's favorite color
pub favorate_color: Colors,
}
// send a Elicit Request , ask for UserInfo data and convert the result back to a valid UserInfo instance
let result: ElicitResult = server
.elicit_input(UserInfo::message(), UserInfo::requested_schema())
.await?;
// Create a UserInfo instance using data provided by the user on the client side
let user_info = UserInfo::from_content_map(result.content)?;💻 For mre info please see :
- https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/crates/rust-mcp-macros
Getting Started
If you are looking for a step-by-step tutorial on how to get started with rust-mcp-sdk , please see : Getting Started MCP Server
HyperServerOptions
HyperServer is a lightweight Axum-based server that streamlines MCP servers by supporting Streamable HTTP and SSE transports. It supports simultaneous client connections, internal session management, and includes built-in security features like DNS rebinding protection and more.
HyperServer is highly customizable through HyperServerOptions provided during initialization.
A typical example of creating a HyperServer that exposes the MCP server via Streamable HTTP and SSE transports at:
let server = hyper_server::create_server(
server_details,
handler,
HyperServerOptions {
host: "127.0.0.1".to_string(),
enable_ssl: true,
..Default::default()
},
);
server.start().await?;Here is a list of available options with descriptions for configuring the HyperServer:
pub struct HyperServerOptions {
/// Hostname or IP address the server will bind to (default: "127.0.0.1")
pub host: String,
/// Hostname or IP address the server will bind to (default: "8080")
pub port: u16,
/// Optional thread-safe session id generator to generate unique session IDs.
pub session_id_generator: Option | [Rust MCP Filesystem](https://rust-mcp-stack.github.io/rust-mcp-filesystem) | Fast, async MCP server enabling high-performance, modern filesystem operations with advanced features. | [GitHub](https://github.com/rust-mcp-stack/rust-mcp-filesystem) |
| | [MCP Discovery](https://rust-mcp-stack.github.io/mcp-discovery) | A lightweight command-line tool for discovering and documenting MCP Server capabilities. | [GitHub](https://github.com/rust-mcp-stack/mcp-discovery) |
| | [mistral.rs](https://github.com/EricLBuehler/mistral.rs) | Blazingly fast LLM inference. | [GitHub](https://github.com/EricLBuehler/mistral.rs) |
| | [moon](https://github.com/moonrepo/moon) | moon is a repository management, organization, orchestration, and notification tool for the web ecosystem, written in Rust. | [GitHub](https://github.com/moonrepo/moon) |
| | [angreal](https://github.com/angreal/angreal) | Angreal provides a way to template the structure of projects and a way of executing methods for interacting with that project in a consistent manner. | [GitHub](https://github.com/angreal/angreal) |
| | [text-to-cypher](https://github.com/FalkorDB/text-to-cypher) | A high-performance Rust-based API service that translates natural language text to Cypher queries for graph databases. | [GitHub](https://github.com/FalkorDB/text-to-cypher) |
| | [notify-mcp](https://github.com/Tuurlijk/notify-mcp) | A Model Context Protocol (MCP) server that provides desktop notification functionality. | [GitHub](https://github.com/Tuurlijk/notify-mcp) |
| | [lst](https://github.com/WismutHansen/lst) | `lst` is a personal lists, notes, and blog posts management application with a focus on plain-text storage, offline-first functionality, and multi-device synchronization. | [GitHub](https://github.com/WismutHansen/lst) |
| | [rust-mcp-server](https://github.com/Vaiz/rust-mcp-server) | `rust-mcp-server` allows the model to perform actions on your behalf, such as building, testing, and analyzing your Rust code. | [GitHub](https://github.com/Vaiz/rust-mcp-server) |
## Contributing
We welcome everyone who wishes to contribute! Please refer to the [contributing](CONTRIBUTING.md) guidelines for more details.
Check out our [development guide](development.md) for instructions on setting up, building, testing, formatting, and trying out example projects.
All contributions, including issues and pull requests, must follow
Rust's Code of Conduct.
Unless explicitly stated otherwise, any contribution you submit for inclusion in rust-mcp-sdk is provided under the terms of the MIT License, without any additional conditions or restrictions.
## Development
Check out our [development guide](development.md) for instructions on setting up, building, testing, formatting, and trying out example projects.
## License
This project is licensed under the MIT License. see the [LICENSE](LICENSE) file for details.Similar MCP
Based on tags & features
Trending MCP
Most active this week