Skip to content

Add actix-web support #294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ rmcp = { version = "0.1", features = ["server"] }
## or dev channel
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" }
```

#### Web Framework Choice
By default, rmcp uses [axum](https://github.com/tokio-rs/axum) for web server transports (SSE and streamable HTTP). You can also use [actix-web](https://github.com/actix/actix-web) as an alternative:

```toml
# For actix-web support
rmcp = { version = "0.1", features = ["server", "actix-web"] }
```

**Note**: When both `axum` and `actix-web` features are enabled, actix-web takes precedence for convenience type aliases.

### Third Dependencies
Basic dependencies:
- [tokio required](https://github.com/tokio-rs/tokio)
Expand Down
10 changes: 8 additions & 2 deletions crates/rmcp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ process-wrap = { version = "8.2", features = ["tokio1"], optional = true }

# for http-server transport
axum = { version = "0.8", features = [], optional = true }
actix-web = { version = "4", optional = true }
actix-rt = { version = "2", optional = true }
rand = { version = "0.9", optional = true }
tokio-stream = { version = "0.1", optional = true }
uuid = { version = "1", features = ["v4"], optional = true }
http-body = { version = "1", optional = true }
http-body-util = { version = "0.1", optional = true }
bytes = { version = "1", optional = true }
async-stream = { version = "0.3", optional = true }
# macro
rmcp-macros = { version = "0.1", workspace = true, optional = true }
[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies]
Expand All @@ -70,10 +73,13 @@ chrono = { version = "0.4.38", features = ["serde"] }
chrono = { version = "0.4.38", default-features = false, features = ["serde", "clock", "std", "oldtime"] }

[features]
default = ["base64", "macros", "server"]
default = ["base64", "macros", "server", "axum"]
client = ["dep:tokio-stream"]
server = ["transport-async-rw", "dep:schemars"]
macros = ["dep:rmcp-macros", "dep:paste"]
# Web framework features
axum = ["dep:axum"]
actix-web = ["dep:actix-web", "dep:actix-rt", "dep:async-stream"]

# reqwest http client
__reqwest = ["dep:reqwest"]
Expand Down Expand Up @@ -116,7 +122,6 @@ transport-sse-server = [
"transport-async-rw",
"transport-worker",
"server-side-http",
"dep:axum",
]
transport-streamable-http-server = [
"transport-streamable-http-server-session",
Expand All @@ -125,6 +130,7 @@ transport-streamable-http-server = [
transport-streamable-http-server-session = [
"transport-async-rw",
"dep:tokio-stream",
"transport-worker",
]
# transport-ws = ["transport-io", "dep:tokio-tungstenite"]
tower = ["dep:tower-service"]
Expand Down
59 changes: 56 additions & 3 deletions crates/rmcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ RMCP uses feature flags to control which components are included:
- `client`: Enable client functionality
- `server`: Enable server functionality and the tool system
- `macros`: Enable the `#[tool]` macro (enabled by default)
- Web framework features:
- `axum`: Axum web framework support (enabled by default)
- `actix-web`: actix-web framework support as an alternative to axum
- Transport-specific features:
- `transport-async-rw`: Async read/write support
- `transport-io`: I/O stream support
Expand All @@ -204,15 +207,65 @@ RMCP uses feature flags to control which components are included:
- `auth`: OAuth2 authentication support
- `schemars`: JSON Schema generation (for tool definitions)

**Note**: When both `axum` and `actix-web` features are enabled, actix-web implementations take precedence for convenience type aliases.


## Web Framework Support

SSE and streamable HTTP server transports support multiple web frameworks:

### Using axum (default)
```rust
use rmcp::transport::SseServer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = SseServer::serve("127.0.0.1:8080".parse()?).await?;
let ct = server.with_service(|| Ok(MyService::new()));
ct.cancelled().await;
Ok(())
}
```

### Using actix-web
Enable the `actix-web` feature in your `Cargo.toml`:
```toml
rmcp = { version = "0.1", features = ["server", "actix-web"] }
```

Then use with actix-web runtime:
```rust
use rmcp::transport::SseServer;

#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = SseServer::serve("127.0.0.1:8080".parse()?).await?;
let ct = server.with_service(|| Ok(MyService::new()));
ct.cancelled().await;
Ok(())
}
```

### Framework-specific imports
When you need to use a specific framework implementation:
```rust
// For axum
#[cfg(feature = "axum")]
use rmcp::transport::sse_server::axum::SseServer;

// For actix-web
#[cfg(feature = "actix-web")]
use rmcp::transport::sse_server::actix_web::SseServer;
```

## Transports

- `transport-io`: Server stdio transport
- `transport-sse-server`: Server SSE transport
- `transport-sse-server`: Server SSE transport (supports both axum and actix-web)
- `transport-child-process`: Client stdio transport
- `transport-sse-client`: Client sse transport
- `transport-streamable-http-server` streamable http server transport
- `transport-streamable-http-client` streamable http client transport
- `transport-streamable-http-server`: Streamable HTTP server transport (supports both axum and actix-web)
- `transport-streamable-http-client`: Streamable HTTP client transport

<details>
<summary>Transport</summary>
Expand Down
17 changes: 17 additions & 0 deletions crates/rmcp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@
//! Next also implement [ServerHandler] for `Counter` and start the server inside
//! `main` by calling `Counter::new().serve(...)`. See the examples directory in the repository for more information.
//!
//! ### Web Framework Support
//!
//! Server transports (SSE and streamable HTTP) support both axum (default) and actix-web:
//!
//! ```rust,ignore
//! // Using actix-web (requires actix-web feature)
//! use rmcp::{ServiceExt, transport::SseServer};
//!
//! #[actix_web::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let server = SseServer::serve("127.0.0.1:8080".parse()?).await?;
//! let ct = server.with_service(Counter::new);
//! ct.cancelled().await;
//! Ok(())
//! }
//! ```
//!
//! ## Client
//!
//! A client can be used to interact with a server. Clients can be used to get a
Expand Down
2 changes: 1 addition & 1 deletion crates/rmcp/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub use auth::{AuthError, AuthorizationManager, AuthorizationSession, Authorized
pub mod streamable_http_server;
#[cfg(feature = "transport-streamable-http-server")]
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-server")))]
pub use streamable_http_server::tower::{StreamableHttpServerConfig, StreamableHttpService};
pub use streamable_http_server::{StreamableHttpServerConfig, StreamableHttpService};

#[cfg(feature = "transport-streamable-http-client")]
#[cfg_attr(docsrs, doc(cfg(feature = "transport-streamable-http-client")))]
Expand Down
1 change: 1 addition & 0 deletions crates/rmcp/src/transport/common/http_header.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub const HEADER_SESSION_ID: &str = "Mcp-Session-Id";
pub const HEADER_LAST_EVENT_ID: &str = "Last-Event-Id";
pub const HEADER_X_ACCEL_BUFFERING: &str = "X-Accel-Buffering";
pub const EVENT_STREAM_MIME_TYPE: &str = "text/event-stream";
pub const JSON_MIME_TYPE: &str = "application/json";
Loading
Loading