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

Conversation

JMLX42
Copy link

@JMLX42 JMLX42 commented Jul 1, 2025

The implementation maintains full backwards compatibility - when only the axum feature is enabled, the original Axum implementation is used.

  • 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

To Do

  • SSE server transport
  • HTTP streamable server transport

Motivation and Context

Add actix_web support to maximize the user base.

How Has This Been Tested?

Using the Python/JS test clients + unit tests mimicking the Axum impl.

Breaking Changes

None

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Axum is still the default impl. There is no breaking change. The actix-web impl must be enabled and default features disabled in order to use the actix-web impl.

- 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.
@github-actions github-actions bot added T-dependencies Dependencies related changes T-test Testing related changes T-config Configuration file changes T-core Core library changes T-examples Example code changes T-transport Transport layer changes labels Jul 1, 2025
@JMLX42 JMLX42 changed the title feat: add actix-web support for SSE server transport Add actix-web support Jul 1, 2025
The LocalSessionManager in streamable-http-server uses WorkerTransport,
which requires the transport-worker feature. This was working accidentally
in examples because transport-sse-server also includes transport-worker.

Without this fix, using transport-streamable-http-server independently
(without transport-sse-server) would fail to compile with unresolved
import errors for WorkerTransport.
@JMLX42 JMLX42 force-pushed the feature/actix-web branch 2 times, most recently from 4db400e to a0b4487 Compare July 1, 2025 15:27
@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

Testing the HTTP streamable impl

Node:

cargo test --test test_with_js test_with_js_streamable_http_client --features "server,client,transport-sse-server,transport-child-process,transport-streamable-http-server,transport-streamable-http-client,transport-sse-client,reqwest,actix-web,base64,macros" --no-default-features

Python: N/A

Testing the SSE impl

Node:

cargo test test_with_js_streamable_http_client_actix --features "server,client,transport-sse-server,transport-child-process,transport-streamable-http-server,transport-streamable-http-client,actix-web"

Python:

cargo test --test test_with_python test_with_python_client_actix --features "server,client,transport-sse-server,transport-child-process,transport-streamable-http-server,transport-streamable-http-client,actix-web"

@4t145 4t145 requested a review from Copilot July 1, 2025 16:09
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds optional actix-web support alongside the existing Axum implementation by refactoring SSE and streamable HTTP transports into a shared structure, re-exporting framework-specific types behind convenience aliases, and updating examples and tests to cover both frameworks.

  • Introduce actix-web-based implementations for SSE (ActixSseServer) and streamable HTTP (ActixStreamableHttpService) while preserving the Axum paths.
  • Refactor StreamableHttpServerConfig out of the tower module into a common location and update re-exports in transport.rs.
  • Extend examples (counter_sse_actix.rs, counter_streamable_http_actix.rs) and test suites (test_with_python.rs, test_with_js.rs, test_sse_server.rs) to exercise both Axum and actix-web flows.

Reviewed Changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
examples/servers/src/counter_streamable_http_actix.rs New example: streamable HTTP server using actix-web
examples/servers/src/counter_sse_actix.rs New example: SSE server using actix-web
examples/servers/Cargo.toml Enable actix-web optional dependencies and example registration
crates/rmcp/tests/test_with_python.rs Split Python-client test into common logic and framework variants
crates/rmcp/tests/test_with_js.rs Split JS-client test into common logic and add Axum/actix variants
crates/rmcp/src/transport/streamable_http_server/tower.rs Removed local config type; import shared StreamableHttpServerConfig
crates/rmcp/src/transport/streamable_http_server/actix_impl.rs New actix-web implementation for streamable HTTP service
crates/rmcp/src/transport/streamable_http_server.rs Add shared config, re-export convenience and explicit types
crates/rmcp/src/transport/sse_server/mod.rs Define module structure and convenience alias for SseServer
crates/rmcp/src/transport/sse_server/common.rs Extract SseServerConfig, SessionId, default keep-alive
crates/rmcp/src/transport/sse_server/axum_impl.rs Refactor axum SSE implementation to use shared common types
crates/rmcp/src/transport/sse_server/actix_impl.rs Add new actix-web SSE implementation
crates/rmcp/src/transport/common/http_header.rs Add HEADER_X_ACCEL_BUFFERING constant
crates/rmcp/src/transport.rs Update docs and re-exports to cover both Axum and actix-web
crates/rmcp/Cargo.toml Enable actix-web and async-stream dependencies and features
Comments suppressed due to low confidence (2)

crates/rmcp/tests/test_with_js.rs:102

  • This Axum JS client test does not invoke init_js_tests().await, so the environment and dependencies (e.g. npm install) might not be set up before the test runs. Consider calling the common initializer at the start of the test.
async fn test_with_js_streamable_http_client_axum() -> anyhow::Result<()> {

crates/rmcp/src/transport/streamable_http_server/actix_impl.rs:1

  • [nitpick] This actix-web streamable HTTP implementation is nearly 500 lines long. Splitting route handlers (handle_get, handle_post, handle_delete) into smaller modules or files could improve readability and ease future maintenance.
use std::sync::Arc;

required-features = ["actix-web"]

[features]
actix-web = ["dep:actix-web", "dep:actix-rt", "rmcp/actix-web"]
Copy link
Preview

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entry "rmcp/actix-web" should be prefixed with "dep:" (i.e., "dep:rmcp/actix-web") so that the example’s rmcp dependency feature is correctly enabled.

Suggested change
actix-web = ["dep:actix-web", "dep:actix-rt", "rmcp/actix-web"]
actix-web = ["dep:actix-web", "dep:actix-rt", "dep:rmcp/actix-web"]

Copilot uses AI. Check for mistakes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entry "rmcp/actix-web" should be prefixed with "dep:" (i.e., "dep:rmcp/actix-web") so that the example’s rmcp dependency feature is correctly enabled.

rmcp/actix-web is not a dependency. actix-web is a feature of rmcp. IMHO the suggestion is invalid.

JMLX42 added 2 commits July 1, 2025 18:23
- Implement ActixStreamableHttpService with full feature parity to Axum implementation
- Support GET (SSE streams), POST (requests), DELETE (session cleanup) endpoints
- Add comprehensive logging matching SSE implementation
- Use X-Accel-Buffering header constant for consistency
- Create working example server (counter_streamable_http_actix.rs)
- Refactor tower.rs to import StreamableHttpServerConfig from parent module
- Add HEADER_X_ACCEL_BUFFERING constant to common http_header module
- Update streamable_client.js to accept URL as command line argument
- Configure example to bind to 127.0.0.1 for IPv4 compatibility

The actix-web implementation provides identical functionality to the Axum version
while following actix-web patterns and conventions.
- Export framework-specific types with explicit names (AxumSseServer, ActixSseServer, etc.)
- Add comprehensive module documentation explaining the type export strategy
- Update all tests to use explicit type names with proper feature gates
- Remove unused TransportReceiver type aliases to fix warnings
- Ensure feature gates are ordered with framework feature last

This design provides both explicit control when both frameworks are enabled
and maintains backward compatibility with existing code.
@JMLX42 JMLX42 force-pushed the feature/actix-web branch from a0b4487 to cd86d78 Compare July 1, 2025 16:34
- Use #[actix_web::test] instead of #[tokio::test] for actix-web tests
- Add NormalizePath middleware to actix-web SSE server for consistent path handling
@JMLX42 JMLX42 force-pushed the feature/actix-web branch from cd86d78 to 82d64b7 Compare July 1, 2025 16:51
JMLX42 added 5 commits July 1, 2025 18:56
Only define the constant when actix-web feature is enabled to avoid dead code warning
The JavaScript streamable server is hardcoded to use port 8002, which was
conflicting with our actix-web test. Changed actix-web test to use port 8004.
Reverted STREAMABLE_HTTP_JS_BIND_ADDRESS back to port 8002 to match
the hardcoded port in streamable_server.js. Our actix-web test uses
port 8004 to avoid conflicts.
The feature definition in examples/servers/Cargo.toml should use
'dep:rmcp/actix-web' to correctly enable the rmcp dependency feature.
…ort types

- Remove type prefixes (AxumSseServer, ActixSseServer, etc.)
- Organize implementations in framework-specific submodules (axum, actix_web)
- Each submodule exports types with consistent names (SseServer, StreamableHttpService)
- Update all imports to use new module paths
- Maintain backward compatibility with convenience aliases at module roots

This provides a cleaner API with better module organization and makes it easier
to add new framework implementations in the future.
@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

When running cargo test, I noticed an error log.

The error message:

  ERROR rmcp::transport::streamable_http_client: fail to delete session: Client error: error sending
   request for url (http://127.0.0.1:8002/mcp)

This error is expected and not caused by changes in this branch.

Here's why:

  1. The error occurs during cleanup: When the streamable HTTP client is shutting down, it tries to
    delete the session by sending a DELETE request to the server.
  2. The JS server is already killed: In the test test_with_js_streamable_http_server, the sequence
    is:
    let quit_reason = client.cancel().await?;
    server.kill().await?;
  3. The client cancellation triggers the session deletion attempt, but by the time it tries to send
    the DELETE request, the server process has already been killed.
  4. The DELETE endpoint exists: The JavaScript server does have a DELETE endpoint handler, but the
    server is no longer running when the cleanup happens.
  5. This behavior exists on main branch: The DELETE endpoint and cleanup logic are not new - they
    exist on the main branch as well.
  6. It's logged as an error but doesn't fail the test: The error is logged but handled gracefully.
    The test still passes because:
    - The session deletion is a best-effort cleanup operation
    - The error occurs after the main test logic has completed successfully

This is a timing issue where the cleanup happens after the server is terminated. It's not a
functional problem and doesn't affect the correctness of the tests or the library functionality.

- Document actix-web as an alternative web framework in README files
- Add feature flag documentation explaining axum (default) vs actix-web
- Include examples showing both framework usage patterns
- Document precedence behavior when both features are enabled
- Add explanatory comments to actix-web example files
- Note runtime differences (#[actix_web::main] vs #[tokio::main])
@github-actions github-actions bot added the T-documentation Documentation improvements label Jul 1, 2025
@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

IMHO everything checks out and this PR is ready for review. 🚀

@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

Wooohoooo that CI fail is brutal. I'm on it 👍

@4t145
Copy link
Collaborator

4t145 commented Jul 1, 2025

Don't mind commit message ci, we will merge all commit into one when merge the pr. As for clippy and format, you can use just fix. As for test failure, I will check them tomorrow. I just click the run ci button casually.

@4t145
Copy link
Collaborator

4t145 commented Jul 1, 2025

And I also want to disscuss that should we maintain actix-web service in rmcp crate? If every web framework add their implementation in rmcp crate, it would be a challenge to maintain all of them. Will it be a good idea to seperate them to other crates? We can also add support like grpc and ws transport in this way.

@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

Will it be a good idea to seperate them to other crates?

At some point, I wanted to use a trait for both the Axum and the actix_web implementations. But there were too dissimilar, mainly because each framework has it's own way to define routes/handlers. And I felt it was too early. Maybe a third leg would help designing the needed abstractions/traits.

Maybe we don't need abstractions at all and the actix_web module introduced by this PR could simply be split away as it is.

Still, a few things to consider:

  1. Moving framework specific code into separate crate would make the build faster. But I'm not sure there is a lot to gain here. If that's the only goal, IMHO it's not worth it as it is. But benchmarking build times might prove me wrong.
  2. Separate crates in this monorepo still means maintained in this monorepo and eventually by the monorepo team. IMHO that's not the goal here. But I'm integrating MCP deep inside an open source product. So I guess me/my org could maintain a separate crate/repo for the actix_web impl.
  3. One core reference implementation must be kept in this repo. I used Claude Code on the Axum implementation to make the actix_web one. If there is a well maintained core implementation that keeps track with the MCP protocol, it should be easy enough to maintain other frameworks separately.

Apply Rust formatting standards to ensure consistent code style across
the codebase.
@JMLX42
Copy link
Author

JMLX42 commented Jul 1, 2025

As for clippy and format, you can use just fix.

@4t145 all done.

@4t145
Copy link
Collaborator

4t145 commented Jul 2, 2025

@JMLX42 It would be great if you are willing to maintain a repo to support actix-web for rcmp, and we can list the ecosystem here in rmcp. It's very hard to make a single piece of code compatible with all major web frameworks. After all they don't even depends on a same basic http type lib. It will be much better if there is some adapter to accept tower http service as other framwork's route, and the core work of it is the convertion bewteen http and different http basic type lib like actix-http.

So would you like maintain a crate, maybe named rmcp-actix-web, yourselves?

@JMLX42
Copy link
Author

JMLX42 commented Jul 2, 2025

It will be much better if there is some adapter to accept tower http service as other framwork's route

IMHO that's most of the work here: routing to the service. I'll double check the code, but if that's all there is (and the unavoidable framework specific boilerplate), it's good enough for quite some time.

So would you like maintain a crate, maybe named rmcp-actix-web, yourselves?

@4t145 OK so new plan:

  1. I move my code to a separate rmcp-actix-web crate. I move the new impl, tests, CI, etc. And rely on the crate naming to differentiate the framework implementations (instead of module names) but I keep the same module/type names so most of the documentation, examples and stuff are consistent.
  2. I close this MR and open a new one to update the doc to mention the new crate.

I'll do it this afternoon.

@JMLX42
Copy link
Author

JMLX42 commented Jul 2, 2025

First version: https://gitlab.com/lx-industries/rmcp-actix-web
Not released/published yet.

Still working on the CI/release system.

@JMLX42
Copy link
Author

JMLX42 commented Jul 5, 2025

First release: https://crates.io/crates/rmcp-actix-web

I'll close this PR and open another one just to update the doc to add a link to the new crate.

@JMLX42 JMLX42 closed this Jul 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-config Configuration file changes T-core Core library changes T-dependencies Dependencies related changes T-documentation Documentation improvements T-examples Example code changes T-test Testing related changes T-transport Transport layer changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants