A comprehensive Rust framework for building Model Context Protocol (MCP) servers and clients with modern patterns, extensive tooling, and enterprise-grade features. Fully compliant with MCP 2025-11-25 specification.
300+ passing tests across workspace β’ Complete async SessionContext integration β’ Framework-native testing patterns
- ποΈ 10 Framework Crates: Complete MCP ecosystem with core framework, client library, and serverless support
- π 45+ Comprehensive Examples: Real-world business applications and framework demonstration examples (all validated through comprehensive testing campaign)
- π§ͺ 300+ Development Tests: Comprehensive test suite with core framework tests, SessionContext integration tests, and framework-native integration tests
- β‘ Multiple Development Patterns: Derive macros, function attributes, declarative macros, and manual implementation
- π Transport Flexibility: HTTP/1.1 and SSE streaming via SessionMcpHandler (WebSocket and stdio planned)
- βοΈ Serverless Support: AWS Lambda integration with streaming responses and SQS event processing
- π§ Development Features: Session management, real-time notifications, performance monitoring, and UUID v7 support
- β‘ Performance Optimized: Comprehensive benchmarking suite with >1000 RPS throughput, <100ms response times, and extensive stress testing
use turul_mcp_derive::mcp_tool;
use turul_mcp_server::prelude::*;
#[mcp_tool(name = "add", description = "Add two numbers")]
async fn add(
#[param(description = "First number")] a: f64,
#[param(description = "Second number")] b: f64,
) -> McpResult<f64> {
Ok(a + b) // Framework wraps as {"output": 8.0} in JSON-RPC response
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = McpServer::builder()
.name("calculator-server")
.version("1.0.0")
.tool_fn(add) // Use function name directly
.bind_address("127.0.0.1:8641".parse()?) // Default port; customize as needed
.build()?;
server.run().await
}use turul_mcp_derive::McpTool;
use turul_mcp_server::prelude::*;
#[derive(McpTool, Clone)]
#[tool(name = "calculator", description = "Mathematical operations")]
struct Calculator {
#[param(description = "First number")]
a: f64,
#[param(description = "Second number")]
b: f64,
#[param(description = "Operation (+, -, *, /)")]
operation: String,
}
impl Calculator {
async fn execute(&self, _session: Option<SessionContext>) -> McpResult<f64> {
match self.operation.as_str() {
"+" => Ok(self.a + self.b),
"-" => Ok(self.a - self.b),
"*" => Ok(self.a * self.b),
"/" => {
if self.b == 0.0 {
Err("Division by zero".into())
} else {
Ok(self.a / self.b)
}
},
_ => Err("Invalid operation".into()),
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = McpServer::builder()
.name("calculator-server")
.version("1.0.0")
.tool(Calculator { a: 0.0, b: 0.0, operation: "+".to_string() })
.bind_address("127.0.0.1:8642".parse()?) // Different port to avoid conflicts
.build()?;
server.run().await
}Create resources that provide data and files using the .resource_fn() method:
use turul_mcp_derive::mcp_resource;
use turul_mcp_server::prelude::*;
use turul_mcp_protocol::resources::ResourceContent;
// Static resource
#[mcp_resource(
uri = "file:///config.json",
name = "config",
description = "Application configuration"
)]
async fn get_config() -> McpResult<Vec<ResourceContent>> {
let config = serde_json::json!({
"app_name": "My Server",
"version": "1.0.0"
});
Ok(vec![ResourceContent::blob(
"file:///config.json",
serde_json::to_string_pretty(&config).unwrap(),
"application/json".to_string()
)])
}
// Template resource with parameter extraction
#[mcp_resource(
uri = "file:///users/{user_id}.json",
name = "user_profile",
description = "User profile data"
)]
async fn get_user_profile(user_id: String) -> McpResult<Vec<ResourceContent>> {
let profile = serde_json::json!({
"user_id": user_id,
"username": format!("user_{}", user_id),
"email": format!("{}@example.com", user_id)
});
Ok(vec![ResourceContent::blob(
format!("file:///users/{}.json", user_id),
serde_json::to_string_pretty(&profile).unwrap(),
"application/json".to_string()
)])
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server = McpServer::builder()
.name("resource-server")
.version("1.0.0")
.resource_fn(get_config) // Static resource
.resource_fn(get_user_profile) // Template: file:///users/{user_id}.json
.bind_address("127.0.0.1:8643".parse()?) // Different port to avoid conflicts
.build()?;
server.run().await
}The framework automatically:
- Detects URI templates (
{user_id}patterns) - Extracts template variables from requests
- Maps them to function parameters
- Registers appropriate resource handlers
# 1. Build the framework
cargo build --workspace
# 2. Run compliance tests
cargo test -p turul-mcp-framework-integration-tests --test mcp_runtime_capability_validation
# 3. Start a simple server
cargo run -p minimal-server
# 4. Test the server (in another terminal)
curl -X POST http://127.0.0.1:8641/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'Core Test Servers:
# Comprehensive server (all MCP features)
cargo run --package comprehensive-server -- --port 8082
# Resource server (17 test resources)
cargo run --package resource-test-server -- --port 8080
# Prompts server (11 test prompts)
cargo run --package prompts-test-server -- --port 8081Business Application Servers:
# Development team resources
cargo run -p resources-server -- --port 8041
# AI development prompts
cargo run -p prompts-server -- --port 8040
# Real-time notifications
cargo run -p notification-server
# Session management demo
cargo run -p stateful-serverStep 1: Initialize Connection
PORT=8080 # Replace with your server's port
curl -X POST http://127.0.0.1:$PORT/mcp \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
},
"id": 1
}' | jqExpected Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"serverInfo": {"name": "server-name", "version": "0.2.0"},
"capabilities": {"tools": {"listChanged": false}}
}
}Step 2: Test Available Operations
# Get session ID from response and test capabilities
SESSION_ID="your-session-id-here"
# If server has tools capability:
curl -X POST http://127.0.0.1:$PORT/mcp \
-H 'Content-Type: application/json' \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}' | jq
# If server has resources capability:
curl -X POST http://127.0.0.1:$PORT/mcp \
-H 'Content-Type: application/json' \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"resources/list","params":{},"id":3}' | jqFor detailed testing instructions, server running guides, and compliance verification:
This guide includes:
- β All server running instructions with expected outputs
- β Manual MCP 2025-11-25 compliance verification
- β SSE event stream testing procedures
- β Performance testing and troubleshooting
- β CI/CD integration examples
# Create and run compliance check
cat > quick_check.sh << 'EOF'
#!/bin/bash
PORT=${1:-8080}
echo "π§ͺ Testing MCP server on port $PORT"
INIT_RESPONSE=$(curl -s -X POST http://127.0.0.1:$PORT/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}')
if [[ $(echo $INIT_RESPONSE | jq -r '.result.protocolVersion') == "2025-11-25" ]]; then
echo "β
MCP 2025-11-25 compliant"
else
echo "β Not compliant"
exit 1
fi
EOF
chmod +x quick_check.sh
# Test any server
cargo run -p minimal-server &
./quick_check.sh 8000The framework provides a trait-based middleware architecture for cross-cutting concerns like authentication, logging, and rate limiting:
use turul_mcp_server::prelude::*;
use std::sync::Arc;
let server = McpServer::builder()
.middleware(Arc::new(AuthMiddleware::new()))
.middleware(Arc::new(LoggingMiddleware))
.middleware(Arc::new(RateLimitMiddleware::new(5, 60)))
.build()?;Key Features:
- β Transport-agnostic (HTTP, Lambda, etc.)
- β Session-aware (read/write session state)
- β Error short-circuiting with semantic JSON-RPC codes
- β Execution order control (FIFO before, LIFO after dispatch)
Examples:
examples/middleware-logging-server- Request timing and tracing (HTTP)examples/middleware-rate-limit-server- Per-session rate limiting (HTTP)examples/middleware-auth-server- API key authentication (HTTP)examples/middleware-auth-lambda- API key authentication (AWS Lambda)
Testing:
- Test HTTP middleware:
bash scripts/test_middleware_live.sh - Test Lambda middleware:
cargo lambda watch --package middleware-auth-lambda
Documentation:
- ADR 012: Middleware Architecture - Core middleware design
- ADR 013: Lambda Authorizer Integration - API Gateway authorizer support
Seamless API Gateway authorizer context extraction for Lambda deployments:
// API Gateway authorizer adds context (userId, tenantId, role, etc.)
// β turul-mcp-aws-lambda adapter extracts β injects x-authorizer-* headers
// β Middleware reads headers β stores in session state
// β Tools access via session.get_typed_state("authorizer")
#[async_trait]
impl McpMiddleware for AuthMiddleware {
async fn before_dispatch(
&self,
ctx: &mut RequestContext<'_>,
_session: Option<&dyn SessionView>,
injection: &mut SessionInjection,
) -> Result<(), MiddlewareError> {
// Extract authorizer context from x-authorizer-* headers
let metadata = ctx.metadata();
let mut authorizer_context = HashMap::new();
for (key, value) in metadata.iter() {
if let Some(field_name) = key.strip_prefix("x-authorizer-") {
if let Some(value_str) = value.as_str() {
authorizer_context.insert(field_name.to_string(), value_str.to_string());
}
}
}
if !authorizer_context.is_empty() {
// Store for tools to access
injection.set_state("authorizer", json!(authorizer_context));
}
Ok(())
}
}Key Features:
- β Supports API Gateway V1 (REST API) and V2 (HTTP API)
- β
Field name sanitization (camelCase β snake_case:
userIdβuser_id) - β Defensive programming (never fails requests)
- β Transport-agnostic (appears as standard HTTP metadata)
- β Session state integration
Example:
examples/middleware-auth-lambda- Full authorizer extraction pattern- Test events:
test-events/apigw-v1-with-authorizer.json,apigw-v2-with-authorizer.json
turul-mcp-server- High-level server builder with session managementturul-mcp-client- Comprehensive client library with HTTP transport supportturul-http-mcp-server- HTTP/SSE transport with CORS and streamingturul-mcp-protocol- Current MCP specification (alias to 2025-11-25)turul-mcp-protocol-2025-11-25- Complete MCP specification implementationturul-mcp-derive- Procedural macros for all MCP areasturul-mcp-builders- Runtime builder patterns for dynamic MCP componentsturul-mcp-json-rpc-server- Transport-agnostic JSON-RPC 2.0 foundationturul-mcp-session-storage- Session storage backends (SQLite, PostgreSQL, DynamoDB)turul-mcp-aws-lambda- AWS Lambda integration for serverless deployment
Modern composable design pattern for all MCP areas:
use turul_mcp_builders::prelude::*; // Framework traits + builders
use turul_mcp_protocol::{ToolSchema, ToolResult, schema::JsonSchema, McpResult};
use turul_mcp_server::{McpTool, SessionContext};
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
struct MyTool;
// Fine-grained trait composition for maximum flexibility
impl HasBaseMetadata for MyTool {
fn name(&self) -> &str { "my_tool" }
}
impl HasDescription for MyTool {
fn description(&self) -> Option<&str> { Some("Tool description") }
}
impl HasInputSchema for MyTool {
fn input_schema(&self) -> &ToolSchema {
static SCHEMA: std::sync::OnceLock<ToolSchema> = std::sync::OnceLock::new();
SCHEMA.get_or_init(|| {
ToolSchema::object()
.with_properties(HashMap::from([
("input".to_string(), JsonSchema::string())
]))
})
}
}
// ... other trait implementations
// ToolDefinition automatically implemented via blanket impl
#[async_trait]
impl McpTool for MyTool {
async fn call(&self, _args: Value, _session: Option<SessionContext>)
-> McpResult<CallToolResult> {
Ok(CallToolResult::success(vec![
ToolResult::text("Tool result")
]))
}
}Supported Areas:
- Tools (
ToolDefinition) - Dynamic tool execution with validation - Resources (
ResourceDefinition) - Static and dynamic content serving - Prompts (
PromptDefinition) - AI interaction template generation - Sampling (
SamplingDefinition) - AI model integration patterns - Completion (
CompletionDefinition) - Context-aware text completion - Logging (
LoggerDefinition) - Dynamic log level management - Roots (
RootDefinition) - Secure file system access boundaries - Elicitation (
ElicitationDefinition) - Structured user input collection - Notifications (
NotificationDefinition) - Real-time event broadcasting
All MCP areas supported with consistent builder pattern:
let server = McpServer::builder()
.name("comprehensive-server")
.version("1.0.0")
.instructions("Full-featured MCP server with all areas")
// Tools
.tool(WeatherTool::new())
.tools(vec![CalculatorTool::new(), ValidationTool::new()])
// Resources
.resource(AppConfigResource::new())
.resources(vec![LogsResource::new(), MetricsResource::new()])
// Prompts
.prompt_provider(CodeReviewPrompt::new())
.prompt_providers(vec![DocumentationPrompt::new(), TestPrompt::new()])
// Sampling
.sampling_provider(CreativeSampling::new())
.sampling_providers(vec![CodeSampling::new(), TechnicalSampling::new()])
// Completion
.completion_provider(IdeCompletion::new())
.completion_providers(vec![SqlCompletion::new(), JsonCompletion::new()])
// Logging
.logger(AuditLogger::new())
.loggers(vec![SecurityLogger::new(), PerformanceLogger::new()])
// Roots
.root_provider(WorkspaceRoot::new())
.root_providers(vec![ConfigRoot::new(), TempRoot::new()])
// Elicitation
.elicitation_provider(OnboardingElicitation::new())
.elicitation_providers(vec![SurveyElicitation::new(), FeedbackElicitation::new()])
// Notifications
.notification_provider(ProgressNotification::new())
.notification_providers(vec![AlertNotification::new(), StatusNotification::new()])
// Server configuration
.bind_address("127.0.0.1:8080".parse()?)
.build()?;All areas implemented with fine-grained trait architecture:
- β
Tools (
ToolDefinition) - Dynamic tool execution with validation, schema generation, and metadata - β
Resources (
ResourceDefinition) - Static and dynamic content serving with access control - β
Prompts (
PromptDefinition) - AI interaction template generation with parameter validation - β
Completion (
CompletionDefinition) - Context-aware text completion with model preferences - β
Logging (
LoggerDefinition) - Dynamic log level management with structured output - β
Notifications (
NotificationDefinition) - Real-time SSE event broadcasting with filtering - β
Roots (
RootDefinition) - Secure file system access boundaries with permissions - β
Sampling (
SamplingDefinition) - AI model integration patterns with constraints - β
Elicitation (
ElicitationDefinition) - Structured user input collection with validation - β Session Management - Stateful operations with UUID v7 correlation IDs
- HTTP/1.1 & HTTP/2 - Standard web transport with JSON-RPC
- Server-Sent Events (SSE) - Development streaming with full real-time capabilities
- Stdio - Command-line integration
- AWS Lambda - Serverless deployment with streaming responses
Note: SSE streaming is in active development with full real-time event broadcasting, session isolation, and correlation ID tracking.
Development servers for actual business problems:
- comprehensive-server β Development Team Integration Platform
- dynamic-resource-server β Enterprise API Data Gateway
- logging-server β Application Audit & Compliance System
- elicitation-server β Customer Onboarding Platform
- notification-server β Development Team Alert System
- completion-server β IDE Auto-Completion Server
- prompts-server β AI-Assisted Development Prompts
- derive-macro-server β Code Generation & Template Engine
- calculator-server β Business Financial Calculator
- resources-server β Development Team Resource Hub
Educational examples showcasing framework patterns:
- Basic Patterns: minimal-server, manual-tools-server, spec-compliant-server
- Advanced Features: stateful-server, pagination-server, version-negotiation-server
- Macro System: tool-macro-example, resource-macro-example, enhanced-tool-macro-test
- Serverless: lambda-turul-mcp-server (AWS Lambda with SQS integration)
- Testing: performance-testing (comprehensive benchmarking suite)
Full serverless implementation with advanced AWS integration:
cd examples/lambda-turul-mcp-server
# Local development
cargo lambda watch
# Deploy to AWS
cargo lambda build --release
sam deploy --guidedFeatures:
- π Dual event sources (HTTP + SQS)
- π‘ 200MB streaming responses
- ποΈ DynamoDB session management
- β‘ Sub-200ms cold starts
- π CloudWatch + X-Ray integration
Framework Excellence: 100+ tests across all components with complete async SessionContext integration:
- β Core Framework Tests - Protocol, server, client, derive macros (36+ passing)
- β SessionContext Integration - Full session state management (8/8 passing)
- β Framework Integration Tests - Proper API usage patterns (7/7 passing)
- β MCP Compliance Tests - Protocol specification validation (28+ passing)
- β Builder Pattern Tests - Runtime tool creation (70+ passing)
- β Example Applications - Real-world scenario validation
# Run all tests - expect 100+ passing
cargo test --workspace
# SessionContext integration tests
cargo test --test session_context_macro_tests
# Framework integration tests (proper patterns)
cargo test --test framework_integration_tests
# MCP compliance tests
cargo test --test mcp_compliance_testsThe RIGHT way to test MCP applications - Use framework APIs, not raw JSON:
// β
CORRECT: Framework integration test
use turul_mcp_server::prelude::*;
use turul_mcp_derive::McpTool;
#[derive(McpTool, Default)]
#[tool(name = "calculator", description = "Add numbers")]
struct Calculator {
#[param(description = "First number")] a: f64,
#[param(description = "Second number")] b: f64,
}
impl Calculator {
async fn execute(&self, _session: Option<SessionContext>) -> McpResult<f64> {
Ok(self.a + self.b)
}
}
#[tokio::test]
async fn test_calculator_tool() {
let tool = Calculator { a: 5.0, b: 3.0 };
// Use framework's McpTool trait
let result = tool.call(json!({"a": 5.0, "b": 3.0}), None).await.unwrap();
// Verify using framework result types
assert_eq!(result.content.len(), 1);
match &result.content[0] {
ToolResult::Text { text, .. } => {
let parsed: Value = serde_json::from_str(text).unwrap();
assert_eq!(parsed["output"], 8.0); // Derive macro uses "output"
}
_ => panic!("Expected text result")
}
}
#[tokio::test]
async fn test_server_integration() {
// Use framework builders
let server = McpServer::builder()
.name("test-server")
.tool(Calculator::default())
.build()
.unwrap();
// Server builds successfully with proper type checking
assert!(true);
}β WRONG: Raw JSON manipulation (old problematic pattern):
// DON'T DO THIS - mixing incompatible JSON-RPC types
let request = json!({
"method": "tools/call",
"params": { "name": "calc" }
});Complete session state management with proper test infrastructure:
// SessionContext integration test
use crate::test_helpers::create_test_session;
#[tokio::test]
async fn test_session_state_management() {
let session = create_test_session().await;
// Session state works perfectly
session.set_typed_state("counter", &42i32).await.unwrap();
let value: i32 = session.get_typed_state("counter").await.unwrap();
assert_eq!(value, 42);
// Progress notifications work
session.notify_progress("processing", 50).await;
// Tool execution with session context
let tool = Calculator { a: 1.0, b: 2.0 };
let result = tool.call(json!({"a": 1.0, "b": 2.0}), Some(session)).await.unwrap();
assert_eq!(result.content.len(), 1);
}Test Infrastructure Available:
TestSessionBuilder- Create real SessionContext instancesTestNotificationBroadcaster- Verify notificationscreate_test_session()- Helper for simple cases- Full storage backend integration
Best for: Quick development, natural syntax, minimal boilerplate
use turul_mcp_derive::mcp_tool;
use turul_mcp_server::prelude::*;
#[mcp_tool(name = "weather", description = "Get weather information")]
async fn get_weather(
#[param(description = "City name")] city: String,
#[param(description = "Temperature unit")] unit: Option<String>,
) -> McpResult<String> {
let unit = unit.unwrap_or_else(|| "celsius".to_string());
Ok(format!("Weather in {}: 22Β°{}", city, if unit == "fahrenheit" { "F" } else { "C" }))
}
// Usage in server
let server = McpServer::builder()
.name("weather-server")
.version("1.0.0")
.tool_fn(get_weather)
.build()?;Best for: Complex tools, organized codebases, multiple related functions
use turul_mcp_derive::McpTool;
use turul_mcp_server::prelude::*;
#[derive(McpTool, Clone)]
#[tool(name = "file_manager", description = "File management operations")]
struct FileManager {
#[param(description = "Operation (create, read, delete)")]
operation: String,
#[param(description = "File path")]
path: String,
#[param(description = "File content (for create operation)")]
content: Option<String>,
}
impl FileManager {
async fn execute(&self, session: Option<SessionContext>) -> McpResult<String> {
match self.operation.as_str() {
"create" => {
let content = self.content.as_ref().unwrap_or(&"Empty file".to_string());
Ok(format!("Created file '{}' with content: {}", self.path, content))
},
"read" => Ok(format!("Reading file: {}", self.path)),
"delete" => {
if let Some(session) = session {
session.notify_progress(&format!("Deleting {}", self.path), 100).await;
}
Ok(format!("Deleted file: {}", self.path))
},
_ => Err("Invalid operation".into()),
}
}
}
// Usage in server
let server = McpServer::builder()
.name("file-server")
.version("1.0.0")
.tool(FileManager {
operation: "create".to_string(),
path: "/tmp/example".to_string(),
content: None,
})
.build()?;Best for: Dynamic tools, configuration-driven systems
use turul_mcp_server::prelude::*;
use serde_json::json;
let multiply_tool = ToolBuilder::new("multiply")
.description("Multiply two numbers")
.number_param("a", "First number")
.number_param("b", "Second number")
.number_output() // Generates {"result": number} schema
.execute(|args| async move {
let a = args.get("a").and_then(|v| v.as_f64())
.ok_or("Missing parameter 'a'")?;
let b = args.get("b").and_then(|v| v.as_f64())
.ok_or("Missing parameter 'b'")?;
Ok(json!({"result": a * b}))
})
.build()
.map_err(|e| format!("Failed to build tool: {}", e))?;
// Usage in server
let server = McpServer::builder()
.name("calculator-server")
.version("1.0.0")
.tool(multiply_tool)
.build()?;Best for: Performance optimization, custom behavior
use turul_mcp_server::prelude::*; // Re-exports builders prelude + framework traits
use turul_mcp_protocol::{ToolSchema, ToolResult, schema::JsonSchema, McpResult};
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
struct ManualTool;
impl HasBaseMetadata for ManualTool {
fn name(&self) -> &str { "manual_tool" }
}
impl HasDescription for ManualTool {
fn description(&self) -> Option<&str> { Some("Manual implementation with full control") }
}
impl HasInputSchema for ManualTool {
fn input_schema(&self) -> &ToolSchema {
static SCHEMA: std::sync::OnceLock<ToolSchema> = std::sync::OnceLock::new();
SCHEMA.get_or_init(|| {
ToolSchema::object()
.with_properties(HashMap::from([
("input".to_string(), JsonSchema::string())
]))
})
}
}
impl HasOutputSchema for ManualTool {
fn output_schema(&self) -> Option<&ToolSchema> { None }
}
impl HasAnnotations for ManualTool {
fn annotations(&self) -> Option<&ToolAnnotations> { None }
}
impl HasToolMeta for ManualTool {
fn tool_meta(&self) -> Option<&HashMap<String, Value>> { None }
}
#[async_trait]
impl McpTool for ManualTool {
async fn call(&self, _args: Value, _session: Option<SessionContext>)
-> McpResult<CallToolResult> {
// Full control over implementation
Ok(CallToolResult::success(vec![
ToolResult::text("Manual tool with complete control")
]))
}
}
// Usage in server
let server = McpServer::builder()
.name("manual-server")
.version("1.0.0")
.tool(ManualTool)
.build()?;Comprehensive MCP client for HTTP transport:
use turul_mcp_client::{McpClient, McpClientBuilder, transport::HttpTransport};
use std::time::Duration;
// Create HTTP transport
let transport = HttpTransport::new("http://localhost:8080/mcp")?;
// Create client using builder pattern
let client = McpClientBuilder::new()
.with_transport(Box::new(transport))
.build();
// Initialize session
let init_result = client.initialize().await?;
// List available tools
let tools = client.list_tools().await?;
// Call a tool
let result = client.call_tool("add", json!({
"a": 10.0,
"b": 20.0
})).await?;
// List and read resources
let resources = client.list_resources().await?;
let content = client.read_resource("config://app.json").await?;- UUID v7 - Time-ordered IDs for better database performance and observability
- Workspace Dependencies - Consistent dependency management across 37 crates
- Rust 2024 Edition - Latest language features and performance improvements
- Tokio/Hyper - High-performance async runtime with HTTP/2 support
- Session Management - Automatic cleanup and state persistence
- Real-time Notifications - SSE-based event streaming
- CORS Support - Browser client compatibility
- Comprehensive Logging - Structured logging with correlation IDs
- Error Handling - Detailed error types with recovery strategies
Full MCP 2025-11-25 specification support:
- β
JSON-RPC 2.0 - Complete request/response with
_metafields - β Protocol Negotiation - Version compatibility and capability exchange
- β Progress Tracking - Long-running operation support
- β Cursor Pagination - Efficient large dataset navigation
- β Session Isolation - Secure multi-client support
- β Transport Agnostic - Multiple transport implementations
# Test tool execution
curl -X POST http://127.0.0.1:8080/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add",
"arguments": {"a": 10, "b": 20}
}
}'
# Test SSE notifications (after getting session ID from above request)
curl -N -H "Accept: text/event-stream" \
-H "Mcp-Session-Id: <session-id>" \
http://127.0.0.1:8080/mcpThe framework includes comprehensive compliance testing for MCP session management specification requirements.
# 1. Start a server with session storage (choose backend: sqlite, postgres, dynamodb, or inmemory)
cargo run --example client-initialise-server -- --port 52950 --storage-backend dynamodb --create-tables
# 2. In another terminal, run the comprehensive compliance test (IMPORTANT: include RUST_LOG=info)
RUST_LOG=info cargo run --example session-management-compliance-test -- http://127.0.0.1:52950/mcp
# 3. Alternative: Use different storage backends
cargo run --example client-initialise-server -- --port 52951 --storage-backend sqlite --create-tables
RUST_LOG=info cargo run --example session-management-compliance-test -- http://127.0.0.1:52951/mcp# Build all workspace crates
cargo build
# Build with release optimizations
cargo build --releaseThe framework includes 300+ comprehensive tests covering all functionality. Test server binaries are automatically built when needed - no manual setup required.
# Run all tests (recommended - includes E2E integration tests)
cargo test --workspace
# Run specific test suite
cargo test --test concurrent_session_advanced
# Run with logging output
RUST_LOG=info cargo test --workspace
# Clean build and test (verifies auto-build works)
cargo clean && cargo test --workspaceKey Features:
- β Auto-build test servers - Missing test binaries are built automatically on first test run
- β
Zero configuration - Just run
cargo testand everything works - β
Clean workspace support -
cargo clean && cargo testworks without manual steps
The test infrastructure automatically builds required test server binaries (resource-test-server, prompts-test-server, tools-test-server, etc.) when running integration tests. This ensures a seamless developer experience.
The comprehensive test validates all MCP session management requirements:
- β Session ID Generation: UUID v7 with cryptographic security and ASCII compliance
- β Session Persistence: Proper session validation and storage backend integration
- β Session Expiry: TTL-based cleanup and 404 responses for expired sessions
- β Client Reinitialize: Graceful session recovery on expiry
- β DELETE Termination: Explicit session termination support
- β Session Isolation: Multi-session security and data separation
π§ͺ MCP Session Management Compliance Test
βββββββββββββββββββββββββββββββββββββββββββ
β
Session ID generation compliance verified
β
Session persistence compliance verified
β
Session expiry compliance verified
β
Client reinitialize compliance verified
β
DELETE session termination compliance verified
β
Session isolation compliance verified
π MCP SESSION MANAGEMENT COMPLIANCE: COMPLETE
βββββββββββββββββββββββββββββββββββββββββββββββ
DynamoDB (Development):
- 5-minute TTL with automatic cleanup
- GSI indexes for efficient queries
- AWS credentials required
SQLite (Development):
- File-based persistence
- 5-minute TTL with background cleanup
- No external dependencies
PostgreSQL (Enterprise):
- Full SQL features with indexing
- 5-minute TTL with efficient cleanup
- Connection string required
InMemory (Testing):
- Fast, no persistence
- 5-minute TTL with memory cleanup
- Zero configuration
// Custom TTL configuration (default: 5 minutes)
let config = DynamoDbConfig {
session_ttl_minutes: 30, // 30-minute session TTL
event_ttl_minutes: 15, // 15-minute event TTL
..Default::default()
};The framework includes comprehensive SSE testing to verify real-time notification streaming:
# Test SSE functionality in prompts package
cargo test --package mcp-prompts-tests --test sse_notifications_test
# Test specific SSE scenarios
cargo test test_sse_prompts_connection_establishment -- --nocapture
cargo test test_sse_prompts_list_changed_notification -- --nocapture
cargo test test_sse_prompts_session_isolation -- --nocapture
# Test SSE functionality in resources package
cargo test --package mcp-resources-tests --test sse_notifications_test
# Test specific resource SSE scenarios
cargo test test_sse_connection_establishment -- --nocapture
cargo test test_sse_resource_list_changed_notification -- --nocapture
cargo test test_sse_session_isolation -- --nocaptureπ Starting MCP Resource Test Server on port 18994
β
Session 01997404-d8f4-7b20-b76d-ac1f4be628a3 created and immediately initialized
β
SSE connection: session=01997404-d908-7e62-ae74-af87f1523836, connection=01997404-d909-7001-8b95-296e806aa1e1
β
Total notifications detected: 1
β
Session ID correlation verified
β
Valid SSE format compliance verified
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
# 1. Start any MCP server with SSE enabled
cargo run --example prompts-server
# 2. Get session ID via initialization
curl -X POST http://127.0.0.1:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# 3. Connect to SSE stream (replace SESSION_ID with actual ID)
curl -N -H "Accept: text/event-stream" \
-H "Mcp-Session-Id: SESSION_ID" \
http://127.0.0.1:8080/mcp
# Expected SSE output:
# id: 0
# event: ping
# data: {"type":"keepalive"}
#
# id: 1
# event: notification
# data: {"type":"resource_update","resource":"prompts/list"}- dynamic-resource-server: API orchestration across Customer, Inventory, Financial, and HR systems
- logging-server: SOX, PCI DSS, GDPR, and HIPAA compliance reporting
- comprehensive-server: Team collaboration with project management and workflow automation
- completion-server: Context-aware IDE completions for multiple languages and frameworks
- prompts-server: AI-powered code review and architecture guidance
- derive-macro-server: Template-based code generation with validation
- elicitation-server: GDPR-compliant customer onboarding with regulatory forms
- notification-server: Real-time incident management with escalation workflows
- Memory Safety - Rust's ownership system prevents common vulnerabilities
- Type Safety - Compile-time validation with automatic schema generation
- Input Validation - Parameter constraints and sanitization
- Session Isolation - Secure multi-tenant operation
- Audit Logging - Comprehensive activity tracking with UUID v7 correlation
- Resource Limits - Configurable timeouts and memory constraints
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Add tests for your changes
- Run the full test suite (
cargo test --workspace) - Benchmark performance impact if applicable
- Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT OR Apache-2.0 License - see the LICENSE files for details.
- Model Context Protocol - The foundational specification
- Tokio - Async runtime powering the framework
- Hyper - HTTP foundation with HTTP/2 support
- Serde - Serialization framework
- Rust Community - For exceptional tooling and ecosystem
- Phase 6 Complete: Session-aware resources implemented with full MCP 2025-11-25 compliance
- 45+ Examples Validated: Comprehensive testing campaign completed across all framework areas
- SSE Streaming Verified: Real-time notifications and session-aware logging working correctly
- Beta Status: Active development with API stability considerations before 1.0.0
Transport & Streaming:
- Lambda SSE: Snapshot-based responses work reliably; real-time streaming requires
run_with_streaming_response - WebSocket Transport: Planned but not yet available (HTTP/1.1 and SSE currently supported)
- CI Environment Testing: SSE tests require port binding capabilities (graceful fallbacks implemented)
Features & Integration:
- Resource Subscriptions:
resources/subscribeMCP spec feature planned for future implementation - Authentication Middleware: OAuth/JWT integration planned for future releases
- Cross-platform Compatibility: Primarily tested on Linux development environments
- Performance Monitoring: Basic benchmarks available, comprehensive monitoring planned
- Concurrency Stress Testing: Some resource tests show occasional failures under extreme load
- Browser Compatibility: CORS support available but may need tuning for specific client requirements
Framework Philosophy: We prioritize honest documentation over inflated claims. This beta status reflects our commitment to transparency about the current development state.
π Ready to build MCP servers? Start with our comprehensive examples or check the getting started guide.
π‘ Need help? Open an issue or check our 45+ validated examples covering everything from simple calculators to enterprise systems.