Skip to content

Commit cd86d78

Browse files
committed
feat: add actix-web support for SSE server transport
- Add actix-web as an optional web framework alongside Axum - Refactor SSE server into modular structure with common types - Create actix_web implementation with identical API to Axum - Add comprehensive unit and integration tests for both implementations - Make actix-web the default when enabled (Axum still available as AxumSseServer) - Add working actix-web example (counter_sse_actix.rs) - Fix calculator test model to use #[tool_handler] macro - Verified 100% protocol compatibility with JavaScript and Python MCP clients The implementation maintains full backwards compatibility - when only the axum feature is enabled, the original Axum implementation is used.
1 parent 9ca20c6 commit cd86d78

File tree

18 files changed

+1919
-63
lines changed

18 files changed

+1919
-63
lines changed

crates/rmcp/Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ process-wrap = { version = "8.2", features = ["tokio1"], optional = true }
5555

5656
# for http-server transport
5757
axum = { version = "0.8", features = [], optional = true }
58+
actix-web = { version = "4", optional = true }
59+
actix-rt = { version = "2", optional = true }
5860
rand = { version = "0.9", optional = true }
5961
tokio-stream = { version = "0.1", optional = true }
6062
uuid = { version = "1", features = ["v4"], optional = true }
6163
http-body = { version = "1", optional = true }
6264
http-body-util = { version = "0.1", optional = true }
6365
bytes = { version = "1", optional = true }
66+
async-stream = { version = "0.3", optional = true }
6467
# macro
6568
rmcp-macros = { version = "0.1", workspace = true, optional = true }
6669
[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies]
@@ -70,10 +73,13 @@ chrono = { version = "0.4.38", features = ["serde"] }
7073
chrono = { version = "0.4.38", default-features = false, features = ["serde", "clock", "std", "oldtime"] }
7174

7275
[features]
73-
default = ["base64", "macros", "server"]
76+
default = ["base64", "macros", "server", "axum"]
7477
client = ["dep:tokio-stream"]
7578
server = ["transport-async-rw", "dep:schemars"]
7679
macros = ["dep:rmcp-macros", "dep:paste"]
80+
# Web framework features
81+
axum = ["dep:axum"]
82+
actix-web = ["dep:actix-web", "dep:actix-rt", "dep:async-stream"]
7783

7884
# reqwest http client
7985
__reqwest = ["dep:reqwest"]
@@ -116,7 +122,6 @@ transport-sse-server = [
116122
"transport-async-rw",
117123
"transport-worker",
118124
"server-side-http",
119-
"dep:axum",
120125
]
121126
transport-streamable-http-server = [
122127
"transport-streamable-http-server-session",
@@ -125,6 +130,7 @@ transport-streamable-http-server = [
125130
transport-streamable-http-server-session = [
126131
"transport-async-rw",
127132
"dep:tokio-stream",
133+
"transport-worker",
128134
]
129135
# transport-ws = ["transport-io", "dep:tokio-tungstenite"]
130136
tower = ["dep:tower-service"]

crates/rmcp/src/transport.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,37 @@
1010
//! | streamable http | [`streamable_http_client::StreamableHttpClientTransport`] | [`streamable_http_server::StreamableHttpService`] |
1111
//! | sse | [`sse_client::SseClientTransport`] | [`sse_server::SseServer`] |
1212
//!
13-
//!## Helper Transport Types
13+
//! ## Framework Support
14+
//!
15+
//! Several transport types support multiple web frameworks through feature flags:
16+
//!
17+
//! ### SSE Server Transport
18+
//! - **Convenience alias**: [`SseServer`] - resolves to the appropriate implementation based on enabled features
19+
//! - **Explicit types**: [`AxumSseServer`], [`ActixSseServer`] - use specific framework implementations
20+
//!
21+
//! ### Streamable HTTP Server Transport
22+
//! - **Convenience alias**: [`StreamableHttpService`] - resolves to the appropriate implementation based on enabled features
23+
//! - **Explicit types**: [`AxumStreamableHttpService`], [`ActixStreamableHttpService`] - use specific framework implementations
24+
//!
25+
//! #### Type Resolution Strategy
26+
//! The convenience aliases resolve as follows:
27+
//! - When `actix-web` feature is enabled: aliases point to actix-web implementations
28+
//! - When only `axum` feature is enabled: aliases point to axum implementations
29+
//!
30+
//! #### Usage Examples
31+
//! ```rust,ignore
32+
//! // Using convenience aliases (recommended for most cases)
33+
//! use rmcp::transport::{SseServer, StreamableHttpService};
34+
//! let server = SseServer::serve("127.0.0.1:8080".parse()?).await?;
35+
//!
36+
//! // Using explicit types (when you need a specific implementation)
37+
//! #[cfg(feature = "axum")]
38+
//! use rmcp::transport::AxumSseServer;
39+
//! #[cfg(feature = "axum")]
40+
//! let server = AxumSseServer::serve("127.0.0.1:8080".parse()?).await?;
41+
//! ```
42+
//!
43+
//! ## Helper Transport Types
1444
//! Thers are several helper transport types that can help you to create transport quickly.
1545
//!
1646
//! ### [Worker Transport](`worker::WorkerTransport`)
@@ -105,10 +135,21 @@ pub use sse_client::SseClientTransport;
105135
#[cfg(feature = "transport-sse-server")]
106136
#[cfg_attr(docsrs, doc(cfg(feature = "transport-sse-server")))]
107137
pub mod sse_server;
108-
#[cfg(feature = "transport-sse-server")]
109-
#[cfg_attr(docsrs, doc(cfg(feature = "transport-sse-server")))]
138+
139+
// Re-export convenience alias
140+
#[cfg(all(feature = "transport-sse-server", any(feature = "axum", feature = "actix-web")))]
141+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-sse-server", any(feature = "axum", feature = "actix-web")))))]
110142
pub use sse_server::SseServer;
111143

144+
// Re-export explicit types
145+
#[cfg(all(feature = "transport-sse-server", feature = "axum"))]
146+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-sse-server", feature = "axum"))))]
147+
pub use sse_server::AxumSseServer;
148+
149+
#[cfg(all(feature = "transport-sse-server", feature = "actix-web"))]
150+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-sse-server", feature = "actix-web"))))]
151+
pub use sse_server::ActixSseServer;
152+
112153
#[cfg(feature = "auth")]
113154
#[cfg_attr(docsrs, doc(cfg(feature = "auth")))]
114155
pub mod auth;
@@ -122,9 +163,25 @@ pub use auth::{AuthError, AuthorizationManager, AuthorizationSession, Authorized
122163
#[cfg(feature = "transport-streamable-http-server-session")]
123164
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-server-session")))]
124165
pub mod streamable_http_server;
166+
167+
// Re-export configuration
125168
#[cfg(feature = "transport-streamable-http-server")]
126169
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-server")))]
127-
pub use streamable_http_server::tower::{StreamableHttpServerConfig, StreamableHttpService};
170+
pub use streamable_http_server::StreamableHttpServerConfig;
171+
172+
// Re-export the preferred implementation
173+
#[cfg(all(feature = "transport-streamable-http-server", any(feature = "axum", feature = "actix-web")))]
174+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-streamable-http-server", any(feature = "axum", feature = "actix-web")))))]
175+
pub use streamable_http_server::StreamableHttpService;
176+
177+
// Re-export explicit types
178+
#[cfg(all(feature = "transport-streamable-http-server", feature = "axum"))]
179+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-streamable-http-server", feature = "axum"))))]
180+
pub use streamable_http_server::AxumStreamableHttpService;
181+
182+
#[cfg(all(feature = "transport-streamable-http-server", feature = "actix-web"))]
183+
#[cfg_attr(docsrs, doc(cfg(all(feature = "transport-streamable-http-server", feature = "actix-web"))))]
184+
pub use streamable_http_server::ActixStreamableHttpService;
128185

129186
#[cfg(feature = "transport-streamable-http-client")]
130187
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-client")))]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub const HEADER_SESSION_ID: &str = "Mcp-Session-Id";
22
pub const HEADER_LAST_EVENT_ID: &str = "Last-Event-Id";
3+
pub const HEADER_X_ACCEL_BUFFERING: &str = "X-Accel-Buffering";
34
pub const EVENT_STREAM_MIME_TYPE: &str = "text/event-stream";
45
pub const JSON_MIME_TYPE: &str = "application/json";

0 commit comments

Comments
 (0)