Skip to content

Commit ddc5600

Browse files
authored
feat!: extract Axum to standalone rust-mcp-axum crate (#146)
* feat: introduce framework-agnostic McpHttpError type * refactor: switch mcp_http layer to framework-agnostic McpHttpError * feat!: extract Axum to standalone rust-mcp-axum crate * Delete error.rs
1 parent e0c44c0 commit ddc5600

53 files changed

Lines changed: 1036 additions & 555 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 20 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"crates/rust-mcp-sdk",
66
"crates/rust-mcp-transport",
77
"crates/rust-mcp-extra",
8+
"crates/rust-mcp-axum",
89
]
910

1011
[workspace.package]
@@ -16,6 +17,7 @@ rust-mcp-transport = { version = "0.9.0", path = "crates/rust-mcp-transport", de
1617
rust-mcp-sdk = { path = "crates/rust-mcp-sdk", default-features = false }
1718
rust-mcp-macros = { version = "0.9.0", path = "crates/rust-mcp-macros", default-features = false }
1819
rust-mcp-extra = { version="0.1.0", path = "crates/rust-mcp-extra", default-features = false }
20+
rust-mcp-axum = { version = "0.1.0", path = "crates/rust-mcp-axum" }
1921

2022
# External crates
2123
rust-mcp-schema = { version="0.10", default-features = false }

Makefile.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ set -e
5454
5555
cargo build --lib -p rust-mcp-sdk --no-default-features --features "client,stdio"
5656
cargo build --lib -p rust-mcp-sdk --no-default-features --features "server,stdio"
57-
cargo build --lib -p rust-mcp-sdk --no-default-features --features "hyper-server,streamable-http"
58-
cargo build --lib -p rust-mcp-sdk --no-default-features --features "hyper-server,sse"
57+
cargo build --lib -p rust-mcp-sdk --no-default-features --features "server,streamable-http"
58+
cargo build --lib -p rust-mcp-sdk --no-default-features --features "server,sse"
5959
'''
6060

6161

README.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ This SDK fully implements the latest MCP protocol version ([2025-11-25](https://
5959
- [Authentication](#authentication)
6060
- [RemoteAuthProvider](#remoteauthprovider)
6161
- [OAuthProxy](#oauthproxy)
62-
- [HyperServerOptions](#hyperserveroptions)
62+
- [AxumServerOptions](#axumserveroptions)
6363
- [Security Considerations](#security-considerations)
6464
- [Cargo features](#cargo-features)
6565
- [Available Features](#available-features)
@@ -164,14 +164,15 @@ async fn main() -> SdkResult<()> {
164164

165165
## Minimal MCP Server (Streamable HTTP)
166166
Creating an MCP server in `rust-mcp-sdk` allows multiple clients to connect simultaneously with no additional setup.
167-
The setup is nearly identical to the stdio example shown above. You only need to create a Hyper server via `hyper_server::create_server()` and pass in the same handler and `HyperServerOptions`.
167+
The setup is nearly identical to the stdio example shown above. You only need to install the `rust-mcp-axum` crate and use `create_axum_server()` with `AxumServerOptions`.
168168

169-
💡 If backward compatibility is required, you can enable **SSE** transport by setting `sse_support` to true in `HyperServerOptions`.
169+
💡 If backward compatibility is required, you can enable **SSE** transport by setting `sse_support` to true in `AxumServerOptions`.
170170

171171
```rust
172172
use async_trait::async_trait;
173+
use rust_mcp_axum::{create_axum_server, AxumServerOptions};
173174
use rust_mcp_sdk::{*,error::SdkResult,event_store::InMemoryEventStore,macros,
174-
mcp_server::{hyper_server, HyperServerOptions, ServerHandler},schema::*,
175+
mcp_server::ServerHandler,schema::*,
175176
};
176177

177178
// Define a mcp tool
@@ -232,10 +233,10 @@ async fn main() -> SdkResult<()> {
232233
};
233234

234235
let handler = HelloHandler::default().to_mcp_server_handler();
235-
let server = hyper_server::create_server(
236+
let server = create_axum_server(
236237
server_info,
237238
handler,
238-
HyperServerOptions {
239+
AxumServerOptions {
239240
host: "127.0.0.1".to_string(),
240241
event_store: Some(std::sync::Arc::new(InMemoryEventStore::default())), // enable resumability
241242
..Default::default()
@@ -442,20 +443,20 @@ MCP server can verify tokens issued by other systems, integrate with external id
442443
443444

444445

445-
## HyperServerOptions
446+
## AxumServerOptions
446447

447-
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.
448+
AxumServer is a lightweight Axum-based server provided by the `rust-mcp-axum` crate 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.
448449

449-
HyperServer is highly customizable through HyperServerOptions provided during initialization.
450+
AxumServer is highly customizable through AxumServerOptions provided during initialization.
450451

451-
A typical example of creating a HyperServer that exposes the MCP server via Streamable HTTP and SSE transports at:
452+
A typical example of creating an AxumServer that exposes the MCP server via Streamable HTTP and SSE transports at:
452453

453454
```rs
454455

455-
let server = hyper_server::create_server(
456+
let server = create_axum_server(
456457
server_details,
457458
handler.to_mcp_server_handler(),
458-
HyperServerOptions {
459+
AxumServerOptions {
459460
host: "127.0.0.1".to_string(),
460461
port: 8080,
461462
event_store: Some(std::sync::Arc::new(InMemoryEventStore::default())), // enable resumability
@@ -468,7 +469,7 @@ let server = hyper_server::create_server(
468469
server.start().await?;
469470
```
470471

471-
📝 Refer to [HyperServerOptions](https://github.com/rust-mcp-stack/rust-mcp-sdk/blob/main/crates/rust-mcp-sdk/src/hyper_servers/server.rs#L43) for a complete overview of HyperServerOptions attributes and options.
472+
📝 Refer to [AxumServerOptions](https://github.com/rust-mcp-stack/rust-mcp-sdk/blob/main/crates/rust-mcp-axum/src/server.rs#L43) for a complete overview of AxumServerOptions attributes and options.
472473

473474

474475
### Security Considerations
@@ -488,8 +489,6 @@ The `rust-mcp-sdk` crate provides several features that can be enabled or disabl
488489

489490
- `server`: Activates MCP server capabilities in `rust-mcp-sdk`, providing modules and APIs for building and managing MCP servers.
490491
- `client`: Activates MCP client capabilities, offering modules and APIs for client development and communicating with MCP servers.
491-
- `hyper-server`: This feature is necessary to enable `Streamable HTTP` or `Server-Sent Events (SSE)` transports for MCP servers. It must be used alongside the server feature to support the required server functionalities.
492-
- `ssl`: This feature enables TLS/SSL support for the `Streamable HTTP` or `Server-Sent Events (SSE)` transport when used with the `hyper-server`.
493492
- `macros`: Provides procedural macros for simplifying the creation and manipulation of MCP Tool structures.
494493
- `sse`: Enables support for the `Server-Sent Events (SSE)` transport.
495494
- `streamable-http`: Enables support for the `Streamable HTTP` transport.
@@ -520,7 +519,7 @@ If you only need the MCP Server functionality, you can disable the default featu
520519
[dependencies]
521520
rust-mcp-sdk = { version = "0.2.0", default-features = false, features = ["server","macros","stdio"] }
522521
```
523-
Optionally add `hyper-server` and `streamable-http` for **Streamable HTTP** transport, and `ssl` feature for tls/ssl support of the `hyper-server`
522+
Optionally add [`rust-mcp-axum`](https://crates.io/crates/rust-mcp-axum) and the `streamable-http` feature for **Streamable HTTP** transport, and use `rust-mcp-axum`'s `ssl` feature for TLS/SSL support.
524523

525524
<!-- x-release-please-end -->
526525

@@ -557,11 +556,11 @@ Learn when to use the `mcp_*_handler` traits versus the lower-level `mcp_*_hand
557556

558557
- For `ServerHandler`:
559558
- Use `server_runtime::create_server()` for servers with stdio transport
560-
- Use `hyper_server::create_server()` for servers with sse transport
559+
- Use `rust_mcp_axum::create_axum_server()` for servers with Streamable HTTP/SSE transport
561560

562561
- For `ServerHandlerCore`:
563562
- Use `server_runtime_core::create_server()` for servers with stdio transport
564-
- Use `hyper_server_core::create_server()` for servers with sse transport
563+
- Use `rust_mcp_axum::create_axum_server()` for servers with Streamable HTTP/SSE transport
565564

566565
---
567566

@@ -609,13 +608,13 @@ While not part of the official MCP spec, `rust-mcp-sdk` provides an optional HTT
609608
- Exposed behind load balancers or reverse proxies (e.g., NGINX, HAProxy, Cloudflare).
610609
- Running in container orchestration environments (e.g., Kubernetes, Docker Swarm, AWS ECS).
611610

612-
The health check endpoint is disabled by default. You can enable it and optionally provide your own custom handler (to return specific metrics or metadata) via `HyperServerOptions`:
611+
The health check endpoint is disabled by default. You can enable it and optionally provide your own custom handler (to return specific metrics or metadata) via `AxumServerOptions`:
613612

614613
```rs
615-
let server = hyper_server::create_server(
614+
let server = create_axum_server(
616615
server_details,
617616
handler.to_mcp_server_handler(),
618-
HyperServerOptions {
617+
AxumServerOptions {
619618
host: "127.0.0.1".into(),
620619
health_endpoint: Some("/health".into()), // enables the endpoint
621620
health_handler: Some(Arc::new(CustomHealth {})), // optional: overrides default 200 OK

crates/rust-mcp-axum/Cargo.toml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[package]
2+
name = "rust-mcp-axum"
3+
version = "0.1.0"
4+
authors = ["Ali Hashemi"]
5+
categories = ["data-structures", "parser-implementations", "parsing"]
6+
description = "Axum HTTP server integration for rust-mcp-sdk"
7+
repository = "https://github.com/rust-mcp-stack/rust-mcp-sdk"
8+
documentation = "https://docs.rs/rust-mcp-axum"
9+
keywords = ["rust-mcp-stack", "model", "context", "protocol", "sdk", "axum"]
10+
license = "MIT"
11+
edition = "2021"
12+
rust-version = { workspace = true }
13+
14+
[dependencies]
15+
rust-mcp-sdk = { workspace = true, features = ["server", "streamable-http", "sse", "auth"] }
16+
thiserror = { workspace = true }
17+
axum = { workspace = true }
18+
axum-server = { workspace = true, features = [] }
19+
tokio = { workspace = true }
20+
futures = { workspace = true }
21+
tracing = { workspace = true }
22+
http = { workspace = true }
23+
24+
[dev-dependencies]
25+
tempfile = "3.23.0"
26+
tower = "0.5"
27+
serde_json = { workspace = true }
28+
http-body-util = { workspace = true }
29+
30+
[features]
31+
ssl = ["axum-server/tls-rustls"]
32+
tls-no-provider = ["axum-server/tls-rustls-no-provider"]
33+
34+
[lints]
35+
workspace = true

crates/rust-mcp-sdk/src/hyper_servers/error.rs renamed to crates/rust-mcp-axum/src/error.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ use std::net::AddrParseError;
33
use axum::{http::StatusCode, response::IntoResponse};
44
use thiserror::Error;
55

6-
use crate::mcp_http::McpHttpError;
6+
use rust_mcp_sdk::mcp_http::McpHttpError;
77

8-
#[cfg(feature = "auth")]
9-
use crate::auth::AuthenticationError;
8+
use rust_mcp_sdk::auth::AuthenticationError;
109

1110
pub type TransportServerResult<T> = core::result::Result<T, TransportServerError>;
1211

@@ -30,13 +29,11 @@ pub enum TransportServerError {
3029
SslCertError(String),
3130
#[error("{0}")]
3231
TransportError(String),
33-
#[cfg(feature = "auth")]
3432
#[error("{0}")]
3533
AuthenticationError(#[from] AuthenticationError),
3634
}
3735

3836
impl IntoResponse for TransportServerError {
39-
//consume self and returns a Response
4037
fn into_response(self) -> axum::response::Response {
4138
let mut response = StatusCode::INTERNAL_SERVER_ERROR.into_response();
4239
response.extensions_mut().insert(self);
@@ -65,7 +62,6 @@ impl From<TransportServerError> for McpHttpError {
6562
TransportServerError::HttpError(s) => McpHttpError::HttpError(s),
6663
TransportServerError::TransportError(s) => McpHttpError::TransportError(s),
6764

68-
#[cfg(feature = "auth")]
6965
TransportServerError::AuthenticationError(e) => McpHttpError::HttpError(e.to_string()),
7066

7167
TransportServerError::AddrParseError(e) => McpHttpError::HttpError(e.to_string()),
@@ -76,10 +72,18 @@ impl From<TransportServerError> for McpHttpError {
7672
}
7773
}
7874

75+
impl From<TransportServerError> for rust_mcp_sdk::error::McpSdkError {
76+
fn from(err: TransportServerError) -> Self {
77+
rust_mcp_sdk::error::McpSdkError::Internal {
78+
description: err.to_string(),
79+
}
80+
}
81+
}
82+
7983
#[cfg(test)]
8084
mod tests {
8185
use super::*;
82-
use crate::mcp_http::McpHttpResult;
86+
use rust_mcp_sdk::mcp_http::{McpHttpError, McpHttpResult};
8387

8488
// McpHttpError to TransportServerError
8589

@@ -189,7 +193,6 @@ mod tests {
189193
assert!(matches!(m, McpHttpError::HttpError(ref s) if s == "cert expired"));
190194
}
191195

192-
#[cfg(feature = "auth")]
193196
#[test]
194197
fn transport_to_mcp_authentication_lossy() {
195198
let auth_err = AuthenticationError::InactiveToken;
@@ -198,7 +201,7 @@ mod tests {
198201
assert!(matches!(m, McpHttpError::HttpError(ref s) if s.contains("Inactive")));
199202
}
200203

201-
// Round-trip: McpHttpError to TransportServerError to McpHttpError
204+
// Round-trip
202205

203206
#[test]
204207
fn round_trip_session_id_missing() {
@@ -240,7 +243,7 @@ mod tests {
240243
assert_eq!(format!("{}", m), format!("{}", back));
241244
}
242245

243-
// Round-trip: TransportServerError > McpHttpError > TransportServerError
246+
// Reverse round-trip
244247

245248
#[test]
246249
fn reverse_round_trip_session_id_missing() {
@@ -296,9 +299,6 @@ mod tests {
296299
fn mcp_http_result_from_transport_error() {
297300
let r: TransportServerResult<()> = Err(TransportServerError::SessionIdInvalid("x".into()));
298301
let m: McpHttpResult<()> = r.map_err(Into::into);
299-
assert!(matches!(
300-
m.unwrap_err(),
301-
McpHttpError::SessionIdInvalid(ref s) if s == "x"
302-
));
302+
assert!(matches!(m.unwrap_err(), McpHttpError::SessionIdInvalid(ref s) if s == "x"));
303303
}
304304
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use super::{AxumServer, AxumServerOptions};
2+
use rust_mcp_sdk::schema::InitializeResult;
3+
use rust_mcp_sdk::McpServerHandler;
4+
use std::sync::Arc;
5+
6+
/// Creates a new AxumServer instance with the provided handler and options
7+
/// The handler must implement ServerHandler.
8+
///
9+
/// # Arguments
10+
/// * `server_details` - Initialization result from the MCP schema
11+
/// * `handler` - Implementation of the ServerHandlerCore trait
12+
/// * `server_options` - Configuration options for the AxumServer
13+
///
14+
/// # Returns
15+
/// * `AxumServer` - A configured AxumServer instance ready to start
16+
pub fn create_axum_server(
17+
server_details: InitializeResult,
18+
handler: Arc<dyn McpServerHandler + 'static>,
19+
server_options: AxumServerOptions,
20+
) -> AxumServer {
21+
AxumServer::new(server_details, handler, server_options)
22+
}

crates/rust-mcp-axum/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
mod error;
2+
mod factory;
3+
pub mod routes;
4+
mod runtime;
5+
mod server;
6+
mod utils;
7+
8+
pub use error::*;
9+
pub use factory::*;
10+
pub use routes::mcp_routes;
11+
pub use runtime::*;
12+
pub use server::*;
13+
14+
pub use axum;

0 commit comments

Comments
 (0)