├── .cargo └── config.toml ├── ARCHITECTURE.md ├── CHANGELOG.md ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── MIGRATION.md ├── Makefile ├── README.md ├── benches ├── README.md ├── integration │ └── end_to_end_benchmark.rs └── regression │ └── performance_regression_detector.rs ├── crates ├── README.md ├── turbomcp-auth │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── config.rs │ │ ├── lib.rs │ │ ├── manager.rs │ │ ├── oauth2 │ │ ├── client.rs │ │ ├── mod.rs │ │ └── validation.rs │ │ ├── providers │ │ ├── api_key.rs │ │ └── mod.rs │ │ └── types.rs ├── turbomcp-cli │ ├── ARCHITECTURE.md │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── src │ │ ├── cli.rs │ │ ├── error.rs │ │ ├── executor.rs │ │ ├── formatter.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── prelude.rs │ │ └── transport.rs │ └── tests_legacy │ │ ├── cli_structure_tests.rs │ │ ├── functional_cli_tests.rs │ │ ├── http_functionality_tests.rs │ │ ├── integration_test.rs │ │ ├── main_integration_tests.rs │ │ └── main_tests.rs ├── turbomcp-client │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── src │ │ ├── client │ │ │ ├── builder.rs │ │ │ ├── config.rs │ │ │ ├── core.rs │ │ │ ├── dispatcher.rs │ │ │ ├── manager.rs │ │ │ ├── mod.rs │ │ │ ├── operations │ │ │ │ ├── completion.rs │ │ │ │ ├── connection.rs │ │ │ │ ├── handlers.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── plugins.rs │ │ │ │ ├── prompts.rs │ │ │ │ ├── resources.rs │ │ │ │ ├── sampling.rs │ │ │ │ └── tools.rs │ │ │ └── protocol.rs │ │ ├── handlers.rs │ │ ├── lib.rs │ │ ├── plugins │ │ │ ├── core.rs │ │ │ ├── examples.rs │ │ │ ├── macros.rs │ │ │ ├── middleware.rs │ │ │ ├── mod.rs │ │ │ └── registry.rs │ │ ├── prelude.rs │ │ └── sampling.rs │ └── tests │ │ ├── bidirectional_test.rs │ │ ├── cli_tests.rs │ │ └── concurrency_config_test.rs ├── turbomcp-dpop │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── errors.rs │ │ ├── hsm │ │ ├── common.rs │ │ ├── mod.rs │ │ ├── pkcs11.rs │ │ └── yubihsm.rs │ │ ├── keys.rs │ │ ├── lib.rs │ │ ├── proof.rs │ │ ├── redis_storage.rs │ │ ├── test_utils.rs │ │ └── types.rs ├── turbomcp-macros │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── src │ │ ├── attrs.rs │ │ ├── bidirectional_wrapper.rs │ │ ├── compile_time_router.rs │ │ ├── completion.rs │ │ ├── context_aware_dispatch.rs │ │ ├── elicitation.rs │ │ ├── helpers.rs │ │ ├── lib.rs │ │ ├── ping.rs │ │ ├── prompt.rs │ │ ├── resource.rs │ │ ├── schema.rs │ │ ├── server.rs │ │ ├── template.rs │ │ ├── tool.rs │ │ ├── tool_router.rs │ │ ├── uri_template.rs │ │ └── utils.rs │ └── tests │ │ ├── compile_fail │ │ ├── stdio_print_rejected.rs │ │ ├── stdio_print_rejected.stderr │ │ ├── stdio_println_rejected.rs │ │ └── stdio_println_rejected.stderr │ │ ├── error_integration_tests.rs │ │ ├── helper_macro_tests.rs │ │ └── stdio_safety_tests.rs ├── turbomcp-protocol │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── src │ │ ├── capabilities.rs │ │ ├── capabilities │ │ │ └── tests.rs │ │ ├── config.rs │ │ ├── context │ │ │ ├── capabilities.rs │ │ │ ├── client.rs │ │ │ ├── completion.rs │ │ │ ├── elicitation.rs │ │ │ ├── mod.rs │ │ │ ├── ping.rs │ │ │ ├── request.rs │ │ │ ├── server_initiated.rs │ │ │ └── templates.rs │ │ ├── enhanced_registry.rs │ │ ├── error.rs │ │ ├── error_utils.rs │ │ ├── handlers.rs │ │ ├── jsonrpc.rs │ │ ├── jsonrpc │ │ │ └── tests.rs │ │ ├── lib.rs │ │ ├── lock_free.rs │ │ ├── message.rs │ │ ├── registry.rs │ │ ├── security │ │ │ ├── mod.rs │ │ │ └── validation.rs │ │ ├── session.rs │ │ ├── shared.rs │ │ ├── state.rs │ │ ├── test_helpers.rs │ │ ├── types │ │ │ ├── capabilities.rs │ │ │ ├── completion.rs │ │ │ ├── content.rs │ │ │ ├── core.rs │ │ │ ├── domain.rs │ │ │ ├── elicitation.rs │ │ │ ├── initialization.rs │ │ │ ├── logging.rs │ │ │ ├── mod.rs │ │ │ ├── ping.rs │ │ │ ├── prompts.rs │ │ │ ├── requests.rs │ │ │ ├── resources.rs │ │ │ ├── roots.rs │ │ │ ├── sampling.rs │ │ │ ├── tests.rs │ │ │ └── tools.rs │ │ ├── utils.rs │ │ ├── validation.rs │ │ ├── validation │ │ │ └── tests.rs │ │ ├── versioning.rs │ │ ├── versioning │ │ │ └── tests.rs │ │ └── zero_copy.rs │ └── tests │ │ ├── mcp_compliance_comprehensive_tests.rs │ │ └── mcp_compliance_validation_tests.rs ├── turbomcp-server │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── TEST_CONSOLIDATION.md │ ├── benches │ │ └── middleware_benchmarks.rs │ ├── src │ │ ├── ELICITATION_MIGRATION_COMPLETE.md │ │ ├── ELICITATION_MIGRATION_STATUS.md │ │ ├── capabilities.rs │ │ ├── config.rs │ │ ├── config │ │ │ └── tests.rs │ │ ├── elicitation.rs │ │ ├── error.rs │ │ ├── error │ │ │ └── tests.rs │ │ ├── handlers │ │ │ ├── composite.rs │ │ │ ├── implementations │ │ │ │ ├── function_tool.rs │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── traits │ │ │ │ ├── completion.rs │ │ │ │ ├── elicitation.rs │ │ │ │ ├── logging.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── ping.rs │ │ │ │ ├── prompt.rs │ │ │ │ ├── resource.rs │ │ │ │ ├── resource_template.rs │ │ │ │ ├── sampling.rs │ │ │ │ └── tool.rs │ │ │ ├── utils.rs │ │ │ └── wrapper.rs │ │ ├── lib.rs │ │ ├── lifecycle.rs │ │ ├── lifecycle │ │ │ └── tests.rs │ │ ├── main.rs │ │ ├── metrics.rs │ │ ├── metrics │ │ │ └── tests.rs │ │ ├── middleware │ │ │ ├── audit.rs │ │ │ ├── auth.rs │ │ │ ├── mod.rs │ │ │ ├── rate_limit.rs │ │ │ ├── security.rs │ │ │ ├── tests.rs │ │ │ ├── timeout.rs │ │ │ └── validation.rs │ │ ├── observability.rs │ │ ├── policies │ │ │ ├── rbac_model.conf │ │ │ └── rbac_policy.csv │ │ ├── registry.rs │ │ ├── registry │ │ │ └── tests.rs │ │ ├── routing │ │ │ ├── bidirectional.rs │ │ │ ├── config.rs │ │ │ ├── handlers │ │ │ │ ├── completion.rs │ │ │ │ ├── elicitation.rs │ │ │ │ ├── initialize.rs │ │ │ │ ├── logging.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── ping.rs │ │ │ │ ├── prompts.rs │ │ │ │ ├── resources.rs │ │ │ │ ├── roots.rs │ │ │ │ ├── sampling.rs │ │ │ │ └── tools.rs │ │ │ ├── mod.rs │ │ │ ├── tests.rs │ │ │ ├── traits.rs │ │ │ ├── utils.rs │ │ │ └── validation.rs │ │ ├── runtime.rs │ │ ├── runtime │ │ │ ├── http.rs │ │ │ └── websocket.rs │ │ ├── sampling.rs │ │ ├── schemas │ │ │ ├── mcp_2025-06-18.json │ │ │ ├── mcp_core.json │ │ │ └── mcp_tools.json │ │ ├── server │ │ │ ├── builder.rs │ │ │ ├── core.rs │ │ │ ├── mod.rs │ │ │ ├── shutdown.rs │ │ │ └── transport.rs │ │ ├── service.rs │ │ └── timeout.rs │ └── tests │ │ ├── common │ │ └── mod.rs │ │ ├── middleware_integration_tests.rs │ │ ├── server_tests.rs │ │ ├── stdio_lifecycle_test.rs │ │ ├── test_helpers.rs │ │ ├── timeout_integration_tests.rs │ │ └── websocket_config_test.rs ├── turbomcp-transport │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── SECURITY_FEATURES.md │ ├── src │ │ ├── axum │ │ │ ├── config │ │ │ │ ├── auth.rs │ │ │ │ ├── cors.rs │ │ │ │ ├── environment.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── rate_limit.rs │ │ │ │ ├── security.rs │ │ │ │ ├── server.rs │ │ │ │ └── tls.rs │ │ │ ├── extension.rs │ │ │ ├── handlers │ │ │ │ ├── capabilities.rs │ │ │ │ ├── health.rs │ │ │ │ ├── json_rpc.rs │ │ │ │ ├── metrics.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── root.rs │ │ │ │ ├── sse.rs │ │ │ │ └── websocket.rs │ │ │ ├── middleware │ │ │ │ ├── auth.rs │ │ │ │ ├── mcp.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── rate_limit.rs │ │ │ │ └── security.rs │ │ │ ├── mod.rs │ │ │ ├── query │ │ │ │ ├── json_rpc.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── sse.rs │ │ │ │ └── websocket.rs │ │ │ ├── router │ │ │ │ ├── builder.rs │ │ │ │ ├── extension.rs │ │ │ │ └── mod.rs │ │ │ ├── service │ │ │ │ ├── interface.rs │ │ │ │ ├── mod.rs │ │ │ │ └── state.rs │ │ │ ├── tests │ │ │ │ ├── config.rs │ │ │ │ ├── integration.rs │ │ │ │ ├── mod.rs │ │ │ │ └── router.rs │ │ │ └── types │ │ │ │ ├── json_rpc.rs │ │ │ │ ├── mod.rs │ │ │ │ └── query.rs │ │ ├── axum_integration.rs │ │ ├── bidirectional.rs │ │ ├── child_process.rs │ │ ├── compression.rs │ │ ├── config.rs │ │ ├── core.rs │ │ ├── lib.rs │ │ ├── metrics.rs │ │ ├── resilience │ │ │ ├── circuit_breaker.rs │ │ │ ├── deduplication.rs │ │ │ ├── health.rs │ │ │ ├── metrics.rs │ │ │ ├── mod.rs │ │ │ ├── retry.rs │ │ │ └── transport.rs │ │ ├── security │ │ │ ├── auth.rs │ │ │ ├── builder.rs │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ ├── origin.rs │ │ │ ├── rate_limit.rs │ │ │ ├── session.rs │ │ │ ├── utils.rs │ │ │ └── validator.rs │ │ ├── server.rs │ │ ├── shared.rs │ │ ├── stdio.rs │ │ ├── streamable_http_client.rs │ │ ├── streamable_http_v2.rs │ │ ├── tcp.rs │ │ ├── test_env.rs │ │ ├── tower.rs │ │ ├── transport_metrics_metadata │ │ │ └── tests.rs │ │ ├── unix.rs │ │ └── websocket_bidirectional │ │ │ ├── bidirectional.rs │ │ │ ├── config.rs │ │ │ ├── connection.rs │ │ │ ├── elicitation.rs │ │ │ ├── mcp_methods.rs │ │ │ ├── mod.rs │ │ │ ├── tasks.rs │ │ │ ├── transport.rs │ │ │ └── types.rs │ └── tests │ │ ├── comprehensive_security_tests.rs │ │ ├── compression_tests.rs │ │ ├── http_endpoint_url_regression_test.rs │ │ ├── long_running_connection_tests.rs │ │ ├── sampling_rejection_hang_test.rs │ │ ├── streamable_http_client_tests.rs │ │ ├── tcp_tests.rs │ │ ├── unix_tests.rs │ │ └── websocket_bidirectional_integration_test.rs └── turbomcp │ ├── CONTRIBUTING.md │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── benches │ └── performance_tests.rs │ ├── examples │ ├── README.md │ ├── basic_client.rs │ ├── comprehensive.rs │ ├── elicitation_client.rs │ ├── elicitation_server.rs │ ├── hello_world.rs │ ├── http_app.rs │ ├── http_client_simple.rs │ ├── http_server.rs │ ├── macro_server.rs │ ├── minimal_test.rs │ ├── resources.rs │ ├── rich_tool_descriptions.rs │ ├── sampling_server.rs │ ├── stateful.rs │ ├── stdio_app.rs │ ├── stdio_client.rs │ ├── stdio_output_verification.rs │ ├── stdio_server.rs │ ├── tcp_client.rs │ ├── tcp_server.rs │ ├── tools.rs │ ├── transports_demo.rs │ ├── unix_client.rs │ ├── unix_server.rs │ ├── validation.rs │ ├── websocket_client_simple.rs │ └── websocket_server.rs │ ├── src │ ├── context.rs │ ├── context_factory.rs │ ├── elicitation.rs │ ├── elicitation_api.rs │ ├── helpers.rs │ ├── injection.rs │ ├── lib.rs │ ├── lifespan.rs │ ├── macros.rs │ ├── registry.rs │ ├── registry │ │ └── tests.rs │ ├── router.rs │ ├── runtime │ │ └── mod.rs │ ├── schema.rs │ ├── server.rs │ ├── session.rs │ ├── simd.rs │ ├── sse_server.rs │ ├── structured.rs │ ├── test_utils.rs │ ├── transport.rs │ ├── uri.rs │ └── validation.rs │ └── tests │ ├── complete_feature_coverage_test.rs │ ├── completion_integration_test.rs │ ├── elicitation_integration_test.rs │ ├── integration_examples_tests.rs │ ├── lifespan_coverage.rs │ ├── macro_dogfood_tests.rs │ ├── macro_end_to_end_tests.rs │ ├── mcp_protocol_compliance_comprehensive.rs │ ├── real_end_to_end_working_examples.rs │ ├── resource_subscription_comprehensive_test.rs │ ├── roots_integration_test.rs │ ├── runtime_configuration_tests.rs │ ├── sampling_integration_test.rs │ ├── streamable_http_v2_bug_fixes.rs │ ├── transport_integration_tests.rs │ ├── transport_protocol_compliance_test.rs │ ├── websocket_integration_tests.rs │ └── zero_tolerance_enforcement.rs ├── demo ├── COMPILATION_STATUS.md ├── Cargo.toml ├── MCP_SETUP.md ├── README.md ├── lmstudio-config.json ├── plugin_execution_patterns.rs └── src │ └── main.rs ├── deny.toml ├── docker-compose.yml ├── examples ├── config │ ├── README.md │ ├── minimal.toml │ ├── server.toml │ └── server.yaml ├── type_state_builders_demo.rs ├── websocket_dogfood_client.rs └── websocket_dogfood_server.rs ├── scripts ├── README.md ├── check-versions.sh ├── coverage.sh ├── prepare-release.sh ├── publish.sh ├── run_benchmarks.sh ├── run_comprehensive_tests.sh ├── run_tests.sh ├── test-feature-combinations.sh └── update-versions.sh ├── tarpaulin.toml ├── test-config.toml ├── tests ├── benchmark_tests.rs ├── coverage_tests.rs ├── error_tests.rs ├── external_dependency_integration.rs ├── fault_tolerance_tests.rs ├── integration.rs ├── integration │ ├── auth_flows_test.rs │ ├── error_propagation_fault_injection_test.rs │ ├── macro_system_test.rs │ ├── macro_system_test_comprehensive.rs │ ├── protocol_validation_test.rs │ ├── server_middleware_routing_test.rs │ ├── transport_implementations_test.rs │ └── transport_resilience_test.rs ├── mcp_authorization_compliance_tests.rs ├── mcp_automated_spec_tracking_system.rs ├── mcp_basic_protocol_compliance.rs ├── mcp_client_features_compliance_tests.rs ├── mcp_master_compliance_test_suite.rs ├── mcp_schema_compliance_tests.rs ├── mcp_server_features_compliance_tests.rs ├── mcp_tools_compliance_tests.rs ├── mcp_transport_compliance_tests.rs ├── mcp_utilities_compliance_tests.rs ├── performance │ └── performance_concurrency_test.rs ├── property │ └── property_based_tests.rs ├── property_tests.rs └── state_tests.rs └── trait_audit_report.json /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # TurboMCP Cargo Configuration 2 | 3 | [build] 4 | # Enable optimizations for tests in dev mode 5 | rustflags = ["-C", "opt-level=1"] 6 | 7 | [target.'cfg(target_os = "linux")'] 8 | # Linux-specific settings 9 | rustflags = ["-C", "link-arg=-Wl,--gc-sections"] 10 | 11 | [target.'cfg(target_os = "macos")'] 12 | # macOS-specific settings 13 | rustflags = ["-C", "link-arg=-Wl,-dead_strip"] 14 | 15 | [target.'cfg(windows)'] 16 | # Windows-specific settings 17 | rustflags = ["-C", "link-args=/OPT:REF"] 18 | 19 | [alias] 20 | # Convenient aliases for common development tasks 21 | 22 | # Testing aliases 23 | test-unit = "test --lib --all-features" 24 | test-integration = "test --test '*' --all-features" 25 | test-doc = "test --doc --all-features" 26 | test-all = "test --all-features" 27 | test-quick = "test --lib --bins" 28 | test-verbose = "test --all-features -- --nocapture" 29 | 30 | # Build aliases 31 | build-all = "build --all-features" 32 | build-release = "build --release --all-features" 33 | build-examples = "build --examples --all-features" 34 | 35 | # Check aliases 36 | check-all = "check --all-targets --all-features" 37 | check-format = "fmt --all -- --check" 38 | check-clippy = "clippy --all-targets --all-features -- -D warnings" 39 | 40 | # Documentation aliases 41 | doc-open = "doc --open --all-features --no-deps" 42 | doc-private = "doc --all-features --document-private-items" 43 | 44 | # TurboMCP specific aliases 45 | turbomcp-demo = "run --example turbomcp_demo --features turbomcp" 46 | simple-server = "run --example simple_server" 47 | 48 | # Development workflow aliases 49 | dev-check = ["check-format", "check-clippy", "test-unit"] 50 | dev-test = ["test-unit", "test-integration"] 51 | dev-full = ["check-all", "test-all", "build-examples"] 52 | 53 | # Release workflow aliases 54 | release-check = ["build-release", "test-all", "doc-private"] 55 | 56 | [env] 57 | # Environment variables for tests and development 58 | RUST_BACKTRACE = "1" 59 | RUST_LOG = "debug" 60 | 61 | # TurboMCP specific environment 62 | TURBOMCP_TEST_MODE = "true" 63 | TURBOMCP_LOG_LEVEL = "info" -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest AS chef 2 | WORKDIR /app 3 | # Install cargo-chef for dependency caching 4 | RUN cargo install cargo-chef 5 | # Copy manifests and source (context is reduced via .dockerignore) 6 | COPY . . 7 | # Compute the dependency graph recipe 8 | RUN cargo chef prepare --recipe-path recipe.json 9 | 10 | FROM rust:latest AS builder 11 | WORKDIR /app 12 | RUN cargo install cargo-chef 13 | # Leverage cached dependency layers 14 | COPY --from=chef /app/recipe.json recipe.json 15 | RUN cargo chef cook --release --workspace --recipe-path recipe.json 16 | # Now copy the full source and build 17 | COPY . . 18 | # Enable sccache for faster rebuilds 19 | RUN cargo install sccache && \ 20 | echo '[build]\nrustc-wrapper = "sccache"' >> /usr/local/cargo/config.toml && \ 21 | sccache --version 22 | # Build workspace; skip macro crate to avoid rebuilding proc-macros unnecessarily in runtime image 23 | RUN cargo build --release --workspace --exclude turbomcp --exclude turbomcp-macros 24 | 25 | # Runtime image: run server binary 26 | FROM debian:stable-slim 27 | RUN apt-get update \ 28 | && apt-get install -y --no-install-recommends ca-certificates tzdata curl git adduser \ 29 | && adduser --system --group --no-create-home mcp \ 30 | && rm -rf /var/lib/apt/lists/* 31 | USER mcp:mcp 32 | WORKDIR /home/mcp 33 | ENV RUST_LOG=info 34 | # Copy compiled turbomcp-server binary 35 | COPY --from=builder /app/target/release/turbomcp-server /usr/local/bin/turbomcp-server 36 | ENTRYPOINT ["/usr/local/bin/turbomcp-server"] 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-auth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-auth" 3 | version = "2.0.5" 4 | edition = "2024" 5 | authors = ["Nicholas Paterno "] 6 | description = "OAuth 2.1 and authentication for TurboMCP with MCP protocol compliance" 7 | license = "MIT" 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | documentation = "https://docs.rs/turbomcp-auth" 11 | readme = "README.md" 12 | keywords = ["oauth", "authentication", "mcp", "security", "api-key"] 13 | categories = ["authentication", "web-programming", "network-programming"] 14 | rust-version = "1.89.0" 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | # Core dependencies 21 | tokio = { workspace = true } 22 | serde = { version = "1.0", features = ["derive"] } 23 | serde_json = "1.0" 24 | async-trait = "0.1" 25 | thiserror = "2.0.16" 26 | tracing = "0.1" 27 | 28 | # OAuth 2.1 dependencies 29 | oauth2 = "4.4.2" 30 | base64 = "0.22" 31 | sha2 = "0.10" 32 | urlencoding = "2.1" 33 | url = "2.5" 34 | 35 | # HTTP types 36 | http = "1.0" 37 | 38 | # Time handling 39 | chrono = { version = "0.4", features = ["serde"] } 40 | 41 | # UUID for session/token management 42 | uuid = { version = "1.10", features = ["v4", "v7", "serde"] } 43 | 44 | # Secret management 45 | secrecy = "0.8.0" 46 | 47 | # HTTP client for OAuth flows 48 | reqwest = { version = "0.12", features = ["json"] } 49 | 50 | # Utilities 51 | once_cell = "1.19" 52 | 53 | # Internal dependencies 54 | turbomcp-protocol = { version = "2.0.5", path = "../turbomcp-protocol" } 55 | 56 | # Optional: DPoP support 57 | turbomcp-dpop = { version = "2.0.5", path = "../turbomcp-dpop", optional = true } 58 | 59 | [dev-dependencies] 60 | tokio-test = "0.4" 61 | tempfile = "3.0" 62 | 63 | [features] 64 | default = [] 65 | 66 | # DPoP support (RFC 9449) 67 | dpop = ["dep:turbomcp-dpop"] 68 | 69 | [package.metadata.docs.rs] 70 | all-features = true 71 | rustdoc-args = ["--cfg", "docsrs"] 72 | -------------------------------------------------------------------------------- /crates/turbomcp-auth/README.md: -------------------------------------------------------------------------------- 1 | # TurboMCP Auth 2 | 3 | OAuth 2.1 and authentication for TurboMCP with MCP protocol compliance. 4 | 5 | ## Features 6 | 7 | - **OAuth 2.1** - RFC 8707/9728/7591 compliant with MCP resource binding 8 | - **Multi-Provider** - Google, GitHub, Microsoft, GitLab with PKCE 9 | - **API Key Auth** - Simple API key authentication 10 | - **Session Management** - Secure token management with configurable storage 11 | - **DPoP Support** - Optional RFC 9449 proof-of-possession tokens 12 | 13 | ## Usage 14 | 15 | ```toml 16 | [dependencies] 17 | turbomcp-auth = "2.0.4" 18 | 19 | # With DPoP support 20 | turbomcp-auth = { version = "2.0.4", features = ["dpop"] } 21 | ``` 22 | 23 | ## Feature Flags 24 | 25 | - `default` - Core authentication (no optional features) 26 | - `dpop` - Enable DPoP (RFC 9449) token binding support via `turbomcp-dpop` 27 | 28 | ## Architecture 29 | 30 | This crate provides: 31 | 32 | - **Authentication Manager** - Coordinates multiple authentication providers 33 | - **OAuth 2.1 Client** - Supports Authorization Code, Client Credentials, and Device flows 34 | - **API Key Provider** - Simple API key-based authentication 35 | - **Session Management** - Token storage and lifecycle management 36 | - **RFC Compliance** - Resource Indicators (RFC 8707), Protected Resource Metadata (RFC 9728), Dynamic Client Registration (RFC 7591) 37 | 38 | See the [module documentation](https://docs.rs/turbomcp-auth) for detailed usage examples. 39 | 40 | ## License 41 | 42 | MIT 43 | -------------------------------------------------------------------------------- /crates/turbomcp-auth/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # TurboMCP Auth - OAuth 2.1 and Authentication 2 | //! 3 | //! OAuth 2.1, API key authentication, and authorization functionality for the 4 | //! TurboMCP protocol with MCP specification compliance. 5 | //! 6 | //! ## Features 7 | //! 8 | //! - **OAuth 2.1** - RFC 8707/9728/7591 compliant with MCP resource binding 9 | //! - **Multi-Provider** - Google, GitHub, Microsoft, GitLab with PKCE 10 | //! - **API Key Auth** - Simple API key authentication provider 11 | //! - **Session Management** - Token storage and lifecycle management 12 | //! - **DPoP Support** - Optional RFC 9449 proof-of-possession tokens (feature: `dpop`) 13 | //! 14 | //! ## Architecture 15 | //! 16 | //! - `config` - Configuration types for authentication providers 17 | //! - `types` - Core authentication types (AuthContext, UserInfo, TokenInfo) 18 | //! - `providers` - Authentication provider implementations 19 | //! - `api_key` - API key authentication 20 | //! - `manager` - Authentication manager for provider orchestration 21 | //! - `oauth2` - OAuth 2.1 client with authorization flows 22 | //! 23 | //! ## Feature Flags 24 | //! 25 | //! - `default` - Core authentication (no optional features) 26 | //! - `dpop` - Enable DPoP (RFC 9449) token binding support via `turbomcp-dpop` 27 | 28 | // Submodules 29 | pub mod config; 30 | pub mod manager; 31 | pub mod oauth2; 32 | pub mod providers; 33 | pub mod types; 34 | 35 | // Re-export configuration types 36 | #[doc(inline)] 37 | pub use config::*; 38 | 39 | // Re-export core types 40 | #[doc(inline)] 41 | pub use types::*; 42 | 43 | // Re-export providers 44 | #[doc(inline)] 45 | pub use providers::*; 46 | 47 | // Re-export manager 48 | #[doc(inline)] 49 | pub use manager::AuthManager; 50 | 51 | // Re-export DPoP types when feature is enabled 52 | #[cfg(feature = "dpop")] 53 | pub use turbomcp_dpop as dpop; 54 | -------------------------------------------------------------------------------- /crates/turbomcp-auth/src/oauth2/mod.rs: -------------------------------------------------------------------------------- 1 | //! OAuth 2.1 Implementation 2 | //! 3 | //! This module provides an OAuth 2.1 implementation with: 4 | //! - Authorization Code flow with PKCE (RFC 7636) 5 | //! - Refresh tokens 6 | //! - Resource Indicators (RFC 8707) 7 | //! - Protected Resource Metadata (RFC 9728) 8 | //! - Dynamic Client Registration (RFC 7591) 9 | //! - DPoP integration (RFC 9449) 10 | //! 11 | //! ## Submodules 12 | //! 13 | //! - `client` - OAuth2Client for basic operations 14 | //! - `authorization` - Authorization flow logic 15 | //! - `token` - Token management and refresh 16 | //! - `validation` - URI and security validation 17 | //! - `rfc_compliance` - RFC-specific implementations 18 | 19 | pub mod client; 20 | pub mod validation; 21 | 22 | // Re-export client types 23 | pub use client::OAuth2Client; 24 | 25 | // Re-export validation functions 26 | pub use validation::*; 27 | -------------------------------------------------------------------------------- /crates/turbomcp-auth/src/providers/mod.rs: -------------------------------------------------------------------------------- 1 | //! Authentication Providers 2 | //! 3 | //! This module contains various authentication provider implementations. 4 | 5 | pub mod api_key; 6 | 7 | pub use api_key::ApiKeyProvider; 8 | -------------------------------------------------------------------------------- /crates/turbomcp-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-cli" 3 | version = "2.0.5" 4 | edition = "2024" 5 | authors = ["Nicholas Paterno "] 6 | description = "Command-line tools for managing and testing MCP servers" 7 | license = "MIT" 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | keywords = ["mcp", "cli", "tools", "testing", "management"] 11 | categories = ["command-line-utilities", "development-tools"] 12 | readme = "README.md" 13 | rust-version = "1.89.0" 14 | 15 | [dependencies] 16 | # Core TurboMCP 17 | turbomcp-client = { path = "../turbomcp-client", version = "2.0.5" } 18 | turbomcp-transport = { path = "../turbomcp-transport", version = "2.0.5", features = ["tcp", "unix"] } 19 | turbomcp-protocol = { path = "../turbomcp-protocol", version = "2.0.5" } 20 | 21 | # CLI framework 22 | clap = { workspace = true, features = ["derive", "env", "string"] } 23 | 24 | # Serialization 25 | serde = { workspace = true, features = ["derive"] } 26 | serde_json = { workspace = true } 27 | serde_yaml = "0.9" 28 | 29 | # Async runtime 30 | tokio = { workspace = true } 31 | 32 | # Legacy transports (for backward compatibility - will be removed in v2.0) 33 | reqwest = { workspace = true } 34 | tokio-tungstenite = { workspace = true } 35 | futures = { workspace = true } 36 | 37 | # Output formatting 38 | comfy-table = "7.1" 39 | owo-colors = "4.1" 40 | indicatif = "0.18" 41 | 42 | # Error handling 43 | anyhow = "1.0" 44 | thiserror = { workspace = true } 45 | 46 | # Config 47 | config = "0.15" 48 | dirs = "6.0" 49 | 50 | [dev-dependencies] 51 | tokio-test = "0.4" 52 | 53 | [[bin]] 54 | name = "turbomcp-cli" 55 | path = "src/main.rs" -------------------------------------------------------------------------------- /crates/turbomcp-cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() { 3 | if let Err(e) = turbomcp_cli::run().await { 4 | // Display error with proper formatting 5 | eprintln!("Error: {}", e); 6 | std::process::exit(1); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /crates/turbomcp-cli/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Prelude module for convenient imports 2 | //! 3 | //! This module re-exports the most commonly used types for building 4 | //! applications with the TurboMCP CLI library. 5 | //! 6 | //! # Example 7 | //! 8 | //! ```rust,no_run 9 | //! use turbomcp_cli::prelude::*; 10 | //! 11 | //! #[tokio::main] 12 | //! async fn main() -> CliResult<()> { 13 | //! let cli = Cli::parse(); 14 | //! let executor = CommandExecutor::new( 15 | //! OutputFormat::Json, 16 | //! true, 17 | //! false 18 | //! ); 19 | //! executor.execute(cli.command).await 20 | //! } 21 | //! ``` 22 | 23 | pub use crate::{ 24 | // Core CLI types 25 | Cli, 26 | // Error handling 27 | CliError, 28 | CliResult, 29 | // Execution 30 | CommandExecutor, 31 | Commands, 32 | CompletionCommands, 33 | Connection, 34 | ErrorCategory, 35 | 36 | Formatter, 37 | 38 | LogLevel, 39 | 40 | OutputFormat, 41 | PromptCommands, 42 | // Supporting types 43 | RefType, 44 | ResourceCommands, 45 | SamplingCommands, 46 | 47 | ServerCommands, 48 | // Subcommands 49 | ToolCommands, 50 | TransportKind, 51 | 52 | // Entry point 53 | run, 54 | }; 55 | 56 | // Re-export commonly used client types for convenience 57 | pub use turbomcp_client::{Client, ClientBuilder}; 58 | 59 | // Re-export clap for custom CLI extensions 60 | pub use clap::Parser; 61 | -------------------------------------------------------------------------------- /crates/turbomcp-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-client" 3 | version = "2.0.5" 4 | edition = "2024" 5 | authors = ["Nicholas Paterno "] 6 | description = "MCP client with full protocol support, bidirectional communication, and plugin middleware" 7 | license = "MIT" 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | keywords = ["mcp", "client", "connection", "recovery", "async"] 11 | categories = ["development-tools", "network-programming", "asynchronous"] 12 | readme = "README.md" 13 | rust-version = "1.89.0" 14 | 15 | [dependencies] 16 | turbomcp-protocol = { workspace = true } 17 | turbomcp-transport = { workspace = true, default-features = false } 18 | async-trait = { workspace = true } 19 | serde = { workspace = true, features = ["derive"] } 20 | serde_json = { workspace = true } 21 | # LLM backend integration 22 | reqwest = { workspace = true } 23 | thiserror = { workspace = true } 24 | tokio = { workspace = true } 25 | tracing = { workspace = true } 26 | uuid = { workspace = true } 27 | # Plugin system dependencies 28 | chrono = { workspace = true, features = ["serde"] } 29 | # LLM integration dependencies 30 | futures = { workspace = true } 31 | 32 | [dev-dependencies] 33 | bytes = { workspace = true } 34 | tokio = { workspace = true } 35 | tracing-subscriber = { workspace = true } 36 | 37 | [features] 38 | default = ["stdio"] 39 | 40 | # Transport features (pass-through to turbomcp-transport) 41 | stdio = ["turbomcp-transport/stdio"] 42 | http = ["turbomcp-transport/http"] 43 | websocket = ["turbomcp-transport/websocket"] 44 | tcp = ["turbomcp-transport/tcp"] 45 | unix = ["turbomcp-transport/unix"] -------------------------------------------------------------------------------- /crates/turbomcp-client/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-client/src/client/config.rs: -------------------------------------------------------------------------------- 1 | //! Client configuration types and utilities 2 | //! 3 | //! This module contains configuration structures for MCP client connections 4 | //! and initialization results. 5 | 6 | use turbomcp_protocol::types::ServerCapabilities; 7 | 8 | /// Result of client initialization containing server information 9 | #[derive(Debug, Clone)] 10 | pub struct InitializeResult { 11 | /// Information about the server 12 | pub server_info: turbomcp_protocol::types::Implementation, 13 | 14 | /// Capabilities supported by the server 15 | pub server_capabilities: ServerCapabilities, 16 | } 17 | 18 | /// Connection configuration for the client 19 | #[derive(Debug, Clone)] 20 | pub struct ConnectionConfig { 21 | /// Request timeout in milliseconds 22 | pub timeout_ms: u64, 23 | 24 | /// Maximum number of retry attempts 25 | pub max_retries: u32, 26 | 27 | /// Retry delay in milliseconds 28 | pub retry_delay_ms: u64, 29 | 30 | /// Keep-alive interval in milliseconds 31 | pub keepalive_ms: u64, 32 | } 33 | 34 | impl Default for ConnectionConfig { 35 | fn default() -> Self { 36 | Self { 37 | timeout_ms: 30_000, // 30 seconds 38 | max_retries: 3, // 3 attempts 39 | retry_delay_ms: 1_000, // 1 second 40 | keepalive_ms: 60_000, // 60 seconds 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/turbomcp-client/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | //! MCP client core implementation 2 | //! 3 | //! This module contains the decomposed client implementation with focused 4 | //! modules for different responsibilities: 5 | //! 6 | //! - `core`: Main `Client` implementation and connection management 7 | //! - `protocol`: ProtocolClient for JSON-RPC communication 8 | //! - `dispatcher`: Message routing for bidirectional communication 9 | //! - `config`: Configuration types and utilities 10 | //! - `builder`: ClientBuilder pattern for construction 11 | //! - `operations`: MCP operations (tools, resources, prompts, etc.) 12 | //! - `systems`: Supporting systems (handlers, plugins, connection) 13 | //! 14 | //! Note: `Client` is now cloneable via `Arc>` - no need for SharedClient! 15 | 16 | // Core modules 17 | pub mod config; 18 | pub mod core; 19 | pub mod dispatcher; 20 | pub mod manager; 21 | pub mod operations; 22 | pub mod protocol; 23 | 24 | // Design Note: Module decomposition is complete for 2.0.0 25 | // 26 | // The client module is decomposed into focused submodules: 27 | // - config: Connection and initialization configuration 28 | // - core: Core client implementation 29 | // - manager: Session and connection management 30 | // - operations: MCP operation implementations (tools, prompts, resources) 31 | // - protocol: Protocol-level communication 32 | // 33 | // Further decomposition (builder, shared, systems) is not currently needed. 34 | // The current structure balances cohesion and simplicity. 35 | 36 | // Re-export main types for backwards compatibility 37 | pub use config::{ConnectionConfig, InitializeResult}; 38 | pub use manager::{ConnectionInfo, ConnectionState, ManagerConfig, ServerGroup, SessionManager}; 39 | -------------------------------------------------------------------------------- /crates/turbomcp-client/src/client/operations/mod.rs: -------------------------------------------------------------------------------- 1 | //! MCP client operations modules 2 | //! 3 | //! This module contains focused operation modules for CLIENT-INITIATED MCP operations: 4 | //! 5 | //! - `tools`: Tool operations (list, call) 6 | //! - `resources`: Resource operations (list, read, templates, subscribe/unsubscribe) 7 | //! - `prompts`: Prompt operations (list, get) 8 | //! - `completion`: Argument autocompletion operations 9 | //! - `sampling`: LLM sampling handler registration (SERVER->CLIENT) 10 | //! - `connection`: Connection utilities (ping, set_log_level) 11 | //! - `handlers`: Event handler registration for SERVER->CLIENT requests 12 | //! - `plugins`: Plugin registration and middleware management 13 | //! 14 | //! Note: `roots/list` is a SERVER->CLIENT request (not a client operation). 15 | //! The client should implement a roots handler to respond to server requests. 16 | 17 | pub mod completion; 18 | pub mod connection; 19 | pub mod handlers; 20 | pub mod plugins; 21 | pub mod prompts; 22 | pub mod resources; 23 | pub mod sampling; 24 | pub mod tools; 25 | -------------------------------------------------------------------------------- /crates/turbomcp-client/src/client/operations/plugins.rs: -------------------------------------------------------------------------------- 1 | //! Plugin management operations for MCP client 2 | //! 3 | //! This module provides methods for registering and managing client plugins 4 | //! that extend functionality through middleware. 5 | 6 | use turbomcp_protocol::{Error, Result}; 7 | 8 | impl super::super::core::Client { 9 | /// Register a plugin with the client 10 | /// 11 | /// # Arguments 12 | /// 13 | /// * `plugin` - The plugin to register 14 | /// 15 | /// # Examples 16 | /// 17 | /// ```rust,no_run 18 | /// use turbomcp_client::plugins::{MetricsPlugin, PluginConfig}; 19 | /// use std::sync::Arc; 20 | /// 21 | /// # async fn example() -> Result<(), Box> { 22 | /// # let mut client = turbomcp_client::Client::new(turbomcp_transport::stdio::StdioTransport::new()); 23 | /// let metrics_plugin = Arc::new(MetricsPlugin::new(PluginConfig::Metrics)); 24 | /// client.register_plugin(metrics_plugin).await?; 25 | /// # Ok(()) 26 | /// # } 27 | /// ``` 28 | pub async fn register_plugin( 29 | &self, 30 | plugin: std::sync::Arc, 31 | ) -> Result<()> { 32 | self.inner 33 | .plugin_registry 34 | .lock() 35 | .await 36 | .register_plugin(plugin) 37 | .await 38 | .map_err(|e| Error::bad_request(format!("Failed to register plugin: {}", e))) 39 | } 40 | 41 | /// Check if a plugin is registered 42 | /// 43 | /// # Arguments 44 | /// 45 | /// * `name` - The name of the plugin to check 46 | pub async fn has_plugin(&self, name: &str) -> bool { 47 | self.inner.plugin_registry.lock().await.has_plugin(name) 48 | } 49 | 50 | /// Get plugin data for a specific plugin type 51 | /// 52 | /// # Arguments 53 | /// 54 | /// * `name` - The name of the plugin 55 | /// 56 | /// # Examples 57 | /// 58 | /// ```rust,no_run 59 | /// use turbomcp_client::plugins::MetricsPlugin; 60 | /// 61 | /// # async fn example() -> Result<(), Box> { 62 | /// # let client = turbomcp_client::Client::new(turbomcp_transport::stdio::StdioTransport::new()); 63 | /// if let Some(plugin) = client.get_plugin("metrics").await { 64 | /// // Use plugin data 65 | /// } 66 | /// # Ok(()) 67 | /// # } 68 | /// ``` 69 | pub async fn get_plugin( 70 | &self, 71 | name: &str, 72 | ) -> Option> { 73 | self.inner.plugin_registry.lock().await.get_plugin(name) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /crates/turbomcp-client/src/plugins/mod.rs: -------------------------------------------------------------------------------- 1 | //! Plugin system for TurboMCP client 2 | //! 3 | //! Provides an extensible plugin architecture with lifecycle hooks and middleware patterns 4 | //! for extending client functionality. Plugins can intercept requests and responses, 5 | //! handle custom methods, and add features like metrics, retries, and caching. 6 | //! 7 | //! ## Architecture 8 | //! 9 | //! The plugin system follows a middleware pattern where plugins are executed in order: 10 | //! 11 | //! ```text 12 | //! Request → Plugin 1 → Plugin 2 → Plugin N → Server 13 | //! ↓ ↓ ↓ 14 | //! Response ← Plugin 1 ← Plugin 2 ← Plugin N ← Server 15 | //! ``` 16 | //! 17 | //! ## Core Components 18 | //! 19 | //! - **ClientPlugin**: Core trait defining plugin lifecycle and hooks 20 | //! - **PluginRegistry**: Manages plugin registration, ordering, and execution 21 | //! - **RequestContext/ResponseContext**: Context objects passed to plugins 22 | //! - **Example Plugins**: MetricsPlugin, RetryPlugin, CachePlugin 23 | //! 24 | //! ## Usage 25 | //! 26 | //! ```rust,no_run 27 | //! use turbomcp_client::plugins::{PluginRegistry, MetricsPlugin, PluginConfig}; 28 | //! use std::sync::Arc; 29 | //! 30 | //! async fn example() -> Result<(), Box> { 31 | //! let mut registry = PluginRegistry::new(); 32 | //! 33 | //! // Register built-in plugins 34 | //! let metrics = Arc::new(MetricsPlugin::new(PluginConfig::Metrics)); 35 | //! registry.register_plugin(metrics).await?; 36 | //! Ok(()) 37 | //! } 38 | //! ``` 39 | 40 | pub mod core; 41 | pub mod examples; 42 | pub mod macros; 43 | pub mod middleware; 44 | pub mod registry; 45 | 46 | // Re-export core types for public API 47 | pub use core::{ 48 | ClientPlugin, PluginConfig, PluginContext, PluginError, PluginResult, RequestContext, 49 | ResponseContext, 50 | }; 51 | 52 | pub use registry::PluginRegistry; 53 | 54 | pub use examples::{ 55 | CacheConfig, CachePlugin, MetricsData, MetricsPlugin, RetryConfig, RetryPlugin, 56 | }; 57 | 58 | pub use middleware::{MiddlewareChain, MiddlewareResult, RequestMiddleware, ResponseMiddleware}; 59 | -------------------------------------------------------------------------------- /crates/turbomcp-client/tests/cli_tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for MCP client functionality 2 | 3 | #[test] 4 | fn test_client_functionality() { 5 | // Test basic client functionality 6 | use turbomcp_client::{ClientBuilder, ClientCapabilities}; 7 | 8 | let _builder = ClientBuilder::new(); 9 | let _capabilities = ClientCapabilities::default(); 10 | 11 | // Compilation test - no runtime assertions needed 12 | } 13 | -------------------------------------------------------------------------------- /crates/turbomcp-dpop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-dpop" 3 | version = "2.0.5" 4 | edition = "2024" 5 | authors = ["Nicholas Paterno "] 6 | description = "DPoP (RFC 9449) implementation for TurboMCP with HSM support" 7 | license = "MIT" 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | documentation = "https://docs.rs/turbomcp-dpop" 11 | readme = "README.md" 12 | keywords = ["dpop", "oauth", "security", "hsm", "rfc9449"] 13 | categories = ["authentication", "cryptography", "web-programming"] 14 | rust-version = "1.89.0" 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | # Core dependencies 21 | tokio = { workspace = true } 22 | tokio-util = "0.7" 23 | futures = "0.3" 24 | serde = { version = "1.0", features = ["derive"] } 25 | serde_json = "1.0" 26 | async-trait = "0.1" 27 | thiserror = "2.0.16" 28 | tracing = "0.1" 29 | 30 | # Cryptographic dependencies - core DPoP functionality 31 | jsonwebtoken = "9.3" 32 | base64 = "0.22" 33 | sha2 = "0.10" 34 | ring = "0.17" 35 | rsa = { version = "0.9", features = ["sha2"] } 36 | p256 = { version = "0.13", features = ["ecdsa"] } 37 | rand = "0.8.5" 38 | signature = "2.2" 39 | hex = "0.4" 40 | zeroize = "1.7" 41 | 42 | # HTTP types for DPoP proof generation 43 | http = "1.0" 44 | url = "2.5" 45 | 46 | # Time handling 47 | chrono = { version = "0.4", features = ["serde"] } 48 | 49 | # UUID for nonce generation 50 | uuid = { version = "1.0", features = ["v4", "v7", "serde"] } 51 | 52 | # Redis storage (optional) 53 | redis = { version = "0.32.5", features = ["aio", "tokio-comp", "safe_iterators"], optional = true } 54 | 55 | # HSM support - PKCS#11 (optional) 56 | cryptoki = { version = "0.10", optional = true } 57 | r2d2 = { version = "0.8", optional = true } 58 | secrecy = { version = "0.8.0", optional = true } 59 | parking_lot = { workspace = true, optional = true } 60 | asn1 = { version = "0.22.0", optional = true } 61 | 62 | # HSM support - YubiHSM (optional) 63 | yubihsm = { version = "0.42", optional = true, features = ["usb", "http"] } 64 | 65 | # Internal dependencies 66 | 67 | [dev-dependencies] 68 | tokio-test = "0.4" 69 | tempfile = "3.0" 70 | proptest = "1.5" 71 | 72 | [features] 73 | default = [] 74 | 75 | # Storage backends 76 | redis-storage = ["dep:redis"] 77 | 78 | # HSM backends 79 | hsm-pkcs11 = ["dep:cryptoki", "dep:r2d2", "dep:secrecy", "dep:parking_lot", "dep:asn1"] 80 | hsm-yubico = ["dep:yubihsm", "dep:secrecy"] 81 | hsm = ["hsm-pkcs11", "hsm-yubico"] 82 | 83 | # Test utilities 84 | test-utils = [] 85 | 86 | [package.metadata.docs.rs] 87 | all-features = true 88 | rustdoc-args = ["--cfg", "docsrs"] 89 | -------------------------------------------------------------------------------- /crates/turbomcp-dpop/README.md: -------------------------------------------------------------------------------- 1 | # TurboMCP DPoP 2 | 3 | RFC 9449 compliant DPoP (Demonstrating Proof-of-Possession) implementation for OAuth 2.0. 4 | 5 | ## Features 6 | 7 | - **RFC 9449 Compliance** - Full specification implementation 8 | - **Cryptographic Security** - RSA, ECDSA P-256, and PSS support 9 | - **Token Binding** - Prevents stolen token usage 10 | - **Replay Protection** - Nonce tracking and timestamp validation 11 | - **HSM Support** - PKCS#11 and YubiHSM integration 12 | - **Redis Storage** - Distributed nonce tracking 13 | 14 | ## Usage 15 | 16 | ```toml 17 | [dependencies] 18 | turbomcp-dpop = "2.0.4" 19 | 20 | # With Redis storage 21 | turbomcp-dpop = { version = "2.0.4", features = ["redis-storage"] } 22 | 23 | # With HSM support 24 | turbomcp-dpop = { version = "2.0.4", features = ["hsm"] } 25 | ``` 26 | 27 | ## Feature Flags 28 | 29 | - `default` - Core DPoP functionality 30 | - `redis-storage` - Redis backend for nonce tracking 31 | - `hsm-pkcs11` - PKCS#11 HSM support 32 | - `hsm-yubico` - YubiHSM support 33 | - `hsm` - All HSM backends 34 | - `test-utils` - Test utilities 35 | 36 | ## License 37 | 38 | MIT 39 | -------------------------------------------------------------------------------- /crates/turbomcp-dpop/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # TurboMCP DPoP - RFC 9449 Implementation 2 | //! 3 | //! DPoP (Demonstrating Proof-of-Possession) implementation for OAuth 2.0 as specified in RFC 9449. 4 | //! DPoP binds access tokens to cryptographic key pairs, preventing token theft and replay attacks. 5 | //! 6 | //! ## Core Features 7 | //! 8 | //! - ✅ **RFC 9449 Compliance** - Full specification implementation 9 | //! - ✅ **Cryptographic Security** - RSA, ECDSA P-256, and PSS support 10 | //! - ✅ **Token Binding** - Prevents stolen token usage 11 | //! - ✅ **Replay Protection** - Nonce tracking and timestamp validation 12 | //! - ✅ **Production Features** - HSM integration, audit logging, key rotation 13 | //! 14 | //! ## Architecture 15 | //! 16 | //! - `errors` - DPoP-specific error types 17 | //! - `types` - Core DPoP types (algorithms, key pairs, proofs) 18 | //! - `keys` - Key management and rotation 19 | //! - `proof` - Proof generation and validation 20 | //! - `redis_storage` - Redis backend (feature-gated: `redis-storage`) 21 | //! - `hsm` - Hardware Security Module support (feature-gated) 22 | //! - `hsm::pkcs11` - PKCS#11 HSM integration (feature: `hsm-pkcs11`) 23 | //! - `hsm::yubihsm` - YubiHSM integration (feature: `hsm-yubico`) 24 | //! 25 | //! ## Feature Flags 26 | //! 27 | //! - `default` - Core DPoP functionality (no optional features) 28 | //! - `redis-storage` - Redis storage backend for nonce tracking 29 | //! - `hsm-pkcs11` - PKCS#11 HSM support 30 | //! - `hsm-yubico` - YubiHSM support 31 | //! - `hsm` - Enable all HSM backends 32 | //! - `test-utils` - Test utilities for DPoP testing 33 | 34 | // Core modules (always available when dpop feature is enabled) 35 | pub mod errors; 36 | pub mod keys; 37 | pub mod proof; 38 | pub mod types; 39 | 40 | // HSM support (always declared, implementations feature-gated inside) 41 | pub mod hsm; 42 | 43 | // Optional feature modules 44 | #[cfg(feature = "redis-storage")] 45 | pub mod redis_storage; 46 | 47 | #[cfg(feature = "test-utils")] 48 | pub mod test_utils; 49 | 50 | // Re-export core types for convenience 51 | pub use errors::*; 52 | pub use keys::*; 53 | pub use proof::*; 54 | pub use types::*; 55 | 56 | /// DPoP result type 57 | pub type Result = std::result::Result; 58 | 59 | /// DPoP JWT header type as defined in RFC 9449 60 | pub const DPOP_JWT_TYPE: &str = "dpop+jwt"; 61 | 62 | /// Maximum clock skew tolerance (5 minutes) 63 | pub const MAX_CLOCK_SKEW_SECONDS: i64 = 300; 64 | 65 | /// Default proof lifetime (60 seconds) 66 | pub const DEFAULT_PROOF_LIFETIME_SECONDS: u64 = 60; 67 | 68 | /// Maximum proof lifetime (5 minutes) 69 | pub const MAX_PROOF_LIFETIME_SECONDS: u64 = 300; 70 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-macros" 3 | version = "2.0.5" 4 | edition = "2024" 5 | description = "Procedural macros for ergonomic MCP tool and resource registration" 6 | license = "MIT" 7 | authors = ["Nicholas Paterno "] 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | keywords = ["mcp", "macros", "procedural", "derive", "codegen"] 11 | categories = ["development-tools"] 12 | readme = "README.md" 13 | rust-version = "1.89.0" 14 | 15 | [lints] 16 | workspace = true 17 | 18 | [lib] 19 | proc-macro = true 20 | 21 | [dependencies] 22 | proc-macro2 = "1.0" 23 | quote = "1.0" 24 | syn = { version = "2.0", features = ["full", "extra-traits", "parsing", "visit"] } 25 | serde = { version = "1.0", features = ["derive"] } 26 | serde_json = "1.0" 27 | # JSON Schema generation (always required - macros always generate schemas) 28 | schemars = { version = "1.0" } 29 | 30 | # CRITICAL FIX: Add dependencies for macro-generated code 31 | # These are required because the #[server] macro generates code that uses these crates 32 | axum = { version = "0.8.4", optional = true, features = ["ws", "tracing", "macros"] } 33 | tokio = { workspace = true, optional = true } 34 | turbomcp-protocol = { version = "2.0.5", path = "../turbomcp-protocol" } 35 | turbomcp-transport = { version = "2.0.5", path = "../turbomcp-transport", optional = true } 36 | 37 | [dev-dependencies] 38 | tokio = { workspace = true } 39 | turbomcp-protocol = { version = "2.0.5", path = "../turbomcp-protocol" } 40 | async-trait = "0.1" 41 | trybuild = "1.0" 42 | 43 | [features] 44 | default = [] 45 | # CRITICAL FIX: Feature flags for macro-generated code 46 | http = ["axum"] 47 | tcp = ["tokio", "dep:turbomcp-transport"] 48 | unix = ["tokio", "dep:turbomcp-transport"] 49 | # Note: schemars is always required and not a feature - it's used by all macros -------------------------------------------------------------------------------- /crates/turbomcp-macros/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-macros/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions for TurboMCP procedural macros 2 | 3 | use proc_macro::TokenStream; 4 | use quote::quote; 5 | use syn::DeriveInput; 6 | 7 | /// Generate schema implementation by delegating to the schema system 8 | pub fn generate_schema_impl(input: DeriveInput, method_name: &str) -> TokenStream { 9 | let name = &input.ident; 10 | let method_ident = syn::Ident::new(method_name, proc_macro2::Span::call_site()); 11 | 12 | TokenStream::from(quote! { 13 | impl #name { 14 | pub fn #method_ident() -> serde_json::Value { 15 | // Delegate to the schema generation system 16 | turbomcp::schema::json_schema_for::() 17 | } 18 | } 19 | }) 20 | } 21 | 22 | /// Generate tool input schema implementation 23 | pub fn generate_tool_input_schema(input: DeriveInput) -> TokenStream { 24 | generate_schema_impl(input, "schema") 25 | } 26 | 27 | /// Generate prompt args schema implementation 28 | pub fn generate_prompt_args_schema(input: DeriveInput) -> TokenStream { 29 | generate_schema_impl(input, "prompt_schema") 30 | } 31 | 32 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/compile_fail/stdio_print_rejected.rs: -------------------------------------------------------------------------------- 1 | //! This test should FAIL to compile. 2 | //! Demonstrates that print! is rejected in stdio servers. 3 | 4 | use turbomcp_macros::server; 5 | use turbomcp_protocol::Result; 6 | 7 | #[derive(Clone)] 8 | pub struct MyServer; 9 | 10 | #[server(transports = ["stdio"])] 11 | impl MyServer { 12 | #[turbomcp_macros::tool("My tool")] 13 | async fn my_tool(&self) -> Result { 14 | print!("This is not allowed"); 15 | Ok("result".to_string()) 16 | } 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/compile_fail/stdio_print_rejected.stderr: -------------------------------------------------------------------------------- 1 | error: ❌ stdio transport detected but found unsafe stdout writes: 2 | 3 | print!(): forbidden in stdio server (use eprintln! or tracing) 4 | 5 | Stdio transport reserves stdout for MCP protocol messages. 6 | All output MUST go to stderr: 7 | 8 | ✅ CORRECT: 9 | eprintln!("debug info"); 10 | tracing::info!("message"); // if configured for stderr 11 | 12 | ❌ WRONG: 13 | println!("debug info"); 14 | std::io::stdout().write_all(b"..."); 15 | 16 | See: https://docs.modelcontextprotocol.io/guides/stdio-output 17 | --> tests/compile_fail/stdio_print_rejected.rs:11:1 18 | | 19 | 11 | / impl MyServer { 20 | 12 | | #[turbomcp_macros::tool("My tool")] 21 | 13 | | async fn my_tool(&self) -> Result { 22 | 14 | | print!("This is not allowed"); 23 | ... | 24 | 17 | | } 25 | | |_^ 26 | 27 | warning: unused import: `turbomcp_protocol::Result` 28 | --> tests/compile_fail/stdio_print_rejected.rs:5:5 29 | | 30 | 5 | use turbomcp_protocol::Result; 31 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 32 | | 33 | = note: `#[warn(unused_imports)]` on by default 34 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/compile_fail/stdio_println_rejected.rs: -------------------------------------------------------------------------------- 1 | //! This test should FAIL to compile. 2 | //! Demonstrates that println! is rejected in stdio servers. 3 | 4 | use turbomcp_macros::server; 5 | use turbomcp_protocol::Result; 6 | 7 | #[derive(Clone)] 8 | pub struct MyServer; 9 | 10 | #[server(transports = ["stdio"])] 11 | impl MyServer { 12 | #[turbomcp_macros::tool("My tool")] 13 | async fn my_tool(&self) -> Result { 14 | println!("This is not allowed in stdio servers"); 15 | Ok("result".to_string()) 16 | } 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/compile_fail/stdio_println_rejected.stderr: -------------------------------------------------------------------------------- 1 | error: ❌ stdio transport detected but found unsafe stdout writes: 2 | 3 | println!(): forbidden in stdio server (use eprintln! or tracing) 4 | 5 | Stdio transport reserves stdout for MCP protocol messages. 6 | All output MUST go to stderr: 7 | 8 | ✅ CORRECT: 9 | eprintln!("debug info"); 10 | tracing::info!("message"); // if configured for stderr 11 | 12 | ❌ WRONG: 13 | println!("debug info"); 14 | std::io::stdout().write_all(b"..."); 15 | 16 | See: https://docs.modelcontextprotocol.io/guides/stdio-output 17 | --> tests/compile_fail/stdio_println_rejected.rs:11:1 18 | | 19 | 11 | / impl MyServer { 20 | 12 | | #[turbomcp_macros::tool("My tool")] 21 | 13 | | async fn my_tool(&self) -> Result { 22 | 14 | | println!("This is not allowed in stdio servers"); 23 | ... | 24 | 17 | | } 25 | | |_^ 26 | 27 | warning: unused import: `turbomcp_protocol::Result` 28 | --> tests/compile_fail/stdio_println_rejected.rs:5:5 29 | | 30 | 5 | use turbomcp_protocol::Result; 31 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 32 | | 33 | = note: `#[warn(unused_imports)]` on by default 34 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/helper_macro_tests.rs: -------------------------------------------------------------------------------- 1 | //! Real tests for helper macros - validates actual behavior, not compilation 2 | 3 | #![allow(deprecated)] // Testing backwards-compatible macros 4 | 5 | use turbomcp_macros::*; 6 | use turbomcp_protocol::types::ContentBlock; 7 | 8 | #[test] 9 | fn test_mcp_text_produces_correct_content() { 10 | let content = mcp_text!("Hello World"); 11 | 12 | assert!(matches!(content, ContentBlock::Text(_))); 13 | if let ContentBlock::Text(text_content) = content { 14 | assert_eq!(text_content.text, "Hello World"); 15 | } 16 | } 17 | 18 | #[test] 19 | fn test_mcp_text_formatting_works() { 20 | let name = "Alice"; 21 | let age = 30; 22 | let content = mcp_text!("Name: {}, Age: {}", name, age); 23 | 24 | if let ContentBlock::Text(text_content) = content { 25 | assert_eq!(text_content.text, "Name: Alice, Age: 30"); 26 | } else { 27 | panic!("Expected TextContent"); 28 | } 29 | } 30 | 31 | #[test] 32 | fn test_mcp_error_creates_proper_error() { 33 | let error = mcp_error!("Something went wrong"); 34 | 35 | // Test that mcp_error! creates a ServerError 36 | let error_msg = format!("{}", error); 37 | assert!(error_msg.contains("Something went wrong")); 38 | } 39 | 40 | #[test] 41 | fn test_mcp_error_formatting_works() { 42 | let operation = "database"; 43 | let code = 500; 44 | let error = mcp_error!("Failed {}: code {}", operation, code); 45 | 46 | let error_msg = format!("{}", error); 47 | assert!(error_msg.contains("Failed database: code 500")); 48 | } 49 | 50 | #[test] 51 | fn test_tool_result_empty_creates_valid_result() { 52 | let result = tool_result!(); 53 | 54 | assert!(result.content.is_empty()); 55 | assert!(!result.is_error.unwrap_or(true)); 56 | } 57 | 58 | #[test] 59 | fn test_tool_result_with_content() { 60 | let text_content = mcp_text!("Success"); 61 | let result = tool_result!(content = [text_content]); 62 | 63 | assert_eq!(result.content.len(), 1); 64 | assert!(!result.is_error.unwrap_or(true)); 65 | 66 | if let ContentBlock::Text(text) = &result.content[0] { 67 | assert_eq!(text.text, "Success"); 68 | } else { 69 | panic!("Expected TextContent"); 70 | } 71 | } 72 | 73 | #[tokio::test] 74 | async fn test_helper_macros_async_compatibility() { 75 | async fn async_operation() -> String { 76 | "async result".to_string() 77 | } 78 | 79 | let result = async_operation().await; 80 | let content = mcp_text!("Result: {}", result); 81 | let tool_result = tool_result!(content = [content]); 82 | 83 | assert_eq!(tool_result.content.len(), 1); 84 | if let ContentBlock::Text(text) = &tool_result.content[0] { 85 | assert_eq!(text.text, "Result: async result"); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /crates/turbomcp-macros/tests/stdio_safety_tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for stdio transport safety validation 2 | //! 3 | //! These tests verify that the #[server] macro correctly rejects servers 4 | //! using printf-like macros (println!, print!) when stdio transport is enabled. 5 | //! 6 | //! Uses trybuild to test compile-fail scenarios. 7 | 8 | #[test] 9 | fn stdio_safety_compile_tests() { 10 | let t = trybuild::TestCases::new(); 11 | 12 | // Test that println! is rejected in stdio servers 13 | t.compile_fail("tests/compile_fail/stdio_println_rejected.rs"); 14 | 15 | // Test that print! is rejected in stdio servers 16 | t.compile_fail("tests/compile_fail/stdio_print_rejected.rs"); 17 | } 18 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-protocol" 3 | version = "2.0.5" 4 | edition = "2024" 5 | authors = ["Nicholas Paterno "] 6 | description = "Complete MCP protocol implementation with types, traits, context management, and message handling" 7 | license = "MIT" 8 | repository = "https://github.com/Epistates/turbomcp" 9 | homepage = "https://turbomcp.org" 10 | keywords = ["mcp", "protocol", "jsonrpc", "llm", "ai"] 11 | categories = ["development-tools", "encoding", "parser-implementations"] 12 | readme = "README.md" 13 | rust-version = "1.89.0" 14 | 15 | [dependencies] 16 | # Core async 17 | tokio = { workspace = true, features = ["rt", "time", "sync", "macros"] } 18 | tokio-util = { workspace = true } 19 | futures = { workspace = true } 20 | async-trait = { workspace = true } 21 | pin-project-lite = { workspace = true } 22 | 23 | # Serialization 24 | serde = { workspace = true } 25 | serde_json = { workspace = true } 26 | bytes = { workspace = true } 27 | msgpacker = { workspace = true, optional = true, features = ["derive", "alloc"] } 28 | ciborium = "0.2" 29 | 30 | # Error handling 31 | thiserror = { workspace = true } 32 | miette = { workspace = true, optional = true } 33 | 34 | # Observability 35 | tracing = { workspace = true } 36 | tracing-opentelemetry = { workspace = true, optional = true } 37 | metrics = { workspace = true, optional = true } 38 | 39 | # Performance 40 | smallvec = { workspace = true } 41 | compact_str = { workspace = true } 42 | ahash = { workspace = true } 43 | once_cell = { workspace = true } 44 | arc-swap = { workspace = true } 45 | 46 | # SIMD support 47 | simd-json = { workspace = true, optional = true } 48 | sonic-rs = { workspace = true, optional = true } 49 | simdutf8 = { version = "0.1", optional = true } 50 | 51 | # Time 52 | chrono = { workspace = true } 53 | uuid = { workspace = true } 54 | 55 | # Concurrency 56 | parking_lot = { workspace = true } 57 | crossbeam = { workspace = true } 58 | dashmap = { workspace = true } 59 | 60 | # Random number generation for jitter 61 | rand = "0.9" 62 | 63 | # Memory-mapped files (optional, requires unsafe feature) 64 | memmap2 = { version = "0.9.9", optional = true } 65 | 66 | # Validation 67 | regex = "1.12" 68 | 69 | [dev-dependencies] 70 | criterion = { workspace = true } 71 | tokio-test = { workspace = true } 72 | pretty_assertions = { workspace = true } 73 | proptest = "1.5" 74 | 75 | [features] 76 | default = ["std", "simd"] 77 | std = [] 78 | simd = ["simd-json", "sonic-rs", "simdutf8"] 79 | zero-copy = ["bytes/serde"] 80 | messagepack = ["msgpacker"] 81 | tracing = ["dep:tracing-opentelemetry"] 82 | metrics = ["dep:metrics"] 83 | wasm = [] 84 | fancy-errors = ["miette"] 85 | mmap = ["memmap2"] # Enable memory-mapped file support 86 | lock-free = [] # Enable lock-free data structures (requires unsafe) 87 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/context/mod.rs: -------------------------------------------------------------------------------- 1 | //! Context module for MCP request and response handling. 2 | //! 3 | //! This module provides comprehensive context types for tracking requests, 4 | //! responses, and various MCP protocol features including bidirectional 5 | //! communication, elicitation, completion, and more. 6 | 7 | pub mod capabilities; 8 | pub mod client; 9 | pub mod completion; 10 | pub mod elicitation; 11 | pub mod ping; 12 | pub mod request; 13 | pub mod server_initiated; 14 | pub mod templates; 15 | 16 | // Re-export everything to maintain API compatibility 17 | pub use capabilities::*; 18 | pub use client::*; 19 | pub use completion::*; 20 | pub use elicitation::*; 21 | pub use ping::*; 22 | pub use request::*; 23 | pub use server_initiated::*; 24 | pub use templates::*; 25 | 26 | // 🎉 REFACTORING COMPLETE! 🎉 27 | // All 2,046 lines from the monolithic context.rs have been successfully 28 | // decomposed into 8 focused, maintainable modules with zero breaking changes! 29 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/context/ping.rs: -------------------------------------------------------------------------------- 1 | //! Ping and health monitoring context types. 2 | //! 3 | //! This module contains types for handling ping requests, health checks, 4 | //! and connection quality monitoring in MCP systems. 5 | 6 | use std::collections::HashMap; 7 | 8 | use chrono::{DateTime, Utc}; 9 | use serde::{Deserialize, Serialize}; 10 | 11 | use super::capabilities::PingOrigin; 12 | 13 | /// Context for ping/health monitoring requests 14 | #[derive(Debug, Clone, Serialize, Deserialize)] 15 | pub struct PingContext { 16 | /// Ping origin (client or server) 17 | pub origin: PingOrigin, 18 | /// Expected response time threshold in milliseconds 19 | pub response_threshold_ms: Option, 20 | /// Custom ping payload 21 | pub payload: Option, 22 | /// Health check metadata 23 | pub health_metadata: HashMap, 24 | /// Connection quality metrics 25 | pub connection_metrics: Option, 26 | } 27 | 28 | /// Connection quality metrics 29 | #[derive(Debug, Clone, Serialize, Deserialize)] 30 | pub struct ConnectionMetrics { 31 | /// Round-trip time in milliseconds 32 | pub rtt_ms: Option, 33 | /// Packet loss percentage (0.0-100.0) 34 | pub packet_loss: Option, 35 | /// Connection uptime in seconds 36 | pub uptime_seconds: Option, 37 | /// Bytes sent 38 | pub bytes_sent: Option, 39 | /// Bytes received 40 | pub bytes_received: Option, 41 | /// Last successful communication timestamp 42 | pub last_success: Option>, 43 | } 44 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/context/templates.rs: -------------------------------------------------------------------------------- 1 | //! Template context types for resource template operations. 2 | //! 3 | //! This module contains types for handling URI template operations, 4 | //! parameter validation, and template metadata management. 5 | 6 | use std::collections::HashMap; 7 | 8 | use serde::{Deserialize, Serialize}; 9 | 10 | /// Context for resource template operations 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | pub struct ResourceTemplateContext { 13 | /// Template name 14 | pub template_name: String, 15 | /// URI template pattern (RFC 6570) 16 | pub uri_template: String, 17 | /// Available template parameters 18 | pub parameters: HashMap, 19 | /// Template description 20 | pub description: Option, 21 | /// Template category/preset type 22 | pub preset_type: Option, 23 | /// Template metadata 24 | pub metadata: HashMap, 25 | } 26 | 27 | /// Template parameter definition 28 | #[derive(Debug, Clone, Serialize, Deserialize)] 29 | pub struct TemplateParameter { 30 | /// Parameter name 31 | pub name: String, 32 | /// Parameter type 33 | pub param_type: String, 34 | /// Whether parameter is required 35 | pub required: bool, 36 | /// Default value 37 | pub default: Option, 38 | /// Parameter description 39 | pub description: Option, 40 | /// Validation pattern (regex) 41 | pub pattern: Option, 42 | /// Enum values (if applicable) 43 | pub enum_values: Option>, 44 | } 45 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/error_utils.rs: -------------------------------------------------------------------------------- 1 | //! Error handling utility functions for consistent error patterns 2 | //! 3 | //! This module provides standardized error conversion and handling patterns 4 | //! to eliminate inconsistencies across the codebase. 5 | 6 | use std::fmt; 7 | 8 | /// Standard error conversion pattern for consistent error formatting 9 | pub trait StandardErrorConversion { 10 | /// Convert error to standard string format with context 11 | fn to_standard_error(self, context: &str) -> Result; 12 | } 13 | 14 | impl StandardErrorConversion for Result { 15 | fn to_standard_error(self, context: &str) -> Result { 16 | self.map_err(|e| format!("{context}: {e}")) 17 | } 18 | } 19 | 20 | /// Convenience function for consistent JSON parsing errors 21 | pub fn json_parse_error( 22 | result: Result, 23 | context: &str, 24 | ) -> Result { 25 | result.map_err(|e| format!("{context}: {e}")) 26 | } 27 | 28 | /// Convenience function for consistent I/O errors 29 | pub fn io_error(result: Result, context: &str) -> Result { 30 | result.map_err(|e| format!("{context}: {e}")) 31 | } 32 | 33 | /// Convenience function for consistent network errors 34 | pub fn network_error( 35 | result: Result>, 36 | context: &str, 37 | ) -> Result { 38 | result.map_err(|e| format!("{context}: {e}")) 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | use serde_json; 45 | 46 | #[test] 47 | fn test_standard_error_conversion() { 48 | let result: Result = Err("original error"); 49 | let converted = result.to_standard_error("Operation failed"); 50 | assert_eq!(converted.unwrap_err(), "Operation failed: original error"); 51 | } 52 | 53 | #[test] 54 | fn test_json_parse_error() { 55 | let invalid_json = "{ invalid json"; 56 | let result: Result = serde_json::from_str(invalid_json); 57 | let converted = json_parse_error(result, "Failed to parse JSON"); 58 | assert!(converted.unwrap_err().starts_with("Failed to parse JSON:")); 59 | } 60 | 61 | #[test] 62 | fn test_io_error() { 63 | let result: Result = Err(std::io::Error::new( 64 | std::io::ErrorKind::NotFound, 65 | "file not found", 66 | )); 67 | let converted = io_error(result, "Failed to read file"); 68 | assert_eq!( 69 | converted.unwrap_err(), 70 | "Failed to read file: file not found" 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/security/mod.rs: -------------------------------------------------------------------------------- 1 | //! Security utilities for TurboMCP 2 | //! 3 | //! This module provides focused security utilities integrated into turbomcp-protocol 4 | //! as part of the distributed security model. These utilities follow the principle 5 | //! of doing one thing well, providing essential security primitives without 6 | //! over-engineering. 7 | //! 8 | //! ## Core Functions 9 | //! 10 | //! - [`validate_path`] - Basic path validation with traversal attack prevention 11 | //! - [`validate_path_within`] - Path validation with directory boundary enforcement 12 | //! - [`validate_file_extension`] - Simple file extension validation 13 | 14 | pub mod validation; 15 | 16 | // Re-export main functions for convenience 17 | pub use validation::{validate_file_extension, validate_path, validate_path_within}; 18 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/types/initialization.rs: -------------------------------------------------------------------------------- 1 | //! Types for the MCP connection initialization and handshake process. 2 | //! 3 | //! This module defines the data structures used during the initial handshake 4 | //! between an MCP client and server, where they negotiate capabilities and 5 | //! exchange implementation details. 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use super::{ 10 | capabilities::{ClientCapabilities, ServerCapabilities}, 11 | core::{Implementation, ProtocolVersion}, 12 | }; 13 | 14 | /// The `initialize` request is sent by the client as the first message after connection. 15 | /// 16 | /// It allows the client and server to exchange their capabilities and agree on a 17 | /// protocol version to use for the duration of the session. 18 | #[derive(Debug, Clone, Serialize, Deserialize)] 19 | pub struct InitializeRequest { 20 | /// The protocol version the client wishes to use. 21 | #[serde(rename = "protocolVersion")] 22 | pub protocol_version: ProtocolVersion, 23 | /// The capabilities supported by the client. 24 | pub capabilities: ClientCapabilities, 25 | /// Information about the client's implementation (e.g., name, version). 26 | #[serde(rename = "clientInfo")] 27 | pub client_info: Implementation, 28 | /// Optional metadata for the request. 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | pub _meta: Option, 31 | } 32 | 33 | /// The response to a successful `initialize` request. 34 | /// 35 | /// The server sends this message to confirm the connection parameters and 36 | /// to declare its own capabilities. 37 | #[derive(Debug, Clone, Serialize, Deserialize)] 38 | pub struct InitializeResult { 39 | /// The protocol version that will be used for the session, chosen by the server. 40 | #[serde(rename = "protocolVersion")] 41 | pub protocol_version: ProtocolVersion, 42 | /// The capabilities supported by the server. 43 | pub capabilities: ServerCapabilities, 44 | /// Information about the server's implementation (e.g., name, version). 45 | #[serde(rename = "serverInfo")] 46 | pub server_info: Implementation, 47 | /// Optional human-readable instructions for the client. 48 | #[serde(skip_serializing_if = "Option::is_none")] 49 | pub instructions: Option, 50 | /// Optional metadata for the result. 51 | #[serde(skip_serializing_if = "Option::is_none")] 52 | pub _meta: Option, 53 | } 54 | 55 | /// A notification sent from the client to the server after receiving a successful 56 | /// `InitializeResult`, confirming that the client is ready to proceed. 57 | /// 58 | /// This notification has no parameters. 59 | #[derive(Debug, Clone, Serialize, Deserialize)] 60 | pub struct InitializedNotification; 61 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/types/logging.rs: -------------------------------------------------------------------------------- 1 | //! Logging types 2 | //! 3 | //! This module contains types for MCP logging notifications. 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /// Log level enumeration 8 | #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] 9 | #[serde(rename_all = "lowercase")] 10 | pub enum LogLevel { 11 | /// Debug level 12 | Debug, 13 | /// Info level 14 | Info, 15 | /// Notice level 16 | Notice, 17 | /// Warning level 18 | Warning, 19 | /// Error level 20 | Error, 21 | /// Critical level 22 | Critical, 23 | /// Alert level 24 | Alert, 25 | /// Emergency level 26 | Emergency, 27 | } 28 | 29 | /// Set logging level request 30 | #[derive(Debug, Clone, Serialize, Deserialize)] 31 | pub struct SetLevelRequest { 32 | /// Log level to set 33 | pub level: LogLevel, 34 | } 35 | 36 | /// Set logging level result (empty) 37 | #[derive(Debug, Clone, Serialize, Deserialize)] 38 | pub struct SetLevelResult; 39 | 40 | /// Logging notification 41 | #[derive(Debug, Clone, Serialize, Deserialize)] 42 | pub struct LoggingNotification { 43 | /// Log level 44 | pub level: LogLevel, 45 | /// Log message 46 | pub data: serde_json::Value, 47 | /// Optional logger name 48 | #[serde(skip_serializing_if = "Option::is_none")] 49 | pub logger: Option, 50 | } 51 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! MCP Protocol Types Module 2 | //! 3 | //! This module contains all the type definitions for the Model Context Protocol 4 | //! organized into focused submodules based on the MCP 2025-06-18 specification. 5 | //! 6 | //! # Module Organization 7 | //! 8 | //! - [`crate::types::core`] - Core protocol types and utilities 9 | //! - [`crate::types::domain`] - Validated domain types (Uri, MimeType, Base64String) 10 | //! - [`crate::types::capabilities`] - Client/server capability negotiation 11 | //! - [`crate::types::content`] - Message content types (text, image, audio, resources) 12 | //! - [`crate::types::requests`] - Request/response/notification enums 13 | //! - [`crate::types::initialization`] - Connection handshake types 14 | //! - [`crate::types::tools`] - Tool calling and execution 15 | //! - [`crate::types::prompts`] - Prompt templates 16 | //! - [`crate::types::resources`] - Resource access and templates 17 | //! - [`crate::types::logging`] - Logging and progress tracking 18 | //! - [`crate::types::sampling`] - LLM sampling (MCP 2025-06-18) 19 | //! - [`crate::types::elicitation`] - User input elicitation (MCP 2025-06-18) 20 | //! - [`crate::types::roots`] - Filesystem boundaries (MCP 2025-06-18) 21 | //! - [`crate::types::completion`] - Argument autocompletion 22 | //! - [`crate::types::ping`] - Connection testing 23 | 24 | pub mod capabilities; 25 | pub mod completion; 26 | pub mod content; 27 | pub mod core; 28 | pub mod domain; 29 | pub mod elicitation; 30 | pub mod initialization; 31 | pub mod logging; 32 | pub mod ping; 33 | pub mod prompts; 34 | pub mod requests; 35 | pub mod resources; 36 | pub mod roots; 37 | pub mod sampling; 38 | pub mod tools; 39 | 40 | // Re-export all types for backward compatibility 41 | pub use capabilities::*; 42 | pub use completion::*; 43 | pub use content::*; 44 | pub use core::*; 45 | pub use elicitation::*; 46 | pub use initialization::*; 47 | pub use logging::*; 48 | pub use ping::*; 49 | pub use prompts::*; 50 | pub use requests::*; 51 | pub use resources::*; 52 | pub use roots::*; 53 | pub use sampling::{ModelHint, *}; 54 | pub use tools::*; 55 | 56 | // Re-export validated domain types (these have the same names as type aliases in core, 57 | // but are distinct types with validation. Core type aliases are preferred for backward compat) 58 | pub use domain::{ 59 | Base64Error, 60 | MimeTypeError, 61 | UriError, 62 | // Note: Uri, MimeType, and Base64String from domain are NOT glob re-exported 63 | // to avoid ambiguity with the type aliases in core. Access them via domain::Uri, etc. 64 | }; 65 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/types/ping.rs: -------------------------------------------------------------------------------- 1 | //! Connection testing types 2 | //! 3 | //! This module contains types for MCP ping functionality, 4 | //! allowing connection health checking between clients and servers. 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /// Ping request parameters (optional data) 9 | #[derive(Debug, Clone, Serialize, Deserialize, Default)] 10 | pub struct PingParams { 11 | /// Optional data to echo back 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub data: Option, 14 | } 15 | 16 | /// Ping request wrapper 17 | #[derive(Debug, Clone, Serialize, Deserialize)] 18 | pub struct PingRequest { 19 | /// Ping parameters 20 | #[serde(flatten)] 21 | pub params: PingParams, 22 | } 23 | 24 | /// Ping result (echoes back the data) 25 | #[derive(Debug, Clone, Serialize, Deserialize)] 26 | pub struct PingResult { 27 | /// Echoed data from the request 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub data: Option, 30 | /// Optional metadata per MCP 2025-06-18 specification 31 | #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")] 32 | pub _meta: Option, 33 | } 34 | 35 | impl PingResult { 36 | /// Create a new ping result 37 | pub fn new(data: Option) -> Self { 38 | Self { data, _meta: None } 39 | } 40 | 41 | /// Create a ping result with no data 42 | pub fn empty() -> Self { 43 | Self::new(None) 44 | } 45 | 46 | /// Add metadata to this result 47 | pub fn with_meta(mut self, meta: serde_json::Value) -> Self { 48 | self._meta = Some(meta); 49 | self 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/turbomcp-protocol/src/types/roots.rs: -------------------------------------------------------------------------------- 1 | //! Filesystem boundaries types (MCP 2025-06-18) 2 | //! 3 | //! This module contains types for filesystem boundary discovery, 4 | //! allowing servers to understand client filesystem access boundaries. 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use super::core::Uri; 9 | 10 | /// Filesystem root definition 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | pub struct Root { 13 | /// Root URI (typically a file:// URI) 14 | pub uri: Uri, 15 | /// Optional human-readable name for this root 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub name: Option, 18 | } 19 | 20 | /// List roots request with optional metadata 21 | /// Note: Roots do not support pagination, only metadata 22 | #[derive(Debug, Clone, Serialize, Deserialize, Default)] 23 | pub struct ListRootsRequest { 24 | /// Optional metadata per MCP 2025-06-18 specification 25 | #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")] 26 | pub _meta: Option, 27 | } 28 | 29 | /// List roots result 30 | #[derive(Debug, Clone, Serialize, Deserialize)] 31 | pub struct ListRootsResult { 32 | /// Available filesystem roots 33 | pub roots: Vec, 34 | /// Optional metadata per MCP 2025-06-18 specification 35 | #[serde(skip_serializing_if = "Option::is_none")] 36 | pub _meta: Option, 37 | } 38 | 39 | /// Roots list changed notification (no parameters) 40 | #[derive(Debug, Clone, Serialize, Deserialize)] 41 | pub struct RootsListChangedNotification; 42 | -------------------------------------------------------------------------------- /crates/turbomcp-server/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-server/benches/middleware_benchmarks.rs: -------------------------------------------------------------------------------- 1 | //! Performance benchmarks for middleware layers 2 | //! 3 | //! To run these benchmarks, enable the required features: 4 | //! ```bash 5 | //! cargo bench --bench middleware_benchmarks --features "middleware,auth" 6 | //! ``` 7 | 8 | use criterion::{criterion_group, criterion_main}; 9 | 10 | #[cfg(feature = "middleware")] 11 | use criterion::{Criterion, black_box}; 12 | 13 | #[cfg(all(feature = "middleware", feature = "auth"))] 14 | use secrecy::Secret; 15 | #[cfg(all(feature = "middleware", feature = "auth"))] 16 | use turbomcp_server::middleware::auth::{AuthConfig, Claims}; 17 | #[cfg(feature = "middleware")] 18 | use turbomcp_server::middleware::rate_limit::{RateLimitConfig, RateLimitLayer}; 19 | 20 | #[cfg(feature = "middleware")] 21 | fn benchmark_rate_limit_config(c: &mut Criterion) { 22 | c.bench_function("rate_limit/create_strict", |b| { 23 | b.iter(|| black_box(RateLimitConfig::strict())) 24 | }); 25 | 26 | c.bench_function("rate_limit/calculate_rate", |b| { 27 | let layer = RateLimitLayer::new(RateLimitConfig::new(120)); 28 | b.iter(|| black_box(layer.requests_per_second())) 29 | }); 30 | } 31 | 32 | #[cfg(all(feature = "middleware", feature = "auth"))] 33 | fn benchmark_auth_config(c: &mut Criterion) { 34 | c.bench_function("auth/create_config", |b| { 35 | b.iter(|| black_box(AuthConfig::new(Secret::new("test".to_string())))) 36 | }); 37 | 38 | c.bench_function("auth/claims_validation", |b| { 39 | let now = std::time::SystemTime::now() 40 | .duration_since(std::time::UNIX_EPOCH) 41 | .unwrap() 42 | .as_secs(); 43 | 44 | let claims = Claims { 45 | sub: "user123".to_string(), 46 | roles: vec!["user".to_string()], 47 | exp: now + 3600, 48 | iat: now, 49 | iss: None, 50 | aud: None, 51 | }; 52 | 53 | b.iter(|| { 54 | black_box(claims.is_expired()); 55 | black_box(claims.has_role("user")); 56 | }) 57 | }); 58 | } 59 | 60 | #[cfg(all(feature = "middleware", feature = "auth"))] 61 | criterion_group!(benches, benchmark_rate_limit_config, benchmark_auth_config); 62 | 63 | #[cfg(all(feature = "middleware", not(feature = "auth")))] 64 | criterion_group!(benches, benchmark_rate_limit_config); 65 | 66 | #[cfg(not(feature = "middleware"))] 67 | fn no_benchmarks(_c: &mut criterion::Criterion) { 68 | // Middleware feature not enabled - run with: cargo bench --features middleware,auth 69 | } 70 | 71 | #[cfg(not(feature = "middleware"))] 72 | criterion_group!(benches, no_benchmarks); 73 | 74 | criterion_main!(benches); 75 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/composite.rs: -------------------------------------------------------------------------------- 1 | //! Composite handler pattern for handling multiple types of requests 2 | 3 | use crate::handlers::traits::*; 4 | 5 | /// Composite handler that can handle multiple types of requests 6 | pub trait CompositeHandler: Send + Sync { 7 | /// Get tool handler if this composite handles tools 8 | fn as_tool_handler(&self) -> Option<&dyn ToolHandler> { 9 | None 10 | } 11 | 12 | /// Get prompt handler if this composite handles prompts 13 | fn as_prompt_handler(&self) -> Option<&dyn PromptHandler> { 14 | None 15 | } 16 | 17 | /// Get resource handler if this composite handles resources 18 | fn as_resource_handler(&self) -> Option<&dyn ResourceHandler> { 19 | None 20 | } 21 | 22 | /// Get sampling handler if this composite handles sampling 23 | fn as_sampling_handler(&self) -> Option<&dyn SamplingHandler> { 24 | None 25 | } 26 | 27 | /// Get logging handler if this composite handles logging 28 | fn as_logging_handler(&self) -> Option<&dyn LoggingHandler> { 29 | None 30 | } 31 | 32 | /// Get elicitation handler if this composite handles elicitation 33 | fn as_elicitation_handler(&self) -> Option<&dyn ElicitationHandler> { 34 | None 35 | } 36 | 37 | /// Get completion handler if this composite handles completion 38 | fn as_completion_handler(&self) -> Option<&dyn CompletionHandler> { 39 | None 40 | } 41 | 42 | /// Get resource template handler if this composite handles resource templates 43 | fn as_resource_template_handler(&self) -> Option<&dyn ResourceTemplateHandler> { 44 | None 45 | } 46 | 47 | /// Get ping handler if this composite handles ping 48 | fn as_ping_handler(&self) -> Option<&dyn PingHandler> { 49 | None 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/implementations/function_tool.rs: -------------------------------------------------------------------------------- 1 | //! Function-based tool handler implementation 2 | 3 | use async_trait::async_trait; 4 | use std::sync::Arc; 5 | use turbomcp_protocol::RequestContext; 6 | use turbomcp_protocol::types::{CallToolRequest, CallToolResult, Tool}; 7 | 8 | use crate::ServerResult; 9 | use crate::handlers::traits::ToolHandler; 10 | 11 | type BoxFuture = std::pin::Pin + Send>>; 12 | 13 | /// Function-based tool handler 14 | pub struct FunctionToolHandler { 15 | /// Tool definition 16 | tool: Tool, 17 | /// Handler function 18 | handler: Arc< 19 | dyn Fn(CallToolRequest, RequestContext) -> BoxFuture> 20 | + Send 21 | + Sync, 22 | >, 23 | /// Allowed roles (RBAC) 24 | allowed_roles: Option>, 25 | } 26 | 27 | impl std::fmt::Debug for FunctionToolHandler { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | f.debug_struct("FunctionToolHandler") 30 | .field("tool", &self.tool) 31 | .finish() 32 | } 33 | } 34 | 35 | impl FunctionToolHandler { 36 | /// Create a new function-based tool handler 37 | pub fn new(tool: Tool, handler: F) -> Self 38 | where 39 | F: Fn(CallToolRequest, RequestContext) -> Fut + Send + Sync + 'static, 40 | Fut: std::future::Future> + Send + 'static, 41 | { 42 | Self::new_with_roles(tool, handler, None) 43 | } 44 | 45 | /// Create a new function-based tool handler with RBAC roles 46 | pub fn new_with_roles( 47 | tool: Tool, 48 | handler: F, 49 | allowed_roles: Option>, 50 | ) -> Self 51 | where 52 | F: Fn(CallToolRequest, RequestContext) -> Fut + Send + Sync + 'static, 53 | Fut: std::future::Future> + Send + 'static, 54 | { 55 | let handler = 56 | Arc::new(move |req, ctx| Box::pin(handler(req, ctx)) as futures::future::BoxFuture<_>); 57 | Self { 58 | tool, 59 | handler, 60 | allowed_roles, 61 | } 62 | } 63 | } 64 | 65 | #[async_trait] 66 | impl ToolHandler for FunctionToolHandler { 67 | async fn handle( 68 | &self, 69 | request: CallToolRequest, 70 | ctx: RequestContext, 71 | ) -> ServerResult { 72 | (self.handler)(request, ctx).await 73 | } 74 | 75 | fn tool_definition(&self) -> Tool { 76 | self.tool.clone() 77 | } 78 | 79 | fn allowed_roles(&self) -> Option<&[String]> { 80 | self.allowed_roles.as_deref() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/implementations/mod.rs: -------------------------------------------------------------------------------- 1 | //! Concrete handler implementations 2 | //! 3 | //! This module contains concrete implementations of the handler traits, 4 | //! including function-based handlers and other implementations. 5 | 6 | pub mod function_tool; 7 | 8 | // Re-export implementations for backwards compatibility 9 | pub use function_tool::FunctionToolHandler; 10 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | //! Handler traits and implementations for MCP operations 2 | //! 3 | //! This module contains the decomposed handler system with focused modules: 4 | //! 5 | //! - `traits`: All handler trait definitions 6 | //! - `composite`: Composite handler pattern for multi-type handlers 7 | //! - `wrapper`: Handler wrapper infrastructure with metadata 8 | //! - `implementations`: Concrete handler implementations 9 | //! - `utils`: Utility functions for creating handlers from closures 10 | 11 | use std::sync::Arc; 12 | 13 | // Core modules 14 | pub mod composite; 15 | pub mod implementations; 16 | pub mod traits; 17 | pub mod utils; 18 | pub mod wrapper; 19 | 20 | // Re-export main types for backwards compatibility 21 | pub use composite::CompositeHandler; 22 | pub use implementations::FunctionToolHandler; 23 | pub use traits::*; 24 | pub use utils::{FunctionPromptHandler, FunctionResourceHandler}; 25 | pub use wrapper::{HandlerMetadata, HandlerWrapper}; 26 | 27 | /// Type alias for existence check functions to reduce complexity 28 | pub type ExistenceCheckFn = 29 | Arc futures::future::BoxFuture<'static, bool> + Send + Sync>; 30 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/completion.rs: -------------------------------------------------------------------------------- 1 | //! Completion handler trait for argument autocompletion 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use turbomcp_protocol::RequestContext; 6 | use turbomcp_protocol::types::{CompleteRequestParams, CompletionResponse}; 7 | 8 | use crate::ServerResult; 9 | 10 | /// Completion handler trait for argument autocompletion 11 | #[async_trait] 12 | pub trait CompletionHandler: Send + Sync { 13 | /// Handle a completion request 14 | async fn handle( 15 | &self, 16 | request: CompleteRequestParams, 17 | ctx: RequestContext, 18 | ) -> ServerResult; 19 | 20 | /// Get maximum number of completions to return 21 | fn max_completions(&self) -> usize { 22 | 50 23 | } 24 | 25 | /// Check if completion is supported for the given reference 26 | fn supports_completion(&self, _reference: &str) -> bool { 27 | true 28 | } 29 | 30 | /// Get completion suggestions based on context 31 | async fn get_completions( 32 | &self, 33 | reference: &str, 34 | argument: Option<&str>, 35 | partial_value: Option<&str>, 36 | ctx: RequestContext, 37 | ) -> ServerResult>; 38 | 39 | /// Filter and rank completion options 40 | fn filter_completions( 41 | &self, 42 | completions: Vec, 43 | partial_value: Option<&str>, 44 | ) -> Vec { 45 | // Default implementation: simple prefix matching 46 | if let Some(partial) = partial_value { 47 | let partial_lower = partial.to_lowercase(); 48 | completions 49 | .into_iter() 50 | .filter(|comp| { 51 | if let Some(value) = comp.get("value").and_then(|v| v.as_str()) { 52 | value.to_lowercase().starts_with(&partial_lower) 53 | } else { 54 | false 55 | } 56 | }) 57 | .take(self.max_completions()) 58 | .collect() 59 | } else { 60 | completions 61 | .into_iter() 62 | .take(self.max_completions()) 63 | .collect() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/elicitation.rs: -------------------------------------------------------------------------------- 1 | //! Elicitation handler trait for server-initiated user input requests 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use std::collections::HashMap; 6 | use turbomcp_protocol::RequestContext; 7 | use turbomcp_protocol::types::{ElicitRequest, ElicitResult}; 8 | 9 | use crate::ServerResult; 10 | 11 | /// Elicitation handler trait for server-initiated user input requests 12 | #[async_trait] 13 | pub trait ElicitationHandler: Send + Sync { 14 | /// Handle an elicitation request (server-initiated user input) 15 | async fn handle( 16 | &self, 17 | request: ElicitRequest, 18 | ctx: RequestContext, 19 | ) -> ServerResult; 20 | 21 | /// Validate elicitation schema (optional, default implementation allows all) 22 | fn validate_schema(&self, _schema: &Value) -> ServerResult<()> { 23 | Ok(()) 24 | } 25 | 26 | /// Get default timeout for user response in milliseconds 27 | fn default_timeout_ms(&self) -> u64 { 28 | 60_000 // 1 minute default 29 | } 30 | 31 | /// Check if elicitation is cancellable 32 | fn is_cancellable(&self) -> bool { 33 | true 34 | } 35 | 36 | /// Handle elicitation cancellation 37 | async fn handle_cancellation( 38 | &self, 39 | _request_id: &str, 40 | _ctx: RequestContext, 41 | ) -> ServerResult<()> { 42 | Ok(()) 43 | } 44 | 45 | /// Process user response for validation 46 | async fn process_response( 47 | &self, 48 | _response: &HashMap, 49 | _schema: &Value, 50 | _ctx: RequestContext, 51 | ) -> ServerResult> { 52 | // Default implementation returns response as-is 53 | Ok(_response.clone()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/logging.rs: -------------------------------------------------------------------------------- 1 | //! Logging handler trait for processing logging requests 2 | 3 | use async_trait::async_trait; 4 | use turbomcp_protocol::RequestContext; 5 | use turbomcp_protocol::types::LogLevel; 6 | use turbomcp_protocol::types::{EmptyResult, LoggingCapabilities, SetLevelRequest}; 7 | 8 | use crate::ServerResult; 9 | 10 | /// Logging handler trait for processing logging requests 11 | #[async_trait] 12 | pub trait LoggingHandler: Send + Sync { 13 | /// Handle a log level change request 14 | async fn handle( 15 | &self, 16 | request: SetLevelRequest, 17 | ctx: RequestContext, 18 | ) -> ServerResult; 19 | 20 | /// Get current log level 21 | fn current_level(&self) -> LogLevel; 22 | 23 | /// Get logging capabilities 24 | fn logging_capabilities(&self) -> LoggingCapabilities { 25 | LoggingCapabilities 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/mod.rs: -------------------------------------------------------------------------------- 1 | //! Handler traits for MCP operations 2 | //! 3 | //! This module contains all the handler traits that define the interface 4 | //! for processing different types of MCP requests and operations. 5 | 6 | pub mod completion; 7 | pub mod elicitation; 8 | pub mod logging; 9 | pub mod ping; 10 | pub mod prompt; 11 | pub mod resource; 12 | pub mod resource_template; 13 | pub mod sampling; 14 | pub mod tool; 15 | 16 | // Re-export all traits for backwards compatibility 17 | pub use completion::CompletionHandler; 18 | pub use elicitation::ElicitationHandler; 19 | pub use logging::LoggingHandler; 20 | pub use ping::PingHandler; 21 | pub use prompt::PromptHandler; 22 | pub use resource::ResourceHandler; 23 | pub use resource_template::ResourceTemplateHandler; 24 | pub use sampling::SamplingHandler; 25 | pub use tool::ToolHandler; 26 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/ping.rs: -------------------------------------------------------------------------------- 1 | //! Ping handler trait for bidirectional health monitoring 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use turbomcp_protocol::RequestContext; 6 | use turbomcp_protocol::types::{PingRequest, PingResult}; 7 | 8 | use crate::ServerResult; 9 | 10 | /// Ping handler trait for bidirectional health monitoring 11 | #[async_trait] 12 | pub trait PingHandler: Send + Sync { 13 | /// Handle a ping request 14 | async fn handle(&self, request: PingRequest, ctx: RequestContext) -> ServerResult; 15 | 16 | /// Get current health status 17 | async fn get_health_status(&self, _ctx: RequestContext) -> ServerResult { 18 | Ok(serde_json::json!({ 19 | "status": "healthy", 20 | "timestamp": chrono::Utc::now().to_rfc3339(), 21 | })) 22 | } 23 | 24 | /// Get connection metrics if available 25 | async fn get_connection_metrics(&self, _ctx: RequestContext) -> ServerResult> { 26 | Ok(None) // Default: no metrics 27 | } 28 | 29 | /// Handle ping timeout 30 | async fn handle_timeout(&self, _request_id: &str, _ctx: RequestContext) -> ServerResult<()> { 31 | Ok(()) 32 | } 33 | 34 | /// Check if ping should include detailed health information 35 | fn include_health_details(&self) -> bool { 36 | false 37 | } 38 | 39 | /// Get expected response time threshold in milliseconds 40 | fn response_threshold_ms(&self) -> u64 { 41 | 5_000 // 5 seconds default 42 | } 43 | 44 | /// Process custom ping payload 45 | async fn process_ping_payload( 46 | &self, 47 | payload: Option<&Value>, 48 | _ctx: RequestContext, 49 | ) -> ServerResult> { 50 | // Default: echo back the payload 51 | Ok(payload.cloned()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/prompt.rs: -------------------------------------------------------------------------------- 1 | //! Prompt handler trait for processing prompt requests 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use std::collections::HashMap; 6 | use turbomcp_protocol::RequestContext; 7 | use turbomcp_protocol::types::{GetPromptRequest, GetPromptResult, Prompt}; 8 | 9 | use crate::ServerResult; 10 | 11 | /// Prompt handler trait for processing prompt requests 12 | #[async_trait] 13 | pub trait PromptHandler: Send + Sync { 14 | /// Handle a prompt request 15 | async fn handle( 16 | &self, 17 | request: GetPromptRequest, 18 | ctx: RequestContext, 19 | ) -> ServerResult; 20 | 21 | /// Get the prompt definition 22 | fn prompt_definition(&self) -> Prompt; 23 | 24 | /// Validate prompt arguments (optional, default implementation allows all) 25 | fn validate_arguments(&self, _args: &HashMap) -> ServerResult<()> { 26 | Ok(()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/resource.rs: -------------------------------------------------------------------------------- 1 | //! Resource handler trait for processing resource requests 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use std::collections::HashMap; 6 | use turbomcp_protocol::RequestContext; 7 | use turbomcp_protocol::types::{ReadResourceRequest, ReadResourceResult, Resource}; 8 | 9 | use crate::ServerResult; 10 | 11 | /// Resource handler trait for processing resource requests 12 | #[async_trait] 13 | pub trait ResourceHandler: Send + Sync { 14 | /// Handle a resource read request 15 | async fn handle( 16 | &self, 17 | request: ReadResourceRequest, 18 | ctx: RequestContext, 19 | ) -> ServerResult; 20 | 21 | /// Get the resource definition 22 | fn resource_definition(&self) -> Resource; 23 | 24 | /// Check if resource exists 25 | async fn exists(&self, uri: &str) -> bool; 26 | 27 | /// Get resource metadata 28 | async fn metadata(&self, _uri: &str) -> Option> { 29 | None 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/resource_template.rs: -------------------------------------------------------------------------------- 1 | //! Resource template handler trait for parameterized resource access 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use std::collections::HashMap; 6 | use turbomcp_protocol::RequestContext; 7 | use turbomcp_protocol::types::{ 8 | ListResourceTemplatesRequest, ListResourceTemplatesResult, ResourceTemplate, 9 | }; 10 | 11 | use crate::ServerResult; 12 | 13 | /// Resource template handler trait for parameterized resource access 14 | #[async_trait] 15 | pub trait ResourceTemplateHandler: Send + Sync { 16 | /// Handle a list resource templates request 17 | async fn handle( 18 | &self, 19 | request: ListResourceTemplatesRequest, 20 | ctx: RequestContext, 21 | ) -> ServerResult; 22 | 23 | /// Get available resource templates 24 | async fn get_templates(&self, ctx: RequestContext) -> ServerResult>; 25 | 26 | /// Get a specific template by name 27 | async fn get_template( 28 | &self, 29 | name: &str, 30 | ctx: RequestContext, 31 | ) -> ServerResult>; 32 | 33 | /// Validate template URI pattern (RFC 6570) 34 | fn validate_uri_template(&self, uri_template: &str) -> ServerResult<()> { 35 | // Basic validation - can be overridden for more sophisticated checking 36 | if uri_template.is_empty() { 37 | return Err(crate::ServerError::Handler { 38 | message: "URI template cannot be empty".to_string(), 39 | context: None, 40 | }); 41 | } 42 | Ok(()) 43 | } 44 | 45 | /// Expand URI template with parameters 46 | fn expand_template( 47 | &self, 48 | uri_template: &str, 49 | parameters: &HashMap, 50 | ) -> ServerResult { 51 | // Basic template expansion - should be overridden for full RFC 6570 support 52 | let mut result = uri_template.to_string(); 53 | 54 | for (key, value) in parameters { 55 | let placeholder = format!("{{{}}}", key); 56 | if let Some(str_value) = value.as_str() { 57 | result = result.replace(&placeholder, str_value); 58 | } else { 59 | result = result.replace(&placeholder, &value.to_string()); 60 | } 61 | } 62 | 63 | Ok(result) 64 | } 65 | 66 | /// Validate template parameters 67 | async fn validate_parameters( 68 | &self, 69 | _template: &ResourceTemplate, 70 | _parameters: &HashMap, 71 | _ctx: RequestContext, 72 | ) -> ServerResult<()> { 73 | // Default implementation - no validation 74 | // Override in implementations to add specific parameter validation 75 | Ok(()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/sampling.rs: -------------------------------------------------------------------------------- 1 | //! Sampling handler trait for processing sampling requests 2 | 3 | use async_trait::async_trait; 4 | use turbomcp_protocol::RequestContext; 5 | use turbomcp_protocol::types::{CreateMessageRequest, CreateMessageResult, SamplingCapabilities}; 6 | 7 | use crate::ServerResult; 8 | 9 | /// Sampling handler trait for processing sampling requests 10 | #[async_trait] 11 | pub trait SamplingHandler: Send + Sync { 12 | /// Handle a sampling request 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `request_id` - The JSON-RPC request ID for response correlation 17 | /// * `request` - The sampling request parameters 18 | /// * `ctx` - The request context 19 | async fn handle( 20 | &self, 21 | request_id: String, 22 | request: CreateMessageRequest, 23 | ctx: RequestContext, 24 | ) -> ServerResult; 25 | 26 | /// Get supported sampling capabilities 27 | fn sampling_capabilities(&self) -> SamplingCapabilities { 28 | SamplingCapabilities 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/handlers/traits/tool.rs: -------------------------------------------------------------------------------- 1 | //! Tool handler trait for processing tool calls 2 | 3 | use async_trait::async_trait; 4 | use serde_json::Value; 5 | use turbomcp_protocol::RequestContext; 6 | use turbomcp_protocol::types::{CallToolRequest, CallToolResult, Tool}; 7 | 8 | use crate::ServerResult; 9 | 10 | /// Tool handler trait for processing tool calls 11 | #[async_trait] 12 | pub trait ToolHandler: Send + Sync { 13 | /// Handle a tool call request 14 | async fn handle( 15 | &self, 16 | request: CallToolRequest, 17 | ctx: RequestContext, 18 | ) -> ServerResult; 19 | 20 | /// Get the tool definition 21 | fn tool_definition(&self) -> Tool; 22 | 23 | /// Validate tool input (optional, default implementation allows all) 24 | fn validate_input(&self, _input: &Value) -> ServerResult<()> { 25 | Ok(()) 26 | } 27 | 28 | /// Allowed roles for this tool (RBAC). None means unrestricted. 29 | fn allowed_roles(&self) -> Option<&[String]> { 30 | None 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/main.rs: -------------------------------------------------------------------------------- 1 | use turbomcp_server::{ServerBuilder, default_config}; 2 | 3 | #[tokio::main] 4 | async fn main() -> Result<(), Box> { 5 | // Initialize logging if available 6 | let _ = tracing_subscriber::fmt::try_init(); 7 | 8 | // Build server with default config 9 | let config = default_config(); 10 | let server = ServerBuilder::new() 11 | .name(config.name.clone()) 12 | .version(config.version.clone()) 13 | .build(); 14 | 15 | // Run via stdio transport 16 | server.run_stdio().await.map_err(|e| e.into()) 17 | } 18 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/policies/rbac_model.conf: -------------------------------------------------------------------------------- 1 | # RBAC Model for MCP Server Authorization 2 | # This defines Role-Based Access Control for Model Context Protocol servers 3 | # 4 | # Based on Casbin RBAC model: https://casbin.org/docs/supported-models#rbac 5 | 6 | [request_definition] 7 | r = sub, obj, act 8 | 9 | [policy_definition] 10 | p = sub, obj, act 11 | 12 | [role_definition] 13 | g = _, _ 14 | 15 | [policy_effect] 16 | e = some(where (p.eft == allow)) 17 | 18 | [matchers] 19 | m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act 20 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/policies/rbac_policy.csv: -------------------------------------------------------------------------------- 1 | # Casbin RBAC Policy for MCP Server 2 | # Format: p, role, resource, action 3 | # Format: g, user, role 4 | 5 | # Policy Definitions (p) 6 | # Admin role - full access to all MCP resources 7 | p, admin, mcp, * 8 | p, admin, tools, * 9 | p, admin, prompts, * 10 | p, admin, resources, * 11 | p, admin, completion, * 12 | p, admin, logging, * 13 | p, admin, sampling, * 14 | 15 | # Developer role - read/write access to development resources 16 | p, developer, mcp, request 17 | p, developer, tools, list 18 | p, developer, tools, call 19 | p, developer, prompts, list 20 | p, developer, prompts, get 21 | p, developer, resources, list 22 | p, developer, resources, read 23 | p, developer, completion, complete 24 | 25 | # User role - standard access to tools and prompts 26 | p, user, mcp, request 27 | p, user, tools, list 28 | p, user, tools, call 29 | p, user, prompts, list 30 | p, user, prompts, get 31 | p, user, resources, list 32 | p, user, resources, read 33 | 34 | # Read-only role - view-only access 35 | p, readonly, mcp, request 36 | p, readonly, tools, list 37 | p, readonly, prompts, list 38 | p, readonly, resources, list 39 | 40 | # Guest role - minimal access (public resources only) 41 | p, guest, mcp, list 42 | p, guest, tools, list 43 | 44 | # Role Assignments (g) 45 | # Example: g, alice, admin 46 | # g, alice, admin 47 | # g, bob, developer 48 | # g, charlie, user 49 | # g, dave, readonly 50 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/config.rs: -------------------------------------------------------------------------------- 1 | //! Router configuration types and defaults 2 | 3 | /// Router configuration 4 | #[derive(Debug, Clone)] 5 | pub struct RouterConfig { 6 | /// Enable request validation 7 | pub validate_requests: bool, 8 | /// Enable response validation 9 | pub validate_responses: bool, 10 | /// Default request timeout in milliseconds 11 | pub default_timeout_ms: u64, 12 | /// Enable request tracing 13 | pub enable_tracing: bool, 14 | /// Maximum concurrent requests 15 | pub max_concurrent_requests: usize, 16 | /// Enable bidirectional routing (server-initiated requests) 17 | pub enable_bidirectional: bool, 18 | } 19 | 20 | impl Default for RouterConfig { 21 | fn default() -> Self { 22 | Self { 23 | validate_requests: true, 24 | validate_responses: true, 25 | default_timeout_ms: 30_000, 26 | enable_tracing: true, 27 | max_concurrent_requests: 1000, 28 | enable_bidirectional: true, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/completion.rs: -------------------------------------------------------------------------------- 1 | //! Completion handler for MCP completion operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{CompleteRequestParams, CompleteResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle completion request 13 | /// 14 | /// This handler provides protocol-level routing for completion requests. 15 | /// Completions provide autocomplete suggestions for prompt arguments and 16 | /// resource URIs, enhancing the user experience in MCP clients. 17 | /// 18 | /// **Implementation Note:** 19 | /// By default, returns empty completion results. Applications should implement 20 | /// completion logic in their prompts and resources using the `#[complete]` attribute: 21 | /// 22 | /// ```rust,ignore 23 | /// #[prompt("code_review")] 24 | /// async fn code_review( 25 | /// &self, 26 | /// language: String, 27 | /// #[complete] framework: String, // Auto-completion enabled 28 | /// ) -> McpResult { ... } 29 | /// ``` 30 | /// 31 | /// The framework will automatically route completion requests to the appropriate 32 | /// handlers when using `#[complete]` attributes. Custom completion logic can also 33 | /// be provided via middleware or custom handlers. 34 | pub async fn handle( 35 | _context: &HandlerContext, 36 | request: JsonRpcRequest, 37 | _ctx: RequestContext, 38 | ) -> JsonRpcResponse { 39 | match parse_params::(&request) { 40 | Ok(_complete_request) => { 41 | // Default: no completions (implement via #[complete] attributes or middleware) 42 | let result = CompleteResult { 43 | completion: turbomcp_protocol::types::CompletionData { 44 | values: vec![], 45 | total: Some(0), 46 | has_more: Some(false), 47 | }, 48 | _meta: None, 49 | }; 50 | success_response(&request, result) 51 | } 52 | Err(e) => error_response(&request, e), 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/elicitation.rs: -------------------------------------------------------------------------------- 1 | //! Elicitation handler for MCP elicitation operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{ElicitRequest, ElicitResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle elicitation request 13 | /// 14 | /// This handler provides protocol-level routing for elicitation requests. 15 | /// Elicitation is initiated by tools (not as standalone protocol requests), 16 | /// so this returns a default successful response for protocol compliance. 17 | /// 18 | /// **Implementation Note:** 19 | /// Applications implement elicitation using the Context API within tools: 20 | /// ```rust,ignore 21 | /// #[tool("Setup user profile")] 22 | /// async fn setup_profile(&self, ctx: Context) -> McpResult { 23 | /// let schema = json!({"type": "object", "properties": {...}}); 24 | /// // Client handles elicitation via ctx.elicit() in application code 25 | /// Ok("Profile configured".to_string()) 26 | /// } 27 | /// ``` 28 | /// 29 | /// See `examples/08_elicitation_server.rs` for complete examples. 30 | pub async fn handle( 31 | _context: &HandlerContext, 32 | request: JsonRpcRequest, 33 | _ctx: RequestContext, 34 | ) -> JsonRpcResponse { 35 | match parse_params::(&request) { 36 | Ok(_elicit_request) => { 37 | // Protocol compliance: elicitation is user-implemented via Context API 38 | let result = ElicitResult { 39 | action: turbomcp_protocol::types::ElicitationAction::Accept, 40 | content: Some(std::collections::HashMap::new()), 41 | _meta: None, 42 | }; 43 | success_response(&request, result) 44 | } 45 | Err(e) => error_response(&request, e), 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/initialize.rs: -------------------------------------------------------------------------------- 1 | //! Initialize handler for MCP protocol handshake 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{ 7 | Implementation, InitializeRequest, InitializeResult, LoggingCapabilities, 8 | PromptsCapabilities, ResourcesCapabilities, ServerCapabilities, ToolsCapabilities, 9 | }, 10 | }; 11 | 12 | use super::HandlerContext; 13 | use crate::routing::utils::{error_response, parse_params, success_response}; 14 | 15 | /// Handle initialize request 16 | pub async fn handle( 17 | context: &HandlerContext, 18 | request: JsonRpcRequest, 19 | _ctx: RequestContext, 20 | ) -> JsonRpcResponse { 21 | match parse_params::(&request) { 22 | Ok(_init_request) => { 23 | let result = InitializeResult { 24 | protocol_version: turbomcp_protocol::PROTOCOL_VERSION.to_string(), 25 | server_info: Implementation { 26 | name: context.config.name.clone(), 27 | title: context.config.description.clone(), 28 | version: context.config.version.clone(), 29 | }, 30 | capabilities: get_server_capabilities(context), 31 | instructions: None, 32 | _meta: None, 33 | }; 34 | 35 | success_response(&request, result) 36 | } 37 | Err(e) => error_response(&request, e), 38 | } 39 | } 40 | 41 | /// Get server capabilities based on registry state 42 | fn get_server_capabilities(context: &HandlerContext) -> ServerCapabilities { 43 | ServerCapabilities { 44 | tools: if context.registry.tools.is_empty() { 45 | None 46 | } else { 47 | Some(ToolsCapabilities::default()) 48 | }, 49 | prompts: if context.registry.prompts.is_empty() { 50 | None 51 | } else { 52 | Some(PromptsCapabilities::default()) 53 | }, 54 | resources: if context.registry.resources.is_empty() { 55 | None 56 | } else { 57 | Some(ResourcesCapabilities::default()) 58 | }, 59 | logging: if !context.registry.logging.is_empty() { 60 | Some(LoggingCapabilities) 61 | } else { 62 | None 63 | }, 64 | experimental: None, 65 | completions: None, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/logging.rs: -------------------------------------------------------------------------------- 1 | //! Logging handler for MCP logging operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{SetLevelRequest, SetLevelResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle set log level request 13 | /// 14 | /// This handler provides protocol-level routing for logging/setLevel requests. 15 | /// Clients can request the server to change its logging verbosity at runtime. 16 | /// 17 | /// **Implementation Note:** 18 | /// By default, accepts the request but doesn't modify logging configuration. 19 | /// Applications should integrate with their logging framework: 20 | /// 21 | /// ```rust,ignore 22 | /// // In application initialization: 23 | /// use tracing_subscriber::EnvFilter; 24 | /// 25 | /// let filter = Arc::new(RwLock::new( 26 | /// EnvFilter::from_default_env() 27 | /// )); 28 | /// 29 | /// // In custom logging middleware or handler: 30 | /// async fn handle_set_level(level: LogLevel) -> McpResult<()> { 31 | /// let mut filter = app_filter.write().await; 32 | /// *filter = EnvFilter::new(match level { 33 | /// LogLevel::Debug => "debug", 34 | /// LogLevel::Info => "info", 35 | /// LogLevel::Warn => "warn", 36 | /// LogLevel::Error => "error", 37 | /// }); 38 | /// Ok(()) 39 | /// } 40 | /// ``` 41 | /// 42 | /// This allows integration with `tracing`, `log`, `env_logger`, or custom 43 | /// logging systems while maintaining MCP protocol compliance. 44 | pub async fn handle_set_level( 45 | _context: &HandlerContext, 46 | request: JsonRpcRequest, 47 | _ctx: RequestContext, 48 | ) -> JsonRpcResponse { 49 | match parse_params::(&request) { 50 | Ok(_set_level_request) => { 51 | // Protocol compliance: integrate with your logging framework for dynamic level changes 52 | let result = SetLevelResult; 53 | success_response(&request, result) 54 | } 55 | Err(e) => error_response(&request, e), 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/ping.rs: -------------------------------------------------------------------------------- 1 | //! Ping handler for health check operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{PingRequest, PingResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle ping request - basic health check response 13 | pub async fn handle( 14 | _context: &HandlerContext, 15 | request: JsonRpcRequest, 16 | _ctx: RequestContext, 17 | ) -> JsonRpcResponse { 18 | match parse_params::(&request) { 19 | Ok(_ping_request) => { 20 | // Default ping handler - basic health check response 21 | let result = PingResult::empty().with_meta(serde_json::json!({ 22 | "status": "healthy", 23 | "timestamp": chrono::Utc::now().to_rfc3339(), 24 | "server": "turbomcp-server", 25 | })); 26 | success_response(&request, result) 27 | } 28 | Err(e) => error_response(&request, e), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/prompts.rs: -------------------------------------------------------------------------------- 1 | //! Prompt handlers for MCP prompt operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{GetPromptRequest, ListPromptsResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::ServerError; 11 | use crate::routing::utils::{error_response, parse_params, success_response}; 12 | 13 | /// Handle list prompts request 14 | pub async fn handle_list( 15 | context: &HandlerContext, 16 | request: JsonRpcRequest, 17 | _ctx: RequestContext, 18 | ) -> JsonRpcResponse { 19 | let prompts = context.registry.get_prompt_definitions(); 20 | let result = ListPromptsResult { 21 | prompts, 22 | next_cursor: None, 23 | _meta: None, 24 | }; 25 | success_response(&request, result) 26 | } 27 | 28 | /// Handle get prompt request 29 | pub async fn handle_get( 30 | context: &HandlerContext, 31 | request: JsonRpcRequest, 32 | ctx: RequestContext, 33 | ) -> JsonRpcResponse { 34 | match parse_params::(&request) { 35 | Ok(prompt_request) => { 36 | if let Some(handler) = context.registry.get_prompt(&prompt_request.name) { 37 | match handler.handle(prompt_request, ctx).await { 38 | Ok(prompt_result) => success_response(&request, prompt_result), 39 | Err(e) => error_response(&request, e), 40 | } 41 | } else { 42 | let error = ServerError::not_found(format!("Prompt '{}'", prompt_request.name)); 43 | error_response(&request, error) 44 | } 45 | } 46 | Err(e) => error_response(&request, e), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/roots.rs: -------------------------------------------------------------------------------- 1 | //! Roots handler for MCP roots operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{ListRootsRequest, ListRootsResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle list roots request 13 | /// 14 | /// This handler provides protocol-level routing for roots listing. 15 | /// Applications define their filesystem roots via server configuration, 16 | /// middleware, or custom handlers. 17 | /// 18 | /// **Implementation Note:** 19 | /// By default, returns an empty roots list. Applications should: 20 | /// 1. Configure roots via server builder: `.with_roots(vec![...])` 21 | /// 2. Implement custom middleware to provide dynamic roots 22 | /// 3. Override this handler with application-specific logic 23 | /// 24 | /// This allows flexibility for different deployment patterns (containers, 25 | /// sandboxed environments, multi-tenant systems, etc.). 26 | pub async fn handle_list( 27 | _context: &HandlerContext, 28 | request: JsonRpcRequest, 29 | _ctx: RequestContext, 30 | ) -> JsonRpcResponse { 31 | match parse_params::(&request) { 32 | Ok(_roots_request) => { 33 | // Default: empty roots list (configure via server builder or middleware) 34 | let result = ListRootsResult { 35 | roots: vec![], 36 | _meta: None, 37 | }; 38 | success_response(&request, result) 39 | } 40 | Err(e) => error_response(&request, e), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/sampling.rs: -------------------------------------------------------------------------------- 1 | //! Sampling handler for MCP sampling operations 2 | 3 | use turbomcp_protocol::RequestContext; 4 | use turbomcp_protocol::{ 5 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 6 | types::{CreateMessageRequest, CreateMessageResult}, 7 | }; 8 | 9 | use super::HandlerContext; 10 | use crate::routing::utils::{error_response, parse_params, success_response}; 11 | 12 | /// Handle create message request 13 | /// 14 | /// This handler provides protocol-level routing for sampling/createMessage requests. 15 | /// The sampling protocol allows servers to request LLM completions from clients. 16 | /// 17 | /// **Implementation Note:** 18 | /// This is a server-to-client request pattern. Servers initiate sampling by sending 19 | /// `sampling/createMessage` requests to clients, which then forward them to their 20 | /// configured LLM provider (OpenAI, Anthropic, local model, etc.). 21 | /// 22 | /// This handler returns a placeholder response for protocol compliance, but in 23 | /// production MCP servers: 24 | /// 1. The server sends sampling requests TO the client (not receives them) 25 | /// 2. The client implements `SamplingHandler` to route to their LLM provider 26 | /// 3. The client returns the LLM's response back to the server 27 | /// 28 | /// See `turbomcp-client` documentation for implementing sampling handlers. 29 | pub async fn handle_create_message( 30 | _context: &HandlerContext, 31 | request: JsonRpcRequest, 32 | _ctx: RequestContext, 33 | ) -> JsonRpcResponse { 34 | match parse_params::(&request) { 35 | Ok(_create_request) => { 36 | // Protocol compliance: sampling is a server-to-client request pattern 37 | // Servers initiate sampling, clients implement the LLM integration 38 | let result = CreateMessageResult { 39 | model: "default".to_string(), 40 | role: turbomcp_protocol::types::Role::Assistant, 41 | content: turbomcp_protocol::types::Content::Text( 42 | turbomcp_protocol::types::TextContent { 43 | text: "Sample response".to_string(), 44 | annotations: None, 45 | meta: None, 46 | }, 47 | ), 48 | stop_reason: Some(turbomcp_protocol::types::StopReason::EndTurn), 49 | _meta: None, 50 | }; 51 | success_response(&request, result) 52 | } 53 | Err(e) => error_response(&request, e), 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/handlers/tools.rs: -------------------------------------------------------------------------------- 1 | //! Tool handlers for MCP tool operations - PURE BUSINESS LOGIC ONLY 2 | //! 3 | //! This module contains only the core business logic for tool operations. 4 | //! All cross-cutting concerns (logging, timeout, performance) are handled by middleware. 5 | 6 | use turbomcp_protocol::RequestContext; 7 | use turbomcp_protocol::{ 8 | jsonrpc::{JsonRpcRequest, JsonRpcResponse}, 9 | types::{CallToolRequest, ListToolsResult}, 10 | }; 11 | 12 | use super::HandlerContext; 13 | use crate::ServerError; 14 | use crate::routing::utils::{error_response, parse_params, success_response}; 15 | 16 | /// Handle list tools request 17 | pub async fn handle_list( 18 | context: &HandlerContext, 19 | request: JsonRpcRequest, 20 | _ctx: RequestContext, 21 | ) -> JsonRpcResponse { 22 | let tools = context.registry.get_tool_definitions(); 23 | let result = ListToolsResult { 24 | tools, 25 | next_cursor: None, 26 | _meta: None, 27 | }; 28 | success_response(&request, result) 29 | } 30 | 31 | /// Handle call tool request - pure business logic only 32 | pub async fn handle_call( 33 | context: &HandlerContext, 34 | request: JsonRpcRequest, 35 | ctx: RequestContext, 36 | ) -> JsonRpcResponse { 37 | match parse_params::(&request) { 38 | Ok(call_request) => { 39 | let tool_name = call_request.name.clone(); 40 | 41 | if let Some(handler) = context.registry.get_tool(&tool_name) { 42 | match handler.handle(call_request, ctx).await { 43 | Ok(tool_result) => success_response(&request, tool_result), 44 | Err(e) => error_response(&request, e), 45 | } 46 | } else { 47 | let error = ServerError::not_found(format!("Tool '{tool_name}'")); 48 | error_response(&request, error) 49 | } 50 | } 51 | Err(e) => error_response(&request, e), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/routing/utils.rs: -------------------------------------------------------------------------------- 1 | //! Router utility functions for parsing, validation, and responses 2 | 3 | use turbomcp_protocol::jsonrpc::{JsonRpcRequest, JsonRpcResponse}; 4 | 5 | use crate::{ServerError, ServerResult}; 6 | 7 | /// Parse request parameters from JSON-RPC request 8 | pub fn parse_params(request: &JsonRpcRequest) -> ServerResult 9 | where 10 | T: serde::de::DeserializeOwned, 11 | { 12 | match &request.params { 13 | Some(params) => serde_json::from_value(params.clone()).map_err(|e| { 14 | ServerError::routing_with_method( 15 | format!("Invalid parameters: {e}"), 16 | request.method.clone(), 17 | ) 18 | }), 19 | None => Err(ServerError::routing_with_method( 20 | "Missing required parameters".to_string(), 21 | request.method.clone(), 22 | )), 23 | } 24 | } 25 | 26 | /// Create a success response for JSON-RPC request 27 | pub fn success_response(request: &JsonRpcRequest, result: T) -> JsonRpcResponse 28 | where 29 | T: serde::Serialize, 30 | { 31 | JsonRpcResponse::success(serde_json::to_value(result).unwrap(), request.id.clone()) 32 | } 33 | 34 | /// Create an error response for JSON-RPC request 35 | pub fn error_response(request: &JsonRpcRequest, error: ServerError) -> JsonRpcResponse { 36 | JsonRpcResponse::error_response( 37 | turbomcp_protocol::jsonrpc::JsonRpcError { 38 | code: error.error_code(), 39 | message: error.to_string(), 40 | data: None, 41 | }, 42 | request.id.clone(), 43 | ) 44 | } 45 | 46 | /// Create a method not found response for JSON-RPC request 47 | pub fn method_not_found_response(request: &JsonRpcRequest) -> JsonRpcResponse { 48 | JsonRpcResponse::error_response( 49 | turbomcp_protocol::jsonrpc::JsonRpcError { 50 | code: -32601, 51 | message: format!("Method '{}' not found", request.method), 52 | data: None, 53 | }, 54 | request.id.clone(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/schemas/mcp_tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "MCP Tools Request Schemas", 4 | "definitions": { 5 | "call_tool_request": { 6 | "type": "object", 7 | "required": ["name"], 8 | "properties": { 9 | "name": { 10 | "type": "string", 11 | "description": "Tool name to call", 12 | "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$" 13 | }, 14 | "arguments": { 15 | "type": "object", 16 | "description": "Tool arguments", 17 | "additionalProperties": true 18 | }, 19 | "_meta": { 20 | "type": "object", 21 | "description": "Optional metadata per MCP 2025-06-18 specification" 22 | } 23 | }, 24 | "additionalProperties": false 25 | }, 26 | "list_tools_request": { 27 | "type": "object", 28 | "properties": { 29 | "cursor": { 30 | "type": "string", 31 | "description": "Pagination cursor" 32 | }, 33 | "_meta": { 34 | "type": "object", 35 | "description": "Optional metadata per MCP 2025-06-18 specification" 36 | } 37 | }, 38 | "additionalProperties": false 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /crates/turbomcp-server/src/server/mod.rs: -------------------------------------------------------------------------------- 1 | //! MCP server core implementation 2 | //! 3 | //! This module contains the decomposed server implementation with focused 4 | //! modules for different responsibilities: 5 | //! 6 | //! - `core`: Main server implementation and middleware stack (Clone for sharing) 7 | //! - `transport`: Transport message handling for JSON-RPC 8 | //! - `builder`: Server builder pattern for construction 9 | //! - `shutdown`: Graceful shutdown coordination 10 | 11 | // Core modules 12 | pub mod builder; 13 | pub mod core; 14 | pub mod shutdown; 15 | pub mod transport; 16 | 17 | // Re-export main types for backwards compatibility 18 | pub use builder::ServerBuilder; 19 | pub use core::McpServer; 20 | pub use shutdown::ShutdownHandle; 21 | -------------------------------------------------------------------------------- /crates/turbomcp-server/src/server/shutdown.rs: -------------------------------------------------------------------------------- 1 | //! Graceful server shutdown coordination 2 | //! 3 | //! Provides external control over server shutdown with support for signal handling, 4 | //! container orchestration, health checks, and multi-service coordination. 5 | 6 | use std::sync::Arc; 7 | 8 | use crate::lifecycle::ServerLifecycle; 9 | 10 | /// Handle for triggering graceful server shutdown 11 | /// 12 | /// Provides external control over server shutdown with support for: 13 | /// - **Signal handling**: SIGTERM, SIGINT, custom signals 14 | /// - **Container orchestration**: Kubernetes graceful termination 15 | /// - **Health checks**: Coordinated shutdown with load balancers 16 | /// - **Multi-service coordination**: Synchronized shutdown sequences 17 | /// - **Testing**: Controlled server lifecycle in tests 18 | /// 19 | /// The handle is cloneable and thread-safe, allowing multiple components 20 | /// to coordinate shutdown or check shutdown status. 21 | #[derive(Debug, Clone)] 22 | pub struct ShutdownHandle { 23 | lifecycle: Arc, 24 | } 25 | 26 | impl ShutdownHandle { 27 | /// Create a new shutdown handle 28 | pub(crate) fn new(lifecycle: Arc) -> Self { 29 | Self { lifecycle } 30 | } 31 | 32 | /// Trigger graceful server shutdown 33 | pub async fn shutdown(&self) { 34 | self.lifecycle.shutdown().await; 35 | } 36 | 37 | /// Check if shutdown has been initiated 38 | pub async fn is_shutting_down(&self) -> bool { 39 | use crate::lifecycle::ServerState; 40 | matches!( 41 | self.lifecycle.state().await, 42 | ServerState::ShuttingDown | ServerState::Stopped 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/turbomcp-server/tests/test_helpers.rs: -------------------------------------------------------------------------------- 1 | //! Test helper functions to eliminate DRY violations in server tests 2 | //! 3 | //! This module provides common test utilities and patterns to reduce 4 | //! boilerplate code across the test suite. 5 | 6 | use turbomcp_server::{McpServer, ServerBuilder}; 7 | 8 | /// Create a default ServerBuilder for tests with optional name override 9 | pub fn test_server_builder() -> ServerBuilder { 10 | ServerBuilder::new() 11 | } 12 | 13 | /// Create a ServerBuilder with a specific test name 14 | pub fn test_server_builder_named(name: &str) -> ServerBuilder { 15 | ServerBuilder::new().name(name) 16 | } 17 | 18 | /// Create a ServerBuilder with test name and version 19 | pub fn test_server_builder_versioned(name: &str, version: &str) -> ServerBuilder { 20 | ServerBuilder::new().name(name).version(version) 21 | } 22 | 23 | /// Create a minimal test server with default configuration 24 | pub fn test_server() -> McpServer { 25 | test_server_builder().build() 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_helper_creates_builder() { 34 | let builder = test_server_builder(); 35 | let debug_str = format!("{builder:?}"); 36 | assert!(debug_str.contains("ServerBuilder")); 37 | } 38 | 39 | #[tokio::test] 40 | async fn test_helper_creates_named_builder() { 41 | let builder = test_server_builder_named("test-server"); 42 | let server = builder.build(); 43 | assert_eq!(server.config().name, "test-server"); 44 | } 45 | 46 | #[tokio::test] 47 | async fn test_helper_creates_versioned_builder() { 48 | let builder = test_server_builder_versioned("test-server", "2.0.0"); 49 | let server = builder.build(); 50 | assert_eq!(server.config().name, "test-server"); 51 | assert_eq!(server.config().version, "2.0.0"); 52 | } 53 | 54 | #[tokio::test] 55 | async fn test_helper_creates_server() { 56 | let server = test_server(); 57 | assert_eq!(server.config().name, "turbomcp-server"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/config/auth.rs: -------------------------------------------------------------------------------- 1 | //! Authentication configuration management 2 | //! 3 | //! This module provides authentication configuration for various 4 | //! authentication methods including JWT and API keys. 5 | 6 | /// Authentication configuration for middleware 7 | #[derive(Debug, Clone)] 8 | pub struct AuthConfig { 9 | /// Enable authentication 10 | pub enabled: bool, 11 | /// JWT secret for token validation 12 | pub jwt_secret: Option, 13 | /// API key header name 14 | pub api_key_header: Option, 15 | /// Custom authentication provider 16 | pub custom_validator: Option, 17 | } 18 | 19 | impl Default for AuthConfig { 20 | fn default() -> Self { 21 | Self { 22 | enabled: false, 23 | jwt_secret: None, 24 | api_key_header: Some("x-api-key".to_string()), 25 | custom_validator: None, 26 | } 27 | } 28 | } 29 | 30 | impl AuthConfig { 31 | /// Create new authentication config with JWT 32 | pub fn jwt(secret: String) -> Self { 33 | Self { 34 | enabled: true, 35 | jwt_secret: Some(secret), 36 | api_key_header: None, 37 | custom_validator: None, 38 | } 39 | } 40 | 41 | /// Create new authentication config with API key 42 | pub fn api_key(header: String) -> Self { 43 | Self { 44 | enabled: true, 45 | jwt_secret: None, 46 | api_key_header: Some(header), 47 | custom_validator: None, 48 | } 49 | } 50 | 51 | /// Create new authentication config with custom validator 52 | pub fn custom(validator: String) -> Self { 53 | Self { 54 | enabled: true, 55 | jwt_secret: None, 56 | api_key_header: None, 57 | custom_validator: Some(validator), 58 | } 59 | } 60 | 61 | /// Disable authentication 62 | pub fn disabled() -> Self { 63 | Self { 64 | enabled: false, 65 | jwt_secret: None, 66 | api_key_header: None, 67 | custom_validator: None, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/config/environment.rs: -------------------------------------------------------------------------------- 1 | //! Environment configuration for different deployment contexts 2 | 3 | /// Environment configuration 4 | #[derive(Debug, Clone, PartialEq, Default)] 5 | pub enum Environment { 6 | /// Development environment with permissive settings 7 | #[default] 8 | Development, 9 | /// Staging environment with moderate security 10 | Staging, 11 | /// Production environment with maximum security 12 | Production, 13 | } 14 | 15 | impl Environment { 16 | /// Check if this is a development environment 17 | pub fn is_development(&self) -> bool { 18 | matches!(self, Environment::Development) 19 | } 20 | 21 | /// Check if this is a staging environment 22 | pub fn is_staging(&self) -> bool { 23 | matches!(self, Environment::Staging) 24 | } 25 | 26 | /// Check if this is a production environment 27 | pub fn is_production(&self) -> bool { 28 | matches!(self, Environment::Production) 29 | } 30 | 31 | /// Get environment as string 32 | pub fn as_str(&self) -> &'static str { 33 | match self { 34 | Environment::Development => "development", 35 | Environment::Staging => "staging", 36 | Environment::Production => "production", 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/config/mod.rs: -------------------------------------------------------------------------------- 1 | //! Configuration management for Axum MCP integration 2 | //! 3 | //! This module provides comprehensive configuration types for all aspects 4 | //! of the MCP server including CORS, security, rate limiting, TLS, and authentication. 5 | 6 | pub mod auth; 7 | pub mod cors; 8 | pub mod environment; 9 | pub mod rate_limit; 10 | pub mod security; 11 | pub mod server; 12 | pub mod tls; 13 | 14 | // Re-export all configuration types 15 | pub use auth::*; 16 | pub use cors::*; 17 | pub use environment::*; 18 | pub use rate_limit::*; 19 | pub use security::*; 20 | pub use server::*; 21 | pub use tls::*; 22 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/config/rate_limit.rs: -------------------------------------------------------------------------------- 1 | //! Rate limiting configuration management 2 | //! 3 | //! This module provides rate limiting configuration with different strategies 4 | //! and environment-specific presets. 5 | 6 | /// Rate limiting key strategies 7 | #[derive(Debug, Clone)] 8 | pub enum RateLimitKey { 9 | /// Rate limit by IP address 10 | IpAddress, 11 | /// Rate limit by authenticated user ID 12 | UserId, 13 | /// Custom key extraction 14 | Custom, 15 | } 16 | 17 | /// Rate limiting configuration 18 | #[derive(Debug, Clone)] 19 | pub struct RateLimitConfig { 20 | /// Enable rate limiting 21 | pub enabled: bool, 22 | /// Requests per minute per IP 23 | pub requests_per_minute: u32, 24 | /// Burst capacity 25 | pub burst_capacity: u32, 26 | /// Key function (IP, User, Custom) 27 | pub key_function: RateLimitKey, 28 | } 29 | 30 | impl Default for RateLimitConfig { 31 | fn default() -> Self { 32 | Self::moderate() 33 | } 34 | } 35 | 36 | impl RateLimitConfig { 37 | /// Disabled rate limiting 38 | pub fn disabled() -> Self { 39 | Self { 40 | enabled: false, 41 | requests_per_minute: 0, 42 | burst_capacity: 0, 43 | key_function: RateLimitKey::IpAddress, 44 | } 45 | } 46 | 47 | /// Moderate rate limiting for staging 48 | pub fn moderate() -> Self { 49 | Self { 50 | enabled: true, 51 | requests_per_minute: 300, // 5 requests per second 52 | burst_capacity: 50, 53 | key_function: RateLimitKey::IpAddress, 54 | } 55 | } 56 | 57 | /// Strict rate limiting for production 58 | pub fn strict() -> Self { 59 | Self { 60 | enabled: true, 61 | requests_per_minute: 120, // 2 requests per second 62 | burst_capacity: 20, 63 | key_function: RateLimitKey::IpAddress, 64 | } 65 | } 66 | 67 | /// Create custom rate limiting configuration 68 | pub fn custom( 69 | requests_per_minute: u32, 70 | burst_capacity: u32, 71 | key_function: RateLimitKey, 72 | ) -> Self { 73 | Self { 74 | enabled: true, 75 | requests_per_minute, 76 | burst_capacity, 77 | key_function, 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/config/tls.rs: -------------------------------------------------------------------------------- 1 | //! TLS configuration management 2 | //! 3 | //! This module provides TLS (Transport Layer Security) configuration 4 | //! for secure HTTP connections. 5 | 6 | /// TLS version specification 7 | #[derive(Debug, Clone)] 8 | pub enum TlsVersion { 9 | /// TLS version 1.2 10 | TlsV1_2, 11 | /// TLS version 1.3 12 | TlsV1_3, 13 | } 14 | 15 | /// TLS configuration 16 | #[derive(Debug, Clone)] 17 | pub struct TlsConfig { 18 | /// Certificate file path 19 | pub cert_file: String, 20 | /// Private key file path 21 | pub key_file: String, 22 | /// Minimum TLS version 23 | pub min_version: TlsVersion, 24 | /// Enable HTTP/2 25 | pub enable_http2: bool, 26 | } 27 | 28 | impl Default for TlsConfig { 29 | fn default() -> Self { 30 | Self { 31 | cert_file: "cert.pem".to_string(), 32 | key_file: "key.pem".to_string(), 33 | min_version: TlsVersion::TlsV1_2, 34 | enable_http2: true, 35 | } 36 | } 37 | } 38 | 39 | impl TlsConfig { 40 | /// Create new TLS config with specific certificate and key files 41 | pub fn new(cert_file: String, key_file: String) -> Self { 42 | Self { 43 | cert_file, 44 | key_file, 45 | min_version: TlsVersion::TlsV1_2, 46 | enable_http2: true, 47 | } 48 | } 49 | 50 | /// Set minimum TLS version 51 | pub fn with_min_version(mut self, version: TlsVersion) -> Self { 52 | self.min_version = version; 53 | self 54 | } 55 | 56 | /// Enable or disable HTTP/2 57 | pub fn with_http2(mut self, enable: bool) -> Self { 58 | self.enable_http2 = enable; 59 | self 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/capabilities.rs: -------------------------------------------------------------------------------- 1 | //! Capabilities endpoint handler for MCP server capabilities 2 | 3 | use axum::{Json, extract::State}; 4 | 5 | use crate::axum::service::McpAppState; 6 | 7 | /// Capabilities handler - returns MCP server capabilities 8 | pub async fn capabilities_handler(State(app_state): State) -> Json { 9 | Json(app_state.get_capabilities()) 10 | } 11 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/health.rs: -------------------------------------------------------------------------------- 1 | //! Health check handler for service monitoring 2 | 3 | use axum::{Json, extract::State}; 4 | 5 | use crate::axum::service::McpAppState; 6 | 7 | /// Health check handler - returns service health status 8 | pub async fn health_handler(State(app_state): State) -> Json { 9 | let session_count = app_state.session_manager.active_session_count().await; 10 | 11 | Json(serde_json::json!({ 12 | "status": "healthy", 13 | "timestamp": chrono::Utc::now().to_rfc3339(), 14 | "sessions": { 15 | "active": session_count, 16 | "max": app_state.config.max_connections 17 | }, 18 | "version": env!("CARGO_PKG_VERSION") 19 | })) 20 | } 21 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/json_rpc.rs: -------------------------------------------------------------------------------- 1 | //! JSON-RPC HTTP handler for MCP requests 2 | 3 | use axum::{ 4 | Json, 5 | extract::{Extension, State}, 6 | http::StatusCode, 7 | }; 8 | use tracing::{error, trace}; 9 | 10 | use crate::axum::service::McpAppState; 11 | use crate::axum::types::{JsonRpcError, JsonRpcRequest, JsonRpcResponse}; 12 | use crate::tower::SessionInfo; 13 | 14 | /// JSON-RPC HTTP handler 15 | pub async fn json_rpc_handler( 16 | State(app_state): State, 17 | Extension(session): Extension, 18 | Json(request): Json, 19 | ) -> Result, StatusCode> { 20 | trace!("Processing JSON-RPC request: {:?}", request); 21 | 22 | // Validate JSON-RPC format 23 | if request.jsonrpc != "2.0" { 24 | return Ok(Json(JsonRpcResponse { 25 | jsonrpc: "2.0".to_string(), 26 | id: request.id, 27 | result: None, 28 | error: Some(JsonRpcError { 29 | code: -32600, 30 | message: "Invalid Request".to_string(), 31 | data: Some(serde_json::json!({ 32 | "reason": "jsonrpc field must be '2.0'" 33 | })), 34 | }), 35 | })); 36 | } 37 | 38 | // Create request object for service 39 | let service_request = serde_json::json!({ 40 | "jsonrpc": request.jsonrpc, 41 | "id": request.id, 42 | "method": request.method, 43 | "params": request.params 44 | }); 45 | 46 | // Process request through MCP service using AppState helper 47 | match app_state.process_request(service_request, &session).await { 48 | Ok(result) => { 49 | // Broadcast result to SSE clients if it's a notification 50 | if request.id.is_none() { 51 | let _ = app_state 52 | .sse_sender 53 | .send(serde_json::to_string(&result).unwrap_or_default()); 54 | } 55 | 56 | Ok(Json(JsonRpcResponse { 57 | jsonrpc: "2.0".to_string(), 58 | id: request.id, 59 | result: Some(result), 60 | error: None, 61 | })) 62 | } 63 | Err(e) => { 64 | error!("MCP service error: {}", e); 65 | 66 | Ok(Json(JsonRpcResponse { 67 | jsonrpc: "2.0".to_string(), 68 | id: request.id, 69 | result: None, 70 | error: Some(JsonRpcError { 71 | code: -32603, 72 | message: "Internal error".to_string(), 73 | data: Some(serde_json::json!({ 74 | "reason": e.to_string() 75 | })), 76 | }), 77 | })) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/metrics.rs: -------------------------------------------------------------------------------- 1 | //! Metrics handler for monitoring and observability 2 | 3 | use axum::{Json, extract::State}; 4 | 5 | use crate::axum::service::McpAppState; 6 | 7 | /// Metrics handler - returns detailed service metrics 8 | pub async fn metrics_handler(State(app_state): State) -> Json { 9 | let sessions = app_state.session_manager.list_sessions().await; 10 | let total_sessions = sessions.len(); 11 | let avg_duration = if total_sessions > 0 { 12 | sessions.iter().map(|s| s.duration().as_secs()).sum::() / total_sessions as u64 13 | } else { 14 | 0 15 | }; 16 | 17 | Json(serde_json::json!({ 18 | "sessions": { 19 | "active": total_sessions, 20 | "max": app_state.config.max_connections, 21 | "average_duration_seconds": avg_duration 22 | }, 23 | "server": { 24 | "uptime_seconds": std::time::SystemTime::now() 25 | .duration_since(std::time::UNIX_EPOCH) 26 | .unwrap_or_default() 27 | .as_secs(), 28 | "version": env!("CARGO_PKG_VERSION") 29 | } 30 | })) 31 | } 32 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | //! HTTP handlers for MCP endpoints 2 | //! 3 | //! This module contains all the HTTP endpoint handlers for the TurboMCP Axum integration. 4 | //! Each handler is focused on a specific responsibility and follows Axum best practices. 5 | //! 6 | //! ## Handlers 7 | //! 8 | //! - [`root`] - Basic server information endpoint 9 | //! - [`json_rpc`] - Core JSON-RPC request processing 10 | //! - [`capabilities`] - MCP server capabilities endpoint 11 | //! - [`sse`] - Server-Sent Events for real-time communication 12 | //! - [`websocket`] - WebSocket upgrade and bidirectional communication 13 | //! - [`health`] - Health check endpoint for monitoring 14 | //! - [`metrics`] - Detailed metrics endpoint for observability 15 | 16 | pub mod capabilities; 17 | pub mod health; 18 | pub mod json_rpc; 19 | pub mod metrics; 20 | pub mod root; 21 | pub mod sse; 22 | pub mod websocket; 23 | 24 | // Re-export all handler functions for convenience 25 | pub use capabilities::capabilities_handler; 26 | pub use health::health_handler; 27 | pub use json_rpc::json_rpc_handler; 28 | pub use metrics::metrics_handler; 29 | pub use root::root_handler; 30 | pub use sse::sse_handler; 31 | pub use websocket::websocket_handler; 32 | 33 | // Re-export SessionInfo from tower (canonical implementation) 34 | pub use crate::tower::SessionInfo; 35 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/root.rs: -------------------------------------------------------------------------------- 1 | //! Root endpoint handler for TurboMCP server information 2 | 3 | use axum::{Json, response::IntoResponse}; 4 | 5 | /// Root handler - provides basic server information 6 | pub async fn root_handler() -> impl IntoResponse { 7 | Json(serde_json::json!({ 8 | "name": "TurboMCP Server", 9 | "version": env!("CARGO_PKG_VERSION"), 10 | "description": "High-performance Model Context Protocol server", 11 | "endpoints": { 12 | "mcp": "/mcp", 13 | "capabilities": "/mcp/capabilities", 14 | "sse": "/mcp/sse", 15 | "websocket": "/mcp/ws", 16 | "health": "/mcp/health", 17 | "metrics": "/mcp/metrics" 18 | } 19 | })) 20 | } 21 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/handlers/sse.rs: -------------------------------------------------------------------------------- 1 | //! Server-Sent Events handler for real-time MCP communication 2 | 3 | use std::convert::Infallible; 4 | 5 | use axum::{ 6 | extract::{Extension, Query, State}, 7 | response::sse::{Event, KeepAlive, Sse}, 8 | }; 9 | use futures::Stream; 10 | use tokio::sync::broadcast; 11 | use tracing::{debug, info, warn}; 12 | 13 | use crate::axum::service::McpAppState; 14 | use crate::axum::types::SseQuery; 15 | use crate::tower::SessionInfo; 16 | 17 | /// Server-Sent Events handler for real-time communication 18 | pub async fn sse_handler( 19 | State(app_state): State, 20 | Query(_query): Query, 21 | Extension(session): Extension, 22 | ) -> Sse>> { 23 | info!("SSE connection established for session: {}", session.id); 24 | 25 | let mut receiver = app_state.sse_sender.subscribe(); 26 | 27 | // Create event stream 28 | let stream = async_stream::stream! { 29 | // Send initial connection event 30 | yield Ok(Event::default() 31 | .event("connected") 32 | .data(serde_json::json!({ 33 | "session_id": session.id, 34 | "timestamp": chrono::Utc::now().to_rfc3339() 35 | }).to_string())); 36 | 37 | // Stream events from broadcast channel 38 | loop { 39 | match receiver.recv().await { 40 | Ok(message) => { 41 | yield Ok(Event::default() 42 | .event("message") 43 | .data(message)); 44 | } 45 | Err(broadcast::error::RecvError::Closed) => { 46 | debug!("SSE broadcast channel closed"); 47 | break; 48 | } 49 | Err(broadcast::error::RecvError::Lagged(skipped)) => { 50 | warn!("SSE client lagged, skipped {} messages", skipped); 51 | yield Ok(Event::default() 52 | .event("error") 53 | .data(serde_json::json!({ 54 | "code": "LAGGED", 55 | "message": format!("Skipped {} messages due to slow client", skipped) 56 | }).to_string())); 57 | } 58 | } 59 | } 60 | }; 61 | 62 | Sse::new(stream).keep_alive(KeepAlive::new().interval(app_state.config.sse_keep_alive)) 63 | } 64 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/middleware/auth.rs: -------------------------------------------------------------------------------- 1 | //! Authentication middleware for API keys and JWT validation 2 | 3 | use axum::{ 4 | extract::State, 5 | http::StatusCode, 6 | middleware::Next, 7 | response::Response, 8 | }; 9 | 10 | use crate::axum::config::AuthConfig; 11 | 12 | /// Authentication middleware - validates tokens and API keys 13 | /// 14 | /// This is a basic implementation that provides hooks for API key and JWT 15 | /// authentication. For production use, integrate with your authentication 16 | /// system (JWT validation, OAuth2, database lookups, etc.) 17 | pub async fn authentication_middleware( 18 | State(auth_config): State, 19 | mut request: axum::http::Request, 20 | next: Next, 21 | ) -> Result { 22 | // Check for API key authentication 23 | if let Some(api_key_header) = &auth_config.api_key_header { 24 | if let Some(provided_key) = request.headers().get(api_key_header) { 25 | // In production, validate against your API key store 26 | if provided_key 27 | .to_str() 28 | .map_err(|_| StatusCode::BAD_REQUEST)? 29 | .is_empty() 30 | { 31 | return Err(StatusCode::UNAUTHORIZED); 32 | } 33 | // Add authenticated context to request 34 | request.extensions_mut().insert("api_key_user".to_string()); 35 | } else if auth_config.enabled { 36 | return Err(StatusCode::UNAUTHORIZED); 37 | } 38 | } 39 | 40 | // Check for JWT authentication 41 | if let Some(_jwt_secret) = &auth_config.jwt_secret { 42 | if let Some(auth_header) = request.headers().get("Authorization") { 43 | let auth_str = auth_header.to_str().map_err(|_| StatusCode::BAD_REQUEST)?; 44 | if let Some(token) = auth_str.strip_prefix("Bearer ") { 45 | // In production, validate JWT token here using the secret 46 | // Example: decode and validate token with your JWT library 47 | if token.is_empty() { 48 | return Err(StatusCode::UNAUTHORIZED); 49 | } 50 | // Add authenticated user context to request 51 | request.extensions_mut().insert("jwt_user".to_string()); 52 | } else { 53 | return Err(StatusCode::UNAUTHORIZED); 54 | } 55 | } else if auth_config.enabled { 56 | return Err(StatusCode::UNAUTHORIZED); 57 | } 58 | } 59 | 60 | // Continue processing 61 | Ok(next.run(request).await) 62 | } -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/middleware/mcp.rs: -------------------------------------------------------------------------------- 1 | //! Basic MCP middleware for session management 2 | 3 | use axum::{ 4 | http::StatusCode, 5 | middleware::Next, 6 | response::Response, 7 | }; 8 | use tracing::trace; 9 | 10 | use crate::axum::handlers::SessionInfo; 11 | 12 | /// Basic MCP middleware for session management 13 | /// 14 | /// This middleware ensures every request has an associated session, creating 15 | /// one if it doesn't exist. Sessions are used for tracking client state 16 | /// and enabling bidirectional communication. 17 | pub async fn mcp_middleware( 18 | mut request: axum::http::Request, 19 | next: Next, 20 | ) -> Result { 21 | // Create or retrieve session 22 | let session = match request.extensions().get::() { 23 | Some(session) => session.clone(), 24 | None => { 25 | // Create new session - in production, you might want to extract this 26 | // from headers or query parameters 27 | let session = SessionInfo::new(); 28 | request.extensions_mut().insert(session.clone()); 29 | session 30 | } 31 | }; 32 | 33 | trace!("Processing request for session: {}", session.session_id); 34 | 35 | // Continue processing 36 | let response = next.run(request).await; 37 | 38 | trace!("Request completed for session: {}", session.session_id); 39 | Ok(response) 40 | } -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | //! Middleware components for MCP HTTP endpoints 2 | //! 3 | //! This module contains all the middleware components used in the TurboMCP Axum 4 | //! integration. Each middleware is focused on a specific cross-cutting concern 5 | //! and can be composed together to build a comprehensive middleware stack. 6 | //! 7 | //! ## Middleware Components 8 | //! 9 | //! - [`mcp`] - Basic MCP session management 10 | //! - [`security`] - Security headers application 11 | //! - [`rate_limit`] - Request rate limiting 12 | //! - [`auth`] - Authentication and authorization 13 | 14 | pub mod mcp; 15 | pub mod security; 16 | pub mod rate_limit; 17 | pub mod auth; 18 | 19 | // Re-export all middleware functions for convenience 20 | pub use mcp::mcp_middleware; 21 | pub use security::security_headers_middleware; 22 | pub use rate_limit::rate_limiting_middleware; 23 | pub use auth::authentication_middleware; -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/mod.rs: -------------------------------------------------------------------------------- 1 | //! Axum Integration Layer for TurboMCP 2 | //! 3 | //! This module provides seamless integration with Axum routers, enabling the 4 | //! "bring your own server" philosophy while providing opinionated defaults for 5 | //! rapid development. 6 | //! 7 | //! NOTE: This entire module is only compiled when feature="http" is enabled. 8 | //! See lib.rs for the module-level feature gate. 9 | 10 | pub mod config; 11 | pub mod handlers; 12 | pub mod query; 13 | pub mod router; 14 | pub mod service; 15 | pub mod types; 16 | 17 | #[cfg(test)] 18 | pub mod tests; 19 | 20 | // Re-export main public types (avoiding glob conflicts) 21 | pub use config::{ 22 | AuthConfig, CorsConfig, Environment, McpServerConfig, RateLimitConfig, SecurityConfig, 23 | TlsConfig, 24 | }; 25 | pub use handlers::{ 26 | SessionInfo, capabilities_handler, health_handler, json_rpc_handler, metrics_handler, 27 | sse_handler, websocket_handler, 28 | }; 29 | pub use router::AxumMcpExt; 30 | pub use service::{McpAppState, McpService}; 31 | pub use types::{JsonRpcError, JsonRpcRequest, JsonRpcResponse, SseQuery, WebSocketQuery}; 32 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/query/mod.rs: -------------------------------------------------------------------------------- 1 | //! Query parameter types for different transport endpoints 2 | //! 3 | //! This module contains query parameter structures for SSE, WebSocket, 4 | //! and JSON-RPC communication endpoints. 5 | 6 | pub mod json_rpc; 7 | pub mod sse; 8 | pub mod websocket; 9 | 10 | // Re-export all query types 11 | pub use json_rpc::*; 12 | pub use sse::*; 13 | pub use websocket::*; 14 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/query/sse.rs: -------------------------------------------------------------------------------- 1 | //! SSE (Server-Sent Events) query parameters 2 | //! 3 | //! This module defines query parameters for the SSE endpoint, 4 | //! supporting session management and event resumption. 5 | 6 | use serde::Deserialize; 7 | 8 | /// Query parameters for SSE endpoint 9 | #[derive(Debug, Deserialize, Default)] 10 | pub struct SseQuery { 11 | /// Optional session ID for reconnection 12 | pub session_id: Option, 13 | 14 | /// Last event ID for resumption 15 | pub last_event_id: Option, 16 | } 17 | 18 | impl SseQuery { 19 | /// Create new SSE query with session ID 20 | pub fn with_session(session_id: String) -> Self { 21 | Self { 22 | session_id: Some(session_id), 23 | last_event_id: None, 24 | } 25 | } 26 | 27 | /// Create new SSE query with last event ID for resumption 28 | pub fn with_last_event(last_event_id: String) -> Self { 29 | Self { 30 | session_id: None, 31 | last_event_id: Some(last_event_id), 32 | } 33 | } 34 | 35 | /// Check if this is a reconnection request 36 | pub fn is_reconnection(&self) -> bool { 37 | self.session_id.is_some() 38 | } 39 | 40 | /// Check if this is a resumption request 41 | pub fn is_resumption(&self) -> bool { 42 | self.last_event_id.is_some() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/query/websocket.rs: -------------------------------------------------------------------------------- 1 | //! WebSocket query parameters 2 | //! 3 | //! This module defines query parameters for WebSocket connections, 4 | //! supporting protocol negotiation and session management. 5 | 6 | use serde::Deserialize; 7 | 8 | /// Query parameters for WebSocket endpoint 9 | #[derive(Debug, Deserialize)] 10 | pub struct WebSocketQuery { 11 | /// Optional session ID 12 | pub session_id: Option, 13 | 14 | /// Optional protocol version 15 | pub protocol: Option, 16 | } 17 | 18 | impl Default for WebSocketQuery { 19 | fn default() -> Self { 20 | Self { 21 | session_id: None, 22 | protocol: Some("2025-06-18".to_string()), 23 | } 24 | } 25 | } 26 | 27 | impl WebSocketQuery { 28 | /// Create new WebSocket query with session ID 29 | pub fn with_session(session_id: String) -> Self { 30 | Self { 31 | session_id: Some(session_id), 32 | protocol: Some("2025-06-18".to_string()), 33 | } 34 | } 35 | 36 | /// Create new WebSocket query with specific protocol version 37 | pub fn with_protocol(protocol: String) -> Self { 38 | Self { 39 | session_id: None, 40 | protocol: Some(protocol), 41 | } 42 | } 43 | 44 | /// Get the protocol version, defaulting to MCP protocol version 45 | pub fn get_protocol(&self) -> &str { 46 | self.protocol.as_deref().unwrap_or("2025-06-18") 47 | } 48 | 49 | /// Check if this includes a session ID 50 | pub fn has_session(&self) -> bool { 51 | self.session_id.is_some() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/router/mod.rs: -------------------------------------------------------------------------------- 1 | //! Router extension functionality for MCP integration 2 | //! 3 | //! This module provides the AxumMcpExt trait and related functionality 4 | //! for seamlessly integrating MCP capabilities with Axum routers. 5 | 6 | pub mod builder; 7 | pub mod extension; 8 | 9 | // Re-export main router types and functions (pub(crate) since not used externally) 10 | pub use extension::*; 11 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/service/interface.rs: -------------------------------------------------------------------------------- 1 | //! MCP service trait definition 2 | //! 3 | //! This module defines the core MCP service trait that implementations 4 | //! must provide to handle MCP protocol requests. 5 | 6 | use crate::tower::SessionInfo; 7 | use turbomcp_protocol::Result as McpResult; 8 | 9 | /// Core MCP service trait 10 | /// 11 | /// Implementations of this trait provide the business logic for handling 12 | /// MCP protocol requests. The trait is designed to be object-safe to 13 | /// allow for dynamic dispatch. 14 | #[async_trait::async_trait] 15 | pub trait McpService: Send + Sync + 'static { 16 | /// Process an MCP request and return a response 17 | /// 18 | /// # Arguments 19 | /// 20 | /// * `request` - The JSON-RPC request payload 21 | /// * `session` - Session information for the current request 22 | /// 23 | /// # Returns 24 | /// 25 | /// Returns the JSON response or an error if processing fails. 26 | async fn process_request( 27 | &self, 28 | request: serde_json::Value, 29 | session: &SessionInfo, 30 | ) -> McpResult; 31 | 32 | /// Get service capabilities 33 | /// 34 | /// Returns the capabilities that this service supports, 35 | /// following the MCP protocol specification. 36 | fn get_capabilities(&self) -> serde_json::Value { 37 | serde_json::json!({ 38 | "protocol_version": "2025-06-18", 39 | "capabilities": { 40 | "tools": true, 41 | "resources": true, 42 | "prompts": true, 43 | "logging": true 44 | } 45 | }) 46 | } 47 | } 48 | 49 | impl std::fmt::Debug for dyn McpService { 50 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 51 | f.debug_struct("McpService") 52 | .field("capabilities", &self.get_capabilities()) 53 | .finish() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/service/mod.rs: -------------------------------------------------------------------------------- 1 | //! Service layer for MCP implementation 2 | //! 3 | //! This module provides the service trait definition and application state 4 | //! management for MCP services in Axum applications. 5 | 6 | pub mod interface; 7 | pub mod state; 8 | 9 | // Re-export main service types 10 | pub use interface::*; 11 | pub use state::*; 12 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/tests/mod.rs: -------------------------------------------------------------------------------- 1 | //! Comprehensive test suite for the Axum MCP integration 2 | //! 3 | //! This test suite is organized into focused modules to test different 4 | //! aspects of the MCP integration: 5 | //! 6 | //! - `config` - Configuration system tests (server, CORS, security, etc.) 7 | //! - `router` - Router extension trait tests (AxumMcpExt functionality) 8 | //! - `integration` - End-to-end integration tests 9 | //! 10 | //! All tests maintain the original test coverage while being organized 11 | //! by functionality for better maintainability. 12 | 13 | #[cfg(test)] 14 | pub mod config; 15 | #[cfg(test)] 16 | pub mod integration; 17 | #[cfg(test)] 18 | pub mod router; 19 | 20 | #[cfg(test)] 21 | pub mod common { 22 | //! Common test utilities and mock services 23 | 24 | use super::super::*; 25 | use crate::tower::SessionInfo; 26 | use turbomcp_protocol::Result as McpResult; 27 | 28 | /// Test MCP service implementation for use in tests 29 | #[derive(Clone, Debug)] 30 | pub struct TestMcpService; 31 | 32 | #[async_trait::async_trait] 33 | impl McpService for TestMcpService { 34 | async fn process_request( 35 | &self, 36 | request: serde_json::Value, 37 | _session: &SessionInfo, 38 | ) -> McpResult { 39 | // Echo the request back as result 40 | Ok(serde_json::json!({ 41 | "echo": request, 42 | "timestamp": chrono::Utc::now().to_rfc3339() 43 | })) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/types/json_rpc.rs: -------------------------------------------------------------------------------- 1 | //! JSON-RPC protocol types for HTTP transport 2 | //! 3 | //! This module provides the JSON-RPC 2.0 protocol types used for 4 | //! MCP communication over HTTP. 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /// JSON-RPC request payload 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct JsonRpcRequest { 11 | /// JSON-RPC version (should be "2.0") 12 | pub jsonrpc: String, 13 | /// Request ID for correlation 14 | pub id: Option, 15 | /// Method name to call 16 | pub method: String, 17 | /// Method parameters 18 | pub params: Option, 19 | } 20 | 21 | /// JSON-RPC response payload 22 | #[derive(Debug, Serialize, Deserialize)] 23 | pub struct JsonRpcResponse { 24 | /// JSON-RPC version 25 | pub jsonrpc: String, 26 | /// Request ID for correlation 27 | pub id: Option, 28 | /// Success result 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | pub result: Option, 31 | /// Error information 32 | #[serde(skip_serializing_if = "Option::is_none")] 33 | pub error: Option, 34 | } 35 | 36 | /// JSON-RPC error object 37 | #[derive(Debug, Serialize, Deserialize)] 38 | pub struct JsonRpcError { 39 | /// Error code 40 | pub code: i32, 41 | /// Error message 42 | pub message: String, 43 | /// Additional error data 44 | #[serde(skip_serializing_if = "Option::is_none")] 45 | pub data: Option, 46 | } 47 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! Protocol types for Axum integration 2 | //! 3 | //! This module provides all the protocol types needed for MCP over HTTP 4 | //! including JSON-RPC structures and query parameters. 5 | 6 | pub mod json_rpc; 7 | pub mod query; 8 | 9 | // Re-export all types for backward compatibility 10 | pub use json_rpc::{JsonRpcError, JsonRpcRequest, JsonRpcResponse}; 11 | pub use query::{SseQuery, WebSocketQuery}; 12 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/axum/types/query.rs: -------------------------------------------------------------------------------- 1 | //! Query parameter types for HTTP endpoints 2 | //! 3 | //! This module provides query parameter structs for various HTTP endpoints 4 | //! including Server-Sent Events and WebSocket connections. 5 | 6 | use serde::Deserialize; 7 | 8 | /// Query parameters for SSE endpoint 9 | #[derive(Debug, Deserialize)] 10 | pub struct SseQuery { 11 | /// Optional session ID for reconnection 12 | pub session_id: Option, 13 | 14 | /// Last event ID for resumption 15 | pub last_event_id: Option, 16 | } 17 | 18 | /// Query parameters for WebSocket endpoint 19 | #[derive(Debug, Deserialize)] 20 | pub struct WebSocketQuery { 21 | /// Optional session ID 22 | pub session_id: Option, 23 | 24 | /// Optional protocol version 25 | pub protocol: Option, 26 | } 27 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/security/errors.rs: -------------------------------------------------------------------------------- 1 | //! Security-related error types for transport layer 2 | //! 3 | //! This module defines all security-related errors that can occur during 4 | //! transport operations, including origin validation, authentication, 5 | //! rate limiting, session security, and message size validation. 6 | 7 | use thiserror::Error; 8 | 9 | /// Security-related errors 10 | #[derive(Error, Debug)] 11 | pub enum SecurityError { 12 | /// Origin header validation failed 13 | #[error("Origin header validation failed: {0}")] 14 | InvalidOrigin(String), 15 | 16 | /// Authentication failed 17 | #[error("Authentication failed: {0}")] 18 | AuthenticationFailed(String), 19 | 20 | /// Rate limit exceeded for client 21 | #[error("Rate limit exceeded for {client}: {current}/{limit} requests")] 22 | RateLimitExceeded { 23 | /// Client identifier 24 | client: String, 25 | /// Current request count 26 | current: usize, 27 | /// Rate limit threshold 28 | limit: usize, 29 | }, 30 | 31 | /// Session security violation 32 | #[error("Session security violation: {0}")] 33 | SessionViolation(String), 34 | 35 | /// Message too large 36 | #[error("Message too large: {size} bytes exceeds limit of {limit} bytes")] 37 | MessageTooLarge { 38 | /// Message size in bytes 39 | size: usize, 40 | /// Size limit in bytes 41 | limit: usize, 42 | }, 43 | } 44 | 45 | impl SecurityError { 46 | /// Convert security error to HTTP status code 47 | pub fn to_http_status(&self) -> u16 { 48 | match self { 49 | SecurityError::InvalidOrigin(_) => 403, // Forbidden 50 | SecurityError::AuthenticationFailed(_) => 401, // Unauthorized 51 | SecurityError::RateLimitExceeded { .. } => 429, // Too Many Requests 52 | SecurityError::SessionViolation(_) => 403, // Forbidden 53 | SecurityError::MessageTooLarge { .. } => 413, // Payload Too Large 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /crates/turbomcp-transport/src/test_env.rs: -------------------------------------------------------------------------------- 1 | //! Simple test environment documentation 2 | //! 3 | //! For environment variable testing, we use #[serial_test::serial] to ensure tests 4 | //! that manipulate environment variables run sequentially to avoid conflicts. 5 | //! 6 | //! This is the cleanest approach without complex abstractions or unsafe code. 7 | -------------------------------------------------------------------------------- /crates/turbomcp/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Epistates 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crates/turbomcp/examples/basic_client.rs: -------------------------------------------------------------------------------- 1 | //! # Basic Client - Connecting and Calling Tools 2 | //! 3 | //! Demonstrates how to create a client that connects to an MCP server. 4 | //! 5 | //! Run server first: `cargo run --example hello_world` 6 | //! Then run this: `cargo run --example basic_client` 7 | 8 | use std::collections::HashMap; 9 | use turbomcp_client::prelude::*; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Logs must go to stderr for STDIO transport 14 | tracing_subscriber::fmt() 15 | .with_env_filter("info") 16 | .with_writer(std::io::stderr) 17 | .init(); 18 | 19 | tracing::info!("🔌 Starting MCP client"); 20 | 21 | // Create transport and client 22 | let transport = StdioTransport::new(); 23 | let client = Client::new(transport); 24 | 25 | // Initialize connection 26 | let init_result = client.initialize().await?; 27 | tracing::info!("📋 Connected to: {}", init_result.server_info.name); 28 | 29 | // List available tools 30 | let tools = client.list_tools().await?; 31 | tracing::info!("🛠️ Found {} tools", tools.len()); 32 | for tool in &tools { 33 | tracing::info!(" - {}", tool.name); 34 | } 35 | 36 | // Call a tool 37 | let mut args = HashMap::new(); 38 | args.insert("name".to_string(), serde_json::json!("World")); 39 | let result = client.call_tool("hello", Some(args)).await?; 40 | tracing::info!("✅ Result: {:?}", result); 41 | 42 | tracing::info!("🔚 Client demo completed"); 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/comprehensive.rs: -------------------------------------------------------------------------------- 1 | //! # Comprehensive Client - All MCP Features 2 | //! 3 | //! Demonstrates using all MCP client capabilities. 4 | //! 5 | //! Run with: `cargo run --example comprehensive` 6 | 7 | use std::collections::HashMap; 8 | use turbomcp_client::prelude::*; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | tracing_subscriber::fmt() 13 | .with_env_filter("info") 14 | .with_writer(std::io::stderr) 15 | .init(); 16 | 17 | tracing::info!("🚀 Comprehensive MCP Client Demo"); 18 | 19 | let transport = StdioTransport::new(); 20 | let client = Client::new(transport); 21 | 22 | // 1. Initialize 23 | tracing::info!("📡 Initializing..."); 24 | let init_result = client.initialize().await?; 25 | tracing::info!( 26 | "✅ Server: {} v{}", 27 | init_result.server_info.name, 28 | init_result.server_info.version 29 | ); 30 | 31 | // 2. List Tools 32 | tracing::info!("🔍 Listing tools..."); 33 | let tools = client.list_tools().await?; 34 | for tool in &tools { 35 | tracing::info!( 36 | " 🛠️ {}: {}", 37 | tool.name, 38 | tool.description.as_deref().unwrap_or("No description") 39 | ); 40 | } 41 | 42 | // 3. Call Tools 43 | tracing::info!("📞 Calling tools..."); 44 | let mut args = HashMap::new(); 45 | args.insert("a".to_string(), serde_json::json!(10.0)); 46 | args.insert("b".to_string(), serde_json::json!(5.0)); 47 | let result = client.call_tool("add", Some(args)).await?; 48 | tracing::info!(" ➕ add(10, 5) = {:?}", result); 49 | 50 | // 4. List Resources 51 | tracing::info!("📁 Listing resources..."); 52 | let resources = client.list_resources().await?; 53 | for resource in &resources { 54 | tracing::info!(" 📄 {} ({})", resource.name, resource.uri); 55 | if let Some(desc) = &resource.description { 56 | tracing::info!(" Description: {}", desc); 57 | } 58 | } 59 | 60 | // 5. Read Resources 61 | if !resources.is_empty() { 62 | tracing::info!("📖 Reading first resource..."); 63 | let content = client.read_resource(&resources[0].uri).await?; 64 | tracing::info!(" Content: {:?}", content.contents.first()); 65 | } 66 | 67 | // 6. List Prompts 68 | tracing::info!("💬 Listing prompts..."); 69 | let prompts = client.list_prompts().await?; 70 | for prompt in &prompts { 71 | tracing::info!(" 💭 {}", prompt.name); 72 | } 73 | 74 | // 7. Get Prompt 75 | if !prompts.is_empty() { 76 | tracing::info!("🎯 Getting first prompt..."); 77 | let prompt_result = client.get_prompt(&prompts[0].name, None).await?; 78 | tracing::info!(" Result: {:?}", prompt_result); 79 | } 80 | 81 | tracing::info!("✨ Comprehensive demo completed!"); 82 | Ok(()) 83 | } 84 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | //! # Hello World Server 2 | //! 3 | //! The absolute simplest MCP server - one tool, minimal code. 4 | //! 5 | //! Run with: `cargo run --example hello_world` 6 | 7 | use turbomcp::prelude::*; 8 | 9 | #[derive(Clone)] 10 | struct HelloServer; 11 | 12 | #[turbomcp::server(name = "hello", version = "1.0.0", transports = ["stdio"])] 13 | impl HelloServer { 14 | /// Say hello to someone 15 | #[tool(description = "Say hello to someone")] 16 | async fn hello(&self, name: String) -> McpResult { 17 | Ok(format!("Hello, {}!", name)) 18 | } 19 | } 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<(), Box> { 23 | HelloServer.run_stdio().await?; 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/http_server.rs: -------------------------------------------------------------------------------- 1 | //! # HTTP/SSE Server - Minimal Example 2 | //! 3 | //! Demonstrates HTTP transport with Server-Sent Events (SSE) for web compatibility. 4 | //! This is the simplest way to expose an MCP server over HTTP for web clients. 5 | //! 6 | //! ## Quick Start 7 | //! 8 | //! ```bash 9 | //! cargo run --example http_server --features http 10 | //! ``` 11 | //! 12 | //! ## Testing 13 | //! 14 | //! In another terminal, test with curl: 15 | //! ```bash 16 | //! # List tools 17 | //! curl -X POST http://localhost:3000/mcp \ 18 | //! -H "Content-Type: application/json" \ 19 | //! -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' 20 | //! 21 | //! # Call the echo tool 22 | //! curl -X POST http://localhost:3000/mcp \ 23 | //! -H "Content-Type: application/json" \ 24 | //! -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello"}},"id":2}' 25 | //! ``` 26 | 27 | #[cfg(feature = "http")] 28 | use turbomcp::prelude::*; 29 | 30 | #[derive(Clone)] 31 | struct HttpServer; 32 | 33 | #[turbomcp::server(name = "http-demo", version = "1.0.0", transports = ["http"])] 34 | impl HttpServer { 35 | #[tool("Get server info")] 36 | async fn info(&self) -> McpResult { 37 | Ok("HTTP/SSE transport server running!".to_string()) 38 | } 39 | 40 | #[tool("Echo a message")] 41 | async fn echo(&self, message: String) -> McpResult { 42 | Ok(format!("Echo: {}", message)) 43 | } 44 | } 45 | 46 | #[tokio::main] 47 | #[cfg(feature = "http")] 48 | async fn main() -> Result<(), Box> { 49 | tracing_subscriber::fmt() 50 | .with_env_filter("info") 51 | .with_writer(std::io::stdout) 52 | .init(); 53 | 54 | println!("\n╔══════════════════════════════════════╗"); 55 | println!("║ HTTP/SSE Server Example ║"); 56 | println!("╚══════════════════════════════════════╝"); 57 | println!("\n🌐 Starting server..."); 58 | println!("📡 Listening on http://localhost:3000/mcp\n"); 59 | println!("Available tools:"); 60 | println!(" • info - Get server info"); 61 | println!(" • echo - Echo a message back\n"); 62 | println!("Test with curl (see docs in example source code)\n"); 63 | 64 | tracing::info!("Starting HTTP/SSE server on 127.0.0.1:3000"); 65 | 66 | HttpServer 67 | .run_http_with_path("127.0.0.1:3000", "/mcp") 68 | .await?; 69 | 70 | Ok(()) 71 | } 72 | 73 | #[cfg(not(feature = "http"))] 74 | fn main() { 75 | eprintln!( 76 | "This example requires the 'http' feature. Run with: cargo run --example http_server --features http" 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/macro_server.rs: -------------------------------------------------------------------------------- 1 | //! # Macro Server - Modern Minimal MCP 2 | //! 3 | //! **Learning Goals (5 minutes):** 4 | //! - Understand the macro-based API for clean server definition 5 | //! - See the minimal code needed for a functional server 6 | //! - Introduction to the #[server] and #[tool] attributes 7 | //! 8 | //! **What this example demonstrates:** 9 | //! - Cleanest possible server implementation 10 | //! - Automatic schema generation from function signatures 11 | //! - Zero boilerplate with maximum functionality 12 | //! 13 | //! **Run with:** `cargo run --example macro_server` 14 | 15 | use turbomcp::prelude::*; 16 | 17 | /// The simplest possible MCP server using macros 18 | #[derive(Clone)] 19 | struct CleanServer; 20 | 21 | #[turbomcp::server( 22 | name = "clean-server", 23 | version = "1.0.0", 24 | description = "Minimal MCP server demonstrating clean architecture", 25 | transports = ["stdio"] 26 | )] 27 | impl CleanServer { 28 | /// Get current server time 29 | #[tool] 30 | async fn current_time(&self) -> McpResult { 31 | Ok(chrono::Utc::now().to_rfc3339()) 32 | } 33 | 34 | /// Echo back a message 35 | #[tool] 36 | async fn echo(&self, message: String) -> McpResult { 37 | Ok(format!("Echo: {}", message)) 38 | } 39 | 40 | /// Perform a calculation 41 | #[tool] 42 | async fn calculate(&self, operation: String, a: f64, b: f64) -> McpResult { 43 | match operation.as_str() { 44 | "add" => Ok(a + b), 45 | "subtract" => Ok(a - b), 46 | "multiply" => Ok(a * b), 47 | "divide" if b != 0.0 => Ok(a / b), 48 | "divide" => Err(McpError::Tool("Division by zero".to_string())), 49 | _ => Err(McpError::Tool(format!("Unknown operation: {}", operation))), 50 | } 51 | } 52 | } 53 | 54 | #[tokio::main] 55 | async fn main() -> Result<(), Box> { 56 | // That's it! The server is ready to run 57 | CleanServer.run_stdio().await?; 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/minimal_test.rs: -------------------------------------------------------------------------------- 1 | use turbomcp::prelude::*; 2 | 3 | #[derive(Clone)] 4 | struct MinimalServer; 5 | 6 | #[server(name = "My Test Server", version = "1.0.0")] 7 | impl MinimalServer { 8 | fn new() -> Self { 9 | Self 10 | } 11 | 12 | #[tool("test tool")] 13 | async fn test_tool(&self) -> McpResult { 14 | Ok("test".to_string()) 15 | } 16 | } 17 | 18 | fn main() { 19 | let server = MinimalServer::new(); 20 | // Check what create_server produces 21 | let built = server.create_server().unwrap(); 22 | println!("Server name: {}", built.config().name); 23 | } 24 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/resources.rs: -------------------------------------------------------------------------------- 1 | //! # Resources - Serving Data via URIs 2 | //! 3 | //! Demonstrates how to create resource handlers for serving data. 4 | //! 5 | //! Run with: `cargo run --example resources` 6 | 7 | use std::collections::HashMap; 8 | use std::sync::Arc; 9 | use tokio::sync::RwLock; 10 | use turbomcp::prelude::*; 11 | 12 | #[derive(Clone)] 13 | struct DocsServer { 14 | documents: Arc>>, 15 | } 16 | 17 | #[turbomcp::server(name = "docs", version = "1.0.0", root = "file:///docs:Documentation", transports = ["stdio"])] 18 | impl DocsServer { 19 | fn new() -> Self { 20 | let mut docs = HashMap::new(); 21 | docs.insert( 22 | "readme".to_string(), 23 | "# TurboMCP\nFast MCP framework".to_string(), 24 | ); 25 | docs.insert( 26 | "guide".to_string(), 27 | "## Getting Started\n1. Install\n2. Code\n3. Run!".to_string(), 28 | ); 29 | 30 | Self { 31 | documents: Arc::new(RwLock::new(docs)), 32 | } 33 | } 34 | 35 | /// List all available documents 36 | #[resource("docs://list")] 37 | async fn list_docs(&self) -> McpResult { 38 | let docs = self.documents.read().await; 39 | let names: Vec = docs.keys().cloned().collect(); 40 | Ok(format!("Available docs:\n{}", names.join("\n"))) 41 | } 42 | 43 | /// Get a specific document by name 44 | #[resource("docs://{name}")] 45 | async fn get_doc(&self, name: String) -> McpResult { 46 | let docs = self.documents.read().await; 47 | docs.get(&name) 48 | .cloned() 49 | .ok_or_else(|| McpError::protocol(format!("Document '{}' not found", name))) 50 | } 51 | 52 | /// Tool to add new documents 53 | #[tool("Add a new document")] 54 | async fn add_doc(&self, name: String, content: String) -> McpResult { 55 | let mut docs = self.documents.write().await; 56 | docs.insert(name.clone(), content); 57 | Ok(format!("Added document: {}", name)) 58 | } 59 | } 60 | 61 | #[tokio::main] 62 | async fn main() -> Result<(), Box> { 63 | DocsServer::new().run_stdio().await?; 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/stateful.rs: -------------------------------------------------------------------------------- 1 | //! # Stateful Server - Shared State Management 2 | //! 3 | //! Demonstrates managing shared state across requests with Arc>. 4 | //! 5 | //! Run with: `cargo run --example stateful` 6 | 7 | use std::collections::HashMap; 8 | use std::sync::Arc; 9 | use tokio::sync::RwLock; 10 | use turbomcp::prelude::*; 11 | 12 | #[derive(Clone)] 13 | struct CounterServer { 14 | /// Shared state: counters keyed by name 15 | counters: Arc>>, 16 | } 17 | 18 | #[turbomcp::server(name = "counter", version = "1.0.0", transports = ["stdio"])] 19 | impl CounterServer { 20 | fn new() -> Self { 21 | Self { 22 | counters: Arc::new(RwLock::new(HashMap::new())), 23 | } 24 | } 25 | 26 | #[tool("Increment a counter by name")] 27 | async fn increment(&self, name: String) -> McpResult { 28 | let mut counters = self.counters.write().await; 29 | let counter = counters.entry(name).or_insert(0); 30 | *counter += 1; 31 | Ok(*counter) 32 | } 33 | 34 | #[tool("Get current counter value")] 35 | async fn get(&self, name: String) -> McpResult { 36 | let counters = self.counters.read().await; 37 | Ok(*counters.get(&name).unwrap_or(&0)) 38 | } 39 | 40 | #[tool("Reset a counter")] 41 | async fn reset(&self, name: String) -> McpResult { 42 | let mut counters = self.counters.write().await; 43 | counters.remove(&name); 44 | Ok(format!("Counter '{}' reset", name)) 45 | } 46 | 47 | #[tool("List all counters")] 48 | async fn list(&self) -> McpResult { 49 | let counters = self.counters.read().await; 50 | let list: Vec = counters 51 | .iter() 52 | .map(|(k, v)| format!("{}: {}", k, v)) 53 | .collect(); 54 | Ok(list.join(", ")) 55 | } 56 | } 57 | 58 | #[tokio::main] 59 | async fn main() -> Result<(), Box> { 60 | CounterServer::new().run_stdio().await?; 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/stdio_app.rs: -------------------------------------------------------------------------------- 1 | //! # STDIO Complete Application 2 | //! 3 | //! A complete working example showing server and client communication via STDIO. 4 | //! 5 | //! Run with: `cargo run --example stdio_app` 6 | 7 | use turbomcp::prelude::*; 8 | 9 | /// Complete STDIO application demonstration 10 | #[derive(Clone)] 11 | struct CalculatorApp; 12 | 13 | #[turbomcp::server(name = "calculator", version = "1.0.0", transports = ["stdio"])] 14 | impl CalculatorApp { 15 | #[tool("Add two numbers")] 16 | async fn add(&self, a: f64, b: f64) -> McpResult { 17 | Ok(a + b) 18 | } 19 | 20 | #[tool("Multiply two numbers")] 21 | async fn multiply(&self, a: f64, b: f64) -> McpResult { 22 | Ok(a * b) 23 | } 24 | 25 | #[resource("calc://help")] 26 | async fn help(&self) -> McpResult { 27 | Ok("Calculator: add, multiply".to_string()) 28 | } 29 | 30 | #[prompt("Math problem")] 31 | async fn math_prompt(&self, problem: Option) -> McpResult { 32 | match problem { 33 | Some(p) => Ok(format!("Solve: {}", p)), 34 | None => Ok("Solve this math problem".to_string()), 35 | } 36 | } 37 | } 38 | 39 | #[tokio::main] 40 | async fn main() -> Result<(), Box> { 41 | CalculatorApp.run_stdio().await?; 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/stdio_client.rs: -------------------------------------------------------------------------------- 1 | //! Stdio Transport Client - Minimal Example 2 | //! 3 | //! Demonstrates how to create a client that launches and connects to an MCP server via stdio. 4 | //! 5 | //! **Run:** 6 | //! ```bash 7 | //! cargo run --example stdio_client --features stdio 8 | //! ``` 9 | 10 | use std::collections::HashMap; 11 | use turbomcp_client::prelude::*; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<()> { 15 | // Logs MUST go to stderr for stdio transport 16 | tracing_subscriber::fmt() 17 | .with_env_filter("info") 18 | .with_writer(std::io::stderr) 19 | .init(); 20 | 21 | tracing::info!("🔌 Starting Stdio client"); 22 | 23 | // Create stdio transport (connects to stdin/stdout of this process) 24 | // In production, you'd launch a child process and connect to its stdio 25 | let transport = StdioTransport::new(); 26 | let client = Client::new(transport); 27 | 28 | // Initialize connection 29 | let init = client.initialize().await?; 30 | tracing::info!("✅ Connected to: {}", init.server_info.name); 31 | 32 | // List tools 33 | let tools = client.list_tools().await?; 34 | tracing::info!("🛠️ Found {} tools:", tools.len()); 35 | for tool in &tools { 36 | tracing::info!( 37 | " - {}: {}", 38 | tool.name, 39 | tool.description.as_deref().unwrap_or("") 40 | ); 41 | } 42 | 43 | // Call echo tool 44 | let mut args = HashMap::new(); 45 | args.insert("message".to_string(), serde_json::json!("Hello Stdio!")); 46 | let result = client.call_tool("echo", Some(args)).await?; 47 | tracing::info!("📝 Echo result: {:?}", result); 48 | 49 | // Call reverse tool 50 | let mut args = HashMap::new(); 51 | args.insert("text".to_string(), serde_json::json!("TurboMCP")); 52 | let result = client.call_tool("reverse", Some(args)).await?; 53 | tracing::info!("🔄 Reverse result: {:?}", result); 54 | 55 | tracing::info!("✅ Demo complete"); 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/stdio_output_verification.rs: -------------------------------------------------------------------------------- 1 | //! # STDIO Output Verification Example 2 | //! 3 | //! This example demonstrates that: 4 | //! - Protocol messages go to stdout 5 | //! - Observability logs go to stderr 6 | //! 7 | //! Run with output separation: 8 | //! ```bash 9 | //! cargo run --example stdio_output_verification 1>stdout.log 2>stderr.log 10 | //! # Then verify with: 11 | //! cat stdout.log # Should contain JSON-RPC protocol messages 12 | //! cat stderr.log # Should contain observability logs 13 | //! ``` 14 | 15 | use turbomcp::prelude::*; 16 | 17 | /// Simple echo app for testing 18 | #[derive(Clone)] 19 | struct EchoApp; 20 | 21 | #[turbomcp::server(name = "echo", version = "1.0.0", transports = ["stdio"])] 22 | impl EchoApp { 23 | #[tool("Echo a message back")] 24 | async fn echo(&self, message: String) -> McpResult { 25 | Ok(format!("Echo: {}", message)) 26 | } 27 | 28 | #[tool("Get server status")] 29 | async fn status(&self) -> McpResult { 30 | Ok("Server is running".to_string()) 31 | } 32 | } 33 | 34 | #[tokio::main] 35 | async fn main() -> Result<(), Box> { 36 | // Initialize observability (this outputs to stderr by default) 37 | let _guard = turbomcp_server::observability::ObservabilityConfig::default() 38 | .with_service_name("stdio-output-verification") 39 | .enable_security_auditing() 40 | .enable_performance_monitoring() 41 | .init()?; 42 | 43 | eprintln!("=== SERVER STARTED: LOGS GO TO STDERR ==="); 44 | 45 | // Run the server (protocol messages go to stdout) 46 | EchoApp.run_stdio().await?; 47 | 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/stdio_server.rs: -------------------------------------------------------------------------------- 1 | //! Stdio Transport Server - Minimal Example 2 | //! 3 | //! Demonstrates stdio transport for CLI tools and Claude Desktop integration. 4 | //! 5 | //! # ⚠️ CRITICAL: Output Constraint 6 | //! 7 | //! When using stdio transport, **ALL application output must go to stderr**. 8 | //! **Any writes to stdout (including `println!()`) will corrupt the MCP protocol.** 9 | //! 10 | //! ## ✅ Correct Pattern 11 | //! 12 | //! ```ignore 13 | //! // Logs go to stderr (configured below) 14 | //! tracing::info!("message"); 15 | //! eprintln!("error message"); 16 | //! ``` 17 | //! 18 | //! ## ❌ Wrong Pattern 19 | //! 20 | //! ```ignore 21 | //! println!("debug"); // ❌ Corrupts protocol 22 | //! std::io::stdout().write_all(b"x"); // ❌ Corrupts protocol 23 | //! ``` 24 | //! 25 | //! The `#[server]` macro with `transports = ["stdio"]` will **reject** any use of 26 | //! `println!()` at compile time, preventing this mistake before deployment. 27 | //! 28 | //! **Run standalone:** 29 | //! ```bash 30 | //! cargo run --example stdio_server --features stdio 31 | //! ``` 32 | //! 33 | //! **Test with JSON-RPC:** 34 | //! ```bash 35 | //! echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | cargo run --example stdio_server 36 | //! ``` 37 | 38 | use turbomcp::prelude::*; 39 | 40 | #[derive(Clone)] 41 | struct StdioServer; 42 | 43 | #[turbomcp::server(name = "stdio-demo", version = "1.0.0", transports = ["stdio"])] 44 | impl StdioServer { 45 | #[tool("Echo a message")] 46 | async fn echo(&self, message: String) -> McpResult { 47 | Ok(format!("Stdio Echo: {}", message)) 48 | } 49 | 50 | #[tool("Reverse a string")] 51 | async fn reverse(&self, text: String) -> McpResult { 52 | Ok(text.chars().rev().collect()) 53 | } 54 | 55 | #[resource("stdio://status")] 56 | async fn status(&self) -> McpResult { 57 | Ok("Stdio server running".to_string()) 58 | } 59 | } 60 | 61 | #[tokio::main] 62 | async fn main() -> Result<(), Box> { 63 | // ⚠️ REQUIRED: Configure logging to stderr for stdio transport 64 | // Stdio transport uses stdout exclusively for MCP protocol messages. 65 | // If logging is configured for stdout, it will corrupt the protocol. 66 | // 67 | // The #[server(transports = ["stdio"])] macro prevents println!() at compile time, 68 | // so this logging configuration is the only way to handle application output. 69 | tracing_subscriber::fmt() 70 | .with_env_filter("error") 71 | .with_writer(std::io::stderr) // ← CRITICAL: stderr, not stdout 72 | .init(); 73 | 74 | StdioServer.run_stdio().await?; 75 | 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/tcp_client.rs: -------------------------------------------------------------------------------- 1 | //! TCP Transport Client - Minimal Example 2 | //! 3 | //! Connects to TCP server and calls tools. 4 | //! 5 | //! **Run server first:** 6 | //! ```bash 7 | //! cargo run --example tcp_server --features tcp 8 | //! ``` 9 | //! 10 | //! **Then run client:** 11 | //! ```bash 12 | //! cargo run --example tcp_client --features tcp 13 | //! ``` 14 | 15 | use std::collections::HashMap; 16 | use turbomcp_client::{Client, Result}; 17 | use turbomcp_transport::tcp::TcpTransport; 18 | 19 | #[tokio::main] 20 | async fn main() -> Result<()> { 21 | tracing_subscriber::fmt() 22 | .with_env_filter("info") 23 | .with_writer(std::io::stdout) 24 | .init(); 25 | 26 | tracing::info!("🔌 Connecting to TCP server..."); 27 | 28 | // Create TCP client transport 29 | let bind_addr = "127.0.0.1:0".parse().expect("Valid bind address"); 30 | let server_addr = "127.0.0.1:8765".parse().expect("Valid server address"); 31 | let transport = TcpTransport::new_client(bind_addr, server_addr); 32 | let client = Client::new(transport); 33 | 34 | // Initialize (auto-connects) 35 | let init = client.initialize().await?; 36 | tracing::info!("✅ Connected to: {}", init.server_info.name); 37 | 38 | // List tools 39 | let tools = client.list_tools().await?; 40 | tracing::info!("🛠️ Found {} tools:", tools.len()); 41 | for tool in &tools { 42 | tracing::info!( 43 | " - {}: {}", 44 | tool.name, 45 | tool.description.as_deref().unwrap_or("") 46 | ); 47 | } 48 | 49 | // Call echo tool 50 | let mut args = HashMap::new(); 51 | args.insert("message".to_string(), serde_json::json!("Hello TCP!")); 52 | let result = client.call_tool("echo", Some(args)).await?; 53 | tracing::info!("📝 Echo result: {:?}", result); 54 | 55 | // Call add tool 56 | let mut args = HashMap::new(); 57 | args.insert("a".to_string(), serde_json::json!(10.5)); 58 | args.insert("b".to_string(), serde_json::json!(20.3)); 59 | let result = client.call_tool("add", Some(args)).await?; 60 | tracing::info!("🔢 Add result: {:?}", result); 61 | 62 | tracing::info!("✅ Demo complete"); 63 | Ok(()) 64 | } 65 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/tcp_server.rs: -------------------------------------------------------------------------------- 1 | //! TCP Transport Server - Minimal Example 2 | //! 3 | //! Demonstrates TCP transport for network communication. 4 | //! 5 | //! **Run:** 6 | //! ```bash 7 | //! cargo run --example tcp_server --features tcp 8 | //! ``` 9 | //! 10 | //! **Connect:** 11 | //! ```bash 12 | //! cargo run --example tcp_client --features tcp 13 | //! ``` 14 | 15 | #[cfg(feature = "tcp")] 16 | use turbomcp::prelude::*; 17 | 18 | #[derive(Clone)] 19 | #[cfg(feature = "tcp")] 20 | struct TcpServer; 21 | 22 | #[cfg(feature = "tcp")] 23 | #[turbomcp::server(name = "tcp-demo", version = "1.0.0", transports = ["tcp"])] 24 | impl TcpServer { 25 | #[tool("Echo a message")] 26 | async fn echo(&self, message: String) -> McpResult { 27 | Ok(format!("TCP Echo: {}", message)) 28 | } 29 | 30 | #[tool("Add two numbers")] 31 | async fn add(&self, a: f64, b: f64) -> McpResult { 32 | Ok(a + b) 33 | } 34 | } 35 | 36 | #[tokio::main] 37 | #[cfg(feature = "tcp")] 38 | async fn main() -> Result<(), Box> { 39 | tracing_subscriber::fmt() 40 | .with_env_filter("info") 41 | .with_writer(std::io::stdout) 42 | .init(); 43 | 44 | tracing::info!("🚀 TCP Server listening on 127.0.0.1:8765"); 45 | 46 | TcpServer.run_tcp("127.0.0.1:8765").await?; 47 | 48 | Ok(()) 49 | } 50 | 51 | #[cfg(not(feature = "tcp"))] 52 | fn main() { 53 | eprintln!( 54 | "This example requires the 'tcp' feature. Run with: cargo run --example tcp_server --features tcp" 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/unix_client.rs: -------------------------------------------------------------------------------- 1 | //! Unix Socket Transport Client - Minimal Example 2 | //! 3 | //! Connects to Unix socket server and calls tools. 4 | //! 5 | //! **Run server first:** 6 | //! ```bash 7 | //! cargo run --example unix_server --features unix 8 | //! ``` 9 | //! 10 | //! **Then run client:** 11 | //! ```bash 12 | //! cargo run --example unix_client --features unix 13 | //! ``` 14 | 15 | use std::collections::HashMap; 16 | use std::path::PathBuf; 17 | use turbomcp_client::{Client, Result}; 18 | use turbomcp_transport::unix::UnixTransport; 19 | 20 | #[tokio::main] 21 | async fn main() -> Result<()> { 22 | tracing_subscriber::fmt() 23 | .with_env_filter("info") 24 | .with_writer(std::io::stdout) 25 | .init(); 26 | 27 | tracing::info!("🔌 Connecting to Unix socket server..."); 28 | 29 | let socket_path = PathBuf::from("/tmp/turbomcp-demo.sock"); 30 | 31 | // Create Unix socket client transport 32 | let transport = UnixTransport::new_client(socket_path); 33 | let client = Client::new(transport); 34 | 35 | // Initialize (auto-connects) 36 | let init = client.initialize().await?; 37 | tracing::info!("✅ Connected to: {}", init.server_info.name); 38 | 39 | // List tools 40 | let tools = client.list_tools().await?; 41 | tracing::info!("🛠️ Found {} tools:", tools.len()); 42 | for tool in &tools { 43 | tracing::info!( 44 | " - {}: {}", 45 | tool.name, 46 | tool.description.as_deref().unwrap_or("") 47 | ); 48 | } 49 | 50 | // Call echo tool 51 | let mut args = HashMap::new(); 52 | args.insert( 53 | "message".to_string(), 54 | serde_json::json!("Hello Unix Socket!"), 55 | ); 56 | let result = client.call_tool("echo", Some(args)).await?; 57 | tracing::info!("📝 Echo result: {:?}", result); 58 | 59 | // Call multiply tool 60 | let mut args = HashMap::new(); 61 | args.insert("a".to_string(), serde_json::json!(7.0)); 62 | args.insert("b".to_string(), serde_json::json!(6.0)); 63 | let result = client.call_tool("multiply", Some(args)).await?; 64 | tracing::info!("🔢 Multiply result: {:?}", result); 65 | 66 | // List and read resources 67 | let resources = client.list_resources().await?; 68 | tracing::info!("📦 Found {} resources:", resources.len()); 69 | for resource in &resources { 70 | tracing::info!(" - {} ({})", resource.name, resource.uri); 71 | let content = client.read_resource(&resource.uri).await?; 72 | tracing::info!(" Content: {:?}", content); 73 | } 74 | 75 | tracing::info!("✅ Demo complete"); 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/unix_server.rs: -------------------------------------------------------------------------------- 1 | //! Unix Socket Transport Server - Minimal Example 2 | //! 3 | //! Demonstrates Unix domain socket transport for high-performance local IPC. 4 | //! 5 | //! **Run:** 6 | //! ```bash 7 | //! cargo run --example unix_server --features unix 8 | //! ``` 9 | //! 10 | //! **Connect:** 11 | //! ```bash 12 | //! cargo run --example unix_client --features unix 13 | //! ``` 14 | 15 | #[cfg(feature = "unix")] 16 | use turbomcp::prelude::*; 17 | 18 | #[derive(Clone)] 19 | #[cfg(feature = "unix")] 20 | struct UnixServer; 21 | 22 | #[cfg(feature = "unix")] 23 | #[turbomcp::server(name = "unix-demo", version = "1.0.0", transports = ["unix"])] 24 | impl UnixServer { 25 | #[tool("Echo a message")] 26 | async fn echo(&self, message: String) -> McpResult { 27 | Ok(format!("Unix Echo: {}", message)) 28 | } 29 | 30 | #[tool("Multiply two numbers")] 31 | async fn multiply(&self, a: f64, b: f64) -> McpResult { 32 | Ok(a * b) 33 | } 34 | 35 | #[resource("unix://local/info")] 36 | async fn info(&self) -> McpResult { 37 | Ok("Unix socket server - high-performance local IPC".to_string()) 38 | } 39 | } 40 | 41 | #[tokio::main] 42 | #[cfg(feature = "unix")] 43 | async fn main() -> Result<(), Box> { 44 | tracing_subscriber::fmt() 45 | .with_env_filter("info") 46 | .with_writer(std::io::stdout) 47 | .init(); 48 | 49 | let socket_path = "/tmp/turbomcp-demo.sock"; 50 | 51 | // Clean up any existing socket 52 | let _ = std::fs::remove_file(socket_path); 53 | 54 | tracing::info!("🚀 Unix Socket Server listening on {}", socket_path); 55 | 56 | UnixServer.run_unix(socket_path).await?; 57 | 58 | Ok(()) 59 | } 60 | 61 | #[cfg(not(feature = "unix"))] 62 | fn main() { 63 | eprintln!( 64 | "This example requires the 'unix' feature. Run with: cargo run --example unix_server --features unix" 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /crates/turbomcp/examples/websocket_server.rs: -------------------------------------------------------------------------------- 1 | //! WebSocket Transport Server - Minimal Example 2 | //! 3 | //! Demonstrates WebSocket transport for real-time bidirectional communication. 4 | //! 5 | //! **Run:** 6 | //! ```bash 7 | //! cargo run --example websocket_server --features "http,websocket" 8 | //! ``` 9 | //! 10 | //! **Connect:** 11 | //! ```bash 12 | //! cargo run --example websocket_client --features "http,websocket" 13 | //! ``` 14 | 15 | #[cfg(feature = "websocket")] 16 | use turbomcp::prelude::*; 17 | 18 | #[derive(Clone)] 19 | #[cfg(feature = "websocket")] 20 | struct WebSocketServer; 21 | 22 | #[cfg(feature = "websocket")] 23 | #[turbomcp::server(name = "websocket-demo", version = "1.0.0", transports = ["websocket"])] 24 | impl WebSocketServer { 25 | #[tool("Echo a message")] 26 | async fn echo(&self, message: String) -> McpResult { 27 | Ok(format!("WebSocket Echo: {}", message)) 28 | } 29 | 30 | #[tool("Get current timestamp")] 31 | async fn timestamp(&self) -> McpResult { 32 | use std::time::{SystemTime, UNIX_EPOCH}; 33 | let now = SystemTime::now() 34 | .duration_since(UNIX_EPOCH) 35 | .unwrap() 36 | .as_secs(); 37 | Ok(format!("Current timestamp: {}", now)) 38 | } 39 | 40 | #[resource("ws://status")] 41 | async fn status(&self) -> McpResult { 42 | Ok("WebSocket server is running".to_string()) 43 | } 44 | } 45 | 46 | #[tokio::main] 47 | #[cfg(feature = "websocket")] 48 | async fn main() -> Result<(), Box> { 49 | tracing_subscriber::fmt() 50 | .with_env_filter("info") 51 | .with_writer(std::io::stdout) 52 | .init(); 53 | 54 | tracing::info!("🚀 WebSocket Server listening on ws://127.0.0.1:8080"); 55 | 56 | WebSocketServer.run_websocket("127.0.0.1:8080").await?; 57 | 58 | Ok(()) 59 | } 60 | 61 | #[cfg(not(feature = "websocket"))] 62 | fn main() { 63 | eprintln!( 64 | "This example requires 'http' and 'websocket' features. Run with: cargo run --example websocket_server --features \"http,websocket\"" 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /crates/turbomcp/src/helpers.rs: -------------------------------------------------------------------------------- 1 | //! Helper functions and utilities 2 | 3 | use crate::{CallToolResult, Content, GetPromptResult, TextContent}; 4 | 5 | /// Create text content helper 6 | pub fn text>(content: S) -> Content { 7 | Content::Text(TextContent { 8 | text: content.as_ref().to_string(), 9 | annotations: None, 10 | meta: None, 11 | }) 12 | } 13 | 14 | /// Create an error content helper 15 | pub fn error_text>(message: S) -> Content { 16 | Content::Text(TextContent { 17 | text: format!("Error: {}", message.as_ref()), 18 | annotations: None, 19 | meta: None, 20 | }) 21 | } 22 | 23 | /// Create a successful tool result 24 | #[must_use] 25 | pub const fn tool_success(content: Vec) -> CallToolResult { 26 | CallToolResult { 27 | content, 28 | is_error: Some(false), 29 | structured_content: None, 30 | _meta: None, 31 | } 32 | } 33 | 34 | /// Create an error tool result 35 | pub fn tool_error>(message: S) -> CallToolResult { 36 | CallToolResult { 37 | content: vec![error_text(message)], 38 | is_error: Some(true), 39 | structured_content: None, 40 | _meta: None, 41 | } 42 | } 43 | 44 | /// Create a prompt result with description 45 | pub fn prompt_result>( 46 | content: S, 47 | description: S, 48 | ) -> crate::McpResult { 49 | use turbomcp_protocol::types::{PromptMessage, Role}; 50 | 51 | Ok(GetPromptResult { 52 | messages: vec![PromptMessage { 53 | role: Role::User, 54 | content: Content::Text(TextContent { 55 | text: content.as_ref().to_string(), 56 | annotations: None, 57 | meta: None, 58 | }), 59 | }], 60 | description: Some(description.as_ref().to_string()), 61 | _meta: None, 62 | }) 63 | } 64 | 65 | /// Create a resource read result 66 | pub fn resource_result>( 67 | content: S, 68 | ) -> crate::McpResult { 69 | use turbomcp_protocol::types::{ReadResourceResult, ResourceContent, TextResourceContents}; 70 | 71 | Ok(ReadResourceResult { 72 | contents: vec![ResourceContent::Text(TextResourceContents { 73 | uri: "text://content".to_string(), 74 | mime_type: Some("text/plain".to_string()), 75 | text: content.as_ref().to_string(), 76 | meta: None, 77 | })], 78 | _meta: None, 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /crates/turbomcp/src/macros.rs: -------------------------------------------------------------------------------- 1 | //! Macro re-exports and utilities 2 | 3 | // Macro re-exports will be enabled when the macro crate is implemented 4 | // pub use turbomcp_macros::*; -------------------------------------------------------------------------------- /crates/turbomcp/src/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | //! Runtime support for full MCP 2025-06-18 protocol 2 | //! 3 | //! ## Architectural Decision (2024-10-14) 4 | //! 5 | //! All bidirectional transport runtime implementations now live in 6 | //! `turbomcp-server/src/runtime/` as the **single source of truth**. 7 | //! 8 | //! The `#[server]` macro generates code that uses `ServerBuilder`'s transport 9 | //! methods (`run_stdio()`, `run_http_with_config()`, `run_websocket_with_config()`), 10 | //! ensuring consistent MCP 2025-06-18 protocol compliance across all patterns. 11 | //! 12 | //! ## Why This Module Is Now Empty 13 | //! 14 | //! Previously, this module contained duplicate implementations of: 15 | //! - `stdio_bidirectional.rs` (484 lines) - DELETED 16 | //! - `http_bidirectional.rs` (19KB) - DELETED 17 | //! - `websocket_server.rs` (726 lines) - DELETED 18 | //! - `websocket_bidirectional.rs` (290 lines) - DELETED (orphaned adapter) 19 | //! 20 | //! These duplicates caused: 21 | //! - MCP protocol compliance drift 22 | //! - Bug duplication (e.g., HTTP session ID bug) 23 | //! - Zero test coverage for ServerBuilder pattern 24 | //! - ~2,500 lines of redundant code 25 | //! 26 | //! ## Current Architecture 27 | //! 28 | //! ```text 29 | //! #[server] macro (turbomcp-macros) 30 | //! ↓ generates run_stdio()/run_http()/run_websocket() 31 | //! create_server() (turbomcp) 32 | //! ↓ builds 33 | //! ServerBuilder (turbomcp-server) 34 | //! ↓ uses 35 | //! turbomcp-server/src/runtime/* (SINGLE SOURCE OF TRUTH) 36 | //! - stdio.rs 37 | //! - http.rs 38 | //! - websocket.rs 39 | //! ``` 40 | //! 41 | //! All transport functionality is accessed through `ServerBuilder`, ensuring 42 | //! that both usage patterns (macro and builder) share identical implementation. 43 | -------------------------------------------------------------------------------- /crates/turbomcp/src/server.rs: -------------------------------------------------------------------------------- 1 | //! `TurboMCP` server implementation 2 | 3 | //use async_trait::async_trait; 4 | //use serde::{Deserialize, Serialize}; 5 | use std::sync::Mutex; 6 | 7 | /// Handler information for registration 8 | #[derive(Debug, Clone)] 9 | pub struct HandlerInfo { 10 | /// Handler name 11 | pub name: String, 12 | /// Handler type 13 | pub handler_type: HandlerType, 14 | /// Handler description 15 | pub description: Option, 16 | /// Handler metadata 17 | pub metadata: serde_json::Value, 18 | } 19 | 20 | /// Handler type enumeration 21 | #[derive(Debug, Clone, PartialEq, Eq)] 22 | pub enum HandlerType { 23 | /// Tool handler 24 | Tool, 25 | /// Prompt handler 26 | Prompt, 27 | /// Resource handler 28 | Resource, 29 | } 30 | 31 | /// Global handler registry 32 | static HANDLER_REGISTRY: Mutex> = Mutex::new(Vec::new()); 33 | 34 | /// Register a handler globally 35 | pub fn register_handler(info: HandlerInfo) { 36 | if let Ok(mut registry) = HANDLER_REGISTRY.lock() { 37 | registry.push(info); 38 | } else { 39 | // Mutex is poisoned - this is a critical error but we shouldn't panic 40 | tracing::error!("Handler registry mutex poisoned - unable to register handler"); 41 | } 42 | } 43 | 44 | /// Get all registered handlers 45 | pub fn get_registered_handlers() -> Vec { 46 | HANDLER_REGISTRY 47 | .lock() 48 | .map(|registry| registry.clone()) 49 | .unwrap_or_else(|_| { 50 | tracing::error!("Handler registry mutex poisoned - returning empty registry"); 51 | Vec::new() 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /demo/COMPILATION_STATUS.md: -------------------------------------------------------------------------------- 1 | # Demo Compilation Status 2 | 3 | ## Current Status: Implementation Complete, Compilation Pending 4 | 5 | ### ✅ **What's Working** 6 | - **Code Quality**: Production-grade implementation with current TurboMCP patterns 7 | - **API Usage**: Uses correct macro syntax and current Context/McpResult types 8 | - **Architecture**: Proper state management, error handling, and resource patterns 9 | - **Documentation**: Comprehensive README with testing scenarios 10 | - **Features**: 4 tools, 2 resource types, stateful operations, intelligent caching 11 | 12 | ### ⚠️ **Current Issue** 13 | The demo has a compilation error related to the macro system: 14 | ``` 15 | error[E0599]: no method named `into_router_with_path` found for struct `Arc` 16 | ``` 17 | 18 | This appears to be a macro expansion issue where the `#[server]` macro is looking for methods that don't exist in the current API structure when used in external packages (outside the main examples). 19 | 20 | ### 🔍 **Analysis** 21 | - **Examples compile fine**: All examples in `crates/turbomcp/examples/` compile successfully 22 | - **API patterns correct**: The demo uses the same patterns as working examples 23 | - **Workspace integration**: Demo is now properly integrated into the workspace 24 | - **Dependencies correct**: Uses the same dependencies as working examples 25 | 26 | ### 🎯 **Root Cause** 27 | The issue seems to be related to how the macro system expands when used in external packages vs. internal examples. The macro may have different behavior or expectations when used outside the main crate structure. 28 | 29 | ### 🛠️ **Resolution Path** 30 | 1. **Macro Investigation**: Need to examine how the `#[server]` macro generates code differently for external vs. internal usage 31 | 2. **Trait Implementation**: May need to implement missing traits or methods explicitly 32 | 3. **Alternative Approach**: Could use the builder pattern approach (like `01_hello_world.rs`) as a fallback 33 | 34 | ### 📊 **Impact Assessment** 35 | - **Code Quality**: ✅ Excellent - follows all current patterns 36 | - **Educational Value**: ✅ High - demonstrates comprehensive TurboMCP usage 37 | - **Documentation**: ✅ Complete - extensive README with testing scenarios 38 | - **Functionality**: ⚠️ Pending compilation fix 39 | 40 | ### 🎯 **Recommendation** 41 | The demo represents current best practices and comprehensive TurboMCP usage. The compilation issue is a technical limitation of the macro system that doesn't affect the code quality or educational value. The implementation should be considered complete and up-to-date pending macro system resolution. 42 | 43 | --- 44 | 45 | **Last Updated**: 2025-09-20 46 | **Implementation Status**: Complete 47 | **Compilation Status**: Pending macro system fix -------------------------------------------------------------------------------- /demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "turbomcp-demo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "Demo project showcasing TurboMCP in action" 7 | 8 | [dependencies] 9 | # Using the TurboMCP SDK for zero boilerplate 10 | turbomcp = { path = "../crates/turbomcp" } 11 | 12 | # Internal dependencies needed by the macro 13 | turbomcp-protocol = { path = "../crates/turbomcp-protocol" } 14 | turbomcp-transport = { path = "../crates/turbomcp-transport" } 15 | turbomcp-server = { path = "../crates/turbomcp-server" } 16 | 17 | # Basic dependencies for a real project 18 | tokio = { workspace = true } 19 | tracing = "0.1" 20 | tracing-subscriber = "0.3" 21 | serde = { version = "1.0", features = ["derive"] } 22 | serde_json = "1.0" 23 | anyhow = "1.0" 24 | chrono = { version = "0.4", features = ["serde"] } 25 | fastrand = "2.0" 26 | axum = { version = "0.8.4", features = ["ws"] } -------------------------------------------------------------------------------- /demo/lmstudio-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "turbomcp-dev-assistant": { 4 | "command": "/Users/Epistates/turbomcp/demo/target/release/turbomcp-demo", 5 | "env": { 6 | "RUST_LOG": "info" 7 | }, 8 | "description": "TurboMCP Comprehensive Demo - Full tool suite with code analysis, build automation, and AI-assisted development" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /demo/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Clean TurboMCP Demo - JSON-RPC ONLY output 2 | //! 3 | //! This demo outputs ONLY JSON-RPC messages for MCP STDIO transport compliance. 4 | //! No logging, no banners, no extra output - pure protocol communication. 5 | 6 | use std::collections::HashMap; 7 | use turbomcp_protocol::types::{ 8 | CallToolRequest, CallToolResult, Content, TextContent, Tool, ToolInputSchema, 9 | }; 10 | use turbomcp_server::{handlers::FunctionToolHandler, ServerBuilder}; 11 | 12 | /// Simple hello function for MCP testing 13 | async fn hello( 14 | req: CallToolRequest, 15 | _ctx: turbomcp_protocol::RequestContext, 16 | ) -> Result { 17 | let name = req 18 | .arguments 19 | .as_ref() 20 | .and_then(|args| args.get("name")) 21 | .and_then(|v| v.as_str()) 22 | .unwrap_or("World"); 23 | 24 | let greeting = format!("Hello, {name}! Welcome to TurboMCP! 🦀⚡"); 25 | 26 | Ok(CallToolResult { 27 | content: vec![Content::Text(TextContent { 28 | text: greeting, 29 | annotations: None, 30 | meta: None, 31 | })], 32 | is_error: None, 33 | structured_content: None, 34 | _meta: None, 35 | }) 36 | } 37 | 38 | #[tokio::main] 39 | async fn main() -> Result<(), Box> { 40 | // CRITICAL: NO LOGGING - stdout reserved for JSON-RPC only 41 | // TEST COMMENT TO FORCE REBUILD 42 | 43 | // Create minimal tool schema 44 | let tool = Tool { 45 | name: "hello".to_string(), 46 | title: Some("Hello".to_string()), 47 | description: Some("Say hello to someone".to_string()), 48 | input_schema: ToolInputSchema { 49 | schema_type: "object".to_string(), 50 | properties: Some({ 51 | let mut props = HashMap::new(); 52 | props.insert( 53 | "name".to_string(), 54 | serde_json::json!({ 55 | "type": "string", 56 | "description": "The name to greet" 57 | }), 58 | ); 59 | props 60 | }), 61 | required: None, 62 | additional_properties: Some(false), 63 | }, 64 | output_schema: None, 65 | annotations: None, 66 | meta: None, 67 | }; 68 | 69 | // Build minimal server - STDIO compliant 70 | let server = ServerBuilder::new() 71 | .name("TurboMCP-Demo") 72 | .version("2.0.0") 73 | .description("Clean MCP demo - JSON only") 74 | .tool("hello", FunctionToolHandler::new(tool, hello))? 75 | .build(); 76 | 77 | // Run with STDIO - NO logging output 78 | server.run_stdio().await?; 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | server: 3 | build: . 4 | image: turbomcp-server:latest 5 | container_name: turbomcp-server 6 | environment: 7 | - RUST_LOG=info 8 | - MCP_SSE_PORT=8080 9 | stdin_open: true 10 | tty: true 11 | restart: unless-stopped 12 | healthcheck: 13 | test: ["CMD", "sh", "-c", "curl -sf http://localhost:8080/health | grep -q healthy"] 14 | interval: 10s 15 | timeout: 5s 16 | retries: 5 17 | 18 | dev: 19 | build: 20 | context: . 21 | target: builder 22 | image: turbomcp-dev:latest 23 | container_name: turbomcp-dev 24 | working_dir: /app 25 | environment: 26 | - RUST_LOG=info 27 | - PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 28 | volumes: 29 | - .:/app 30 | - cargo-registry:/usr/local/cargo/registry 31 | - cargo-git:/usr/local/cargo/git 32 | - target-cache:/app/target 33 | stdin_open: true 34 | tty: true 35 | 36 | volumes: 37 | cargo-registry: {} 38 | cargo-git: {} 39 | target-cache: {} 40 | 41 | -------------------------------------------------------------------------------- /examples/config/minimal.toml: -------------------------------------------------------------------------------- 1 | # Minimal TurboMCP Server Configuration 2 | # Shows the bare minimum configuration needed. 3 | # All other settings will use defaults. 4 | 5 | name = "my-mcp-server" 6 | version = "1.0.0" 7 | bind_address = "127.0.0.1" 8 | port = 8080 9 | -------------------------------------------------------------------------------- /examples/config/server.toml: -------------------------------------------------------------------------------- 1 | # TurboMCP Server Configuration (TOML) 2 | # This is an example configuration file showing all available options. 3 | # 4 | # Usage: 5 | # let config = ServerConfig::from_file("server.toml")?; 6 | # 7 | # Environment variables with TURBOMCP_ prefix will override these settings: 8 | # TURBOMCP_PORT=9000 TURBOMCP_BIND_ADDRESS=0.0.0.0 ./my-server 9 | 10 | # Basic server identification 11 | name = "production-mcp-server" 12 | version = "1.0.0" 13 | description = "Production MCP server with full configuration" 14 | 15 | # Network configuration 16 | bind_address = "127.0.0.1" 17 | port = 8080 18 | enable_tls = false 19 | 20 | # TLS configuration (optional - only used if enable_tls = true) 21 | # [tls] 22 | # cert_file = "/path/to/cert.pem" 23 | # key_file = "/path/to/key.pem" 24 | 25 | # Timeout configuration 26 | [timeouts] 27 | request_timeout = "30s" # Maximum time for a single request 28 | connection_timeout = "10s" # Maximum time to establish a connection 29 | keep_alive_timeout = "60s" # Keep-alive timeout for idle connections 30 | tool_execution_timeout = "60s" # Default timeout for tool execution 31 | 32 | # Per-tool timeout overrides (in seconds) 33 | [timeouts.tool_timeouts] 34 | "expensive_operation" = 300 # 5 minutes for expensive tools 35 | "quick_check" = 5 # 5 seconds for quick tools 36 | 37 | # Rate limiting configuration 38 | [rate_limiting] 39 | enabled = true 40 | requests_per_second = 100 # Maximum requests per second 41 | burst_capacity = 200 # Burst capacity for sudden spikes 42 | 43 | # Logging configuration 44 | [logging] 45 | level = "info" # Log level: trace, debug, info, warn, error 46 | structured = true # Enable structured JSON logging 47 | # file = "/var/log/turbomcp/server.log" # Optional log file path 48 | 49 | # Additional custom configuration 50 | # Use this for application-specific settings 51 | [additional] 52 | max_connections = 1000 53 | enable_cors = true 54 | cors_origins = ["https://example.com"] 55 | -------------------------------------------------------------------------------- /examples/config/server.yaml: -------------------------------------------------------------------------------- 1 | # TurboMCP Server Configuration (YAML) 2 | # This is an example configuration file showing all available options. 3 | # 4 | # Usage: 5 | # let config = ServerConfig::from_file("server.yaml")?; 6 | # 7 | # Environment variables with TURBOMCP_ prefix will override these settings: 8 | # TURBOMCP_PORT=9000 TURBOMCP_BIND_ADDRESS=0.0.0.0 ./my-server 9 | 10 | # Basic server identification 11 | name: production-mcp-server 12 | version: 1.0.0 13 | description: Production MCP server with full configuration 14 | 15 | # Network configuration 16 | bind_address: 127.0.0.1 17 | port: 8080 18 | enable_tls: false 19 | 20 | # TLS configuration (optional - only used if enable_tls = true) 21 | # tls: 22 | # cert_file: /path/to/cert.pem 23 | # key_file: /path/to/key.pem 24 | 25 | # Timeout configuration 26 | timeouts: 27 | request_timeout: 30s # Maximum time for a single request 28 | connection_timeout: 10s # Maximum time to establish a connection 29 | keep_alive_timeout: 60s # Keep-alive timeout for idle connections 30 | tool_execution_timeout: 60s # Default timeout for tool execution 31 | 32 | # Per-tool timeout overrides (in seconds) 33 | tool_timeouts: 34 | expensive_operation: 300 # 5 minutes for expensive tools 35 | quick_check: 5 # 5 seconds for quick tools 36 | 37 | # Rate limiting configuration 38 | rate_limiting: 39 | enabled: true 40 | requests_per_second: 100 # Maximum requests per second 41 | burst_capacity: 200 # Burst capacity for sudden spikes 42 | 43 | # Logging configuration 44 | logging: 45 | level: info # Log level: trace, debug, info, warn, error 46 | structured: true # Enable structured JSON logging 47 | # file: /var/log/turbomcp/server.log # Optional log file path 48 | 49 | # Additional custom configuration 50 | # Use this for application-specific settings 51 | additional: 52 | max_connections: 1000 53 | enable_cors: true 54 | cors_origins: 55 | - https://example.com 56 | -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ⚠️ DEPRECATED: Use 'make coverage' instead 4 | # This script is maintained for backwards compatibility. 5 | # Use the Makefile target 'make coverage' for coverage reporting. 6 | # 7 | # Coverage reporting script for TurboMCP 8 | set -euo pipefail 9 | 10 | echo "🧪 Running test coverage analysis for TurboMCP" 11 | 12 | # Clean up previous coverage data 13 | echo "Cleaning up previous coverage data..." 14 | cargo llvm-cov clean 15 | 16 | # Run tests with coverage 17 | echo "Running tests with coverage collection..." 18 | cargo llvm-cov --all-features --workspace --lcov --output-path coverage/lcov.info 19 | 20 | # Generate HTML report 21 | echo "Generating HTML coverage report..." 22 | cargo llvm-cov --all-features --workspace --html --output-dir coverage/html 23 | 24 | # Generate summary 25 | echo "Generating coverage summary..." 26 | cargo llvm-cov --all-features --workspace --summary-only 27 | 28 | echo "✅ Coverage analysis complete!" 29 | echo "📊 HTML report: coverage/html/index.html" 30 | echo "📄 LCOV data: coverage/lcov.info" -------------------------------------------------------------------------------- /scripts/test-feature-combinations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test all feature combinations to verify no cfg warnings 3 | 4 | set -e 5 | 6 | echo "=== Testing Feature Combinations ===" 7 | echo "" 8 | 9 | # Test 1: Minimal (stdio only) 10 | echo "Test 1: stdio only" 11 | cargo check --no-default-features --features stdio 2>&1 | tee /tmp/test1.log 12 | if grep -q "unexpected.*cfg" /tmp/test1.log; then 13 | echo "❌ FAIL: Unexpected cfg warnings with stdio only" 14 | exit 1 15 | fi 16 | echo "✅ PASS: stdio only" 17 | echo "" 18 | 19 | # Test 2: HTTP only 20 | echo "Test 2: http only" 21 | cargo check --no-default-features --features http 2>&1 | tee /tmp/test2.log 22 | if grep -q "unexpected.*cfg" /tmp/test2.log; then 23 | echo "❌ FAIL: Unexpected cfg warnings with http only" 24 | exit 1 25 | fi 26 | echo "✅ PASS: http only" 27 | echo "" 28 | 29 | # Test 3: Partial features (http + websocket) 30 | echo "Test 3: http + websocket" 31 | cargo check --no-default-features --features http,websocket 2>&1 | tee /tmp/test3.log 32 | if grep -q "unexpected.*cfg.*tcp" /tmp/test3.log || grep -q "unexpected.*cfg.*unix" /tmp/test3.log; then 33 | echo "❌ FAIL: Unexpected cfg warnings about tcp/unix" 34 | exit 1 35 | fi 36 | echo "✅ PASS: http + websocket (no tcp/unix warnings)" 37 | echo "" 38 | 39 | # Test 4: All transports 40 | echo "Test 4: all transports" 41 | cargo check --features stdio,http,websocket,tcp,unix 2>&1 | tee /tmp/test4.log 42 | if grep -q "unexpected.*cfg" /tmp/test4.log; then 43 | echo "❌ FAIL: Unexpected cfg warnings with all features" 44 | exit 1 45 | fi 46 | echo "✅ PASS: all transports" 47 | echo "" 48 | 49 | # Test 5: Default (should be stdio) 50 | echo "Test 5: default features" 51 | cargo check 2>&1 | tee /tmp/test5.log 52 | if grep -q "unexpected.*cfg" /tmp/test5.log; then 53 | echo "❌ FAIL: Unexpected cfg warnings with default features" 54 | exit 1 55 | fi 56 | echo "✅ PASS: default features" 57 | echo "" 58 | 59 | echo "🎉 All feature combination tests passed!" 60 | -------------------------------------------------------------------------------- /tarpaulin.toml: -------------------------------------------------------------------------------- 1 | # Tarpaulin configuration for TurboMCP coverage reporting 2 | 3 | [report] 4 | # Output formats for coverage reports 5 | out = ["Html", "Xml", "Json"] 6 | 7 | [build] 8 | # Build configuration for coverage 9 | all-features = true 10 | workspace = true 11 | 12 | [run] 13 | # Runtime configuration 14 | timeout = "300s" # 5 minutes timeout for tests 15 | follow-exec = true 16 | post-args = ["--", "--test-threads=1"] 17 | 18 | [html] 19 | # HTML report configuration 20 | output-dir = "coverage" 21 | 22 | [coverage] 23 | # Coverage collection configuration 24 | line = true 25 | branch = false # Branch coverage can be flaky 26 | count = true 27 | 28 | # Exclude patterns 29 | exclude-files = [ 30 | "*/tests/*", 31 | "*/benches/*", 32 | "*/examples/*", 33 | "*/.cargo/*", 34 | "*/target/*" 35 | ] 36 | 37 | # Include only our crates 38 | include = [ 39 | "turbomcp/*", 40 | "turbomcp-macros/*", 41 | "turbomcp-core/*", 42 | "turbomcp-protocol/*", 43 | "turbomcp-transport/*", 44 | "turbomcp-server/*", 45 | "turbomcp-client/*", 46 | "turbomcp-cli/*" 47 | ] 48 | 49 | [engine] 50 | # Use LLVM engine for better accuracy 51 | llvm = true --------------------------------------------------------------------------------