oauth_cognito_dynamodb_mcp_server/
oauth_cognito_dynamodb_mcp_server.rs

1//! OAuth 2.0 MCP Server with AWS Cognito authentication and DynamoDB storage
2//!
3//! This example demonstrates a complete MCP (Model Context Protocol) server implementation
4//! using AWS Cognito for OAuth 2.0 authentication and DynamoDB for persistent storage.
5//! The server follows microkernel architecture principles where independent handlers
6//! are composed at runtime.
7//!
8//! # Features
9//! - AWS Cognito OAuth 2.0 authentication
10//! - DynamoDB persistent storage for OAuth tokens and client data
11//! - MCP over HTTP (streamable)
12//! - MCP over Server-Sent Events (SSE)
13//! - Microkernel architecture with independent, composable handlers
14//!
15//! # Required Environment Variables
16//! ## Cognito Configuration
17//! - `COGNITO_CLIENT_ID`: Your Cognito app client ID
18//! - `COGNITO_CLIENT_SECRET`: Your Cognito app client secret (optional for public clients)
19//! - `COGNITO_DOMAIN`: Your Cognito domain (e.g., mydomain.auth.us-east-1.amazoncognito.com)
20//! - `COGNITO_REGION`: AWS region (e.g., us-east-1)
21//! - `COGNITO_USER_POOL_ID`: Your Cognito user pool ID (e.g., us-east-1_XXXXXXXXX)
22//! - `COGNITO_SCOPE`: OAuth scopes (default: 'openid email profile phone')
23//!
24//! ## AWS Configuration (for DynamoDB)
25//! - `AWS_ACCESS_KEY_ID`: Your AWS access key ID
26//! - `AWS_SECRET_ACCESS_KEY`: Your AWS secret access key
27//! - `AWS_REGION`: AWS region (should match COGNITO_REGION)
28//!
29//! ## Server Configuration
30//! - `MCP_HOST`: Server host (default: localhost)
31//! - `MCP_PORT`: Server port (default: 8080)
32//! - `DYNAMODB_TABLE_NAME`: DynamoDB table name (default: oauth-storage)
33//! - `DYNAMODB_CREATE_TABLE`: Whether to auto-create table (default: true)
34//!
35//! # Usage
36//! ```bash
37//! # Set environment variables
38//! export COGNITO_CLIENT_ID="your_client_id"
39//! export COGNITO_CLIENT_SECRET="your_client_secret"
40//! export COGNITO_DOMAIN="mydomain.auth.us-east-1.amazoncognito.com"
41//! export COGNITO_REGION="us-east-1"
42//! export COGNITO_USER_POOL_ID="us-east-1_XXXXXXXXX"
43//! export AWS_ACCESS_KEY_ID="your_aws_access_key"
44//! export AWS_SECRET_ACCESS_KEY="your_aws_secret_key"
45//! export AWS_REGION="us-east-1"
46//!
47//! # Run the server
48//! cargo run --example oauth_cognito_dynamodb_mcp_server
49//! ```
50
51use remote_mcp_kernel::{
52    config::{get_cognito_oauth_provider_config, get_cognito_domain, get_cognito_region, get_cognito_user_pool_id, get_bind_socket_addr, get_logging_level, get_server_host, get_server_port}, 
53    error::AppResult, 
54    microkernel::create_full_cognito_microkernel_dynamodb,
55};
56use std::env;
57use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
58
59#[tokio::main]
60async fn main() -> AppResult<()> {
61    // Load environment variables
62    dotenv::dotenv().ok();
63
64    // Initialize tracing
65    init_tracing()?;
66
67    tracing::info!("Starting MCP OAuth server with Cognito and DynamoDB storage...");
68
69    // Create Cognito OAuth configuration
70    let cognito_config = get_cognito_oauth_provider_config()?;
71
72    // Get DynamoDB configuration
73    let table_name =
74        env::var("DYNAMODB_TABLE_NAME").unwrap_or_else(|_| "oauth-storage".to_string());
75    let create_table = env::var("DYNAMODB_CREATE_TABLE")
76        .unwrap_or_else(|_| "true".to_string())
77        .parse::<bool>()
78        .unwrap_or(true);
79
80    // Log configuration
81    log_startup_info(&table_name, create_table);
82
83    // Create microkernel server with Cognito OAuth and DynamoDB storage
84    let microkernel = create_full_cognito_microkernel_dynamodb(
85        cognito_config,
86        get_cognito_domain()?,
87        get_cognito_region()?,
88        get_cognito_user_pool_id()?,
89        table_name,
90        create_table,
91    )
92    .await
93    .map_err(|e| {
94        remote_mcp_kernel::error::AppError::Internal(format!("Failed to create microkernel: {}", e))
95    })?;
96
97    // Start the microkernel server
98    let bind_address = get_bind_socket_addr()?;
99    microkernel.serve(bind_address).await?;
100
101    Ok(())
102}
103
104fn init_tracing() -> AppResult<()> {
105    tracing_subscriber::registry()
106        .with(
107            tracing_subscriber::EnvFilter::try_from_default_env()
108                .unwrap_or_else(|_| get_logging_level().as_str().into()),
109        )
110        .with(tracing_subscriber::fmt::layer())
111        .init();
112
113    Ok(())
114}
115
116fn log_startup_info(table_name: &str, create_table: bool) {
117    use remote_mcp_kernel::config::{get_server_host, get_server_port, get_server_version, get_cognito_client_id, get_cognito_client_secret, get_cognito_domain, get_cognito_region, get_cognito_user_pool_id, get_cognito_scope};
118    
119    println!("🚀 Starting MCP OAuth server with Cognito and DynamoDB storage...");
120    println!("📋 Configuration:");
121    println!("  - Architecture: Microkernel (independent handlers)");
122    println!("  - OAuth Provider: AWS Cognito");
123    println!("  - Storage Backend: DynamoDB");
124    println!("  - Server: {}:{}", get_server_host(), get_server_port().unwrap_or(8080));
125    println!("  - Version: {}", get_server_version());
126    println!();
127
128    println!("🔐 AWS Cognito Configuration:");
129    println!(
130        "  - Client ID: {}",
131        if get_cognito_client_id().is_ok() {
132            "Configured"
133        } else {
134            "Not configured"
135        }
136    );
137    println!(
138        "  - Client Secret: {}",
139        match get_cognito_client_secret() {
140            Some(secret) if !secret.is_empty() => "Configured",
141            _ => "Not configured (Public Client)",
142        }
143    );
144    println!(
145        "  - Domain: {}",
146        get_cognito_domain().unwrap_or_else(|_| "Not configured".to_string())
147    );
148    println!(
149        "  - Region: {}",
150        get_cognito_region().unwrap_or_else(|_| "Not configured".to_string())
151    );
152    println!(
153        "  - User Pool ID: {}",
154        get_cognito_user_pool_id().unwrap_or_else(|_| "Not configured".to_string())
155    );
156    println!("  - Scopes: {}", get_cognito_scope());
157    println!();
158
159    println!("🗄️  DynamoDB Storage Configuration:");
160    println!("  - Table Name: {}", table_name);
161    println!("  - Auto-create Table: {}", create_table);
162    println!("  - TTL Attribute: expires_at");
163    println!();
164
165    println!("🔧 Handlers:");
166    println!("  - OAuth Provider (Cognito authentication & authorization)");
167    println!("  - Streamable HTTP Handler (MCP over HTTP)");
168    println!("  - SSE Handler (MCP over SSE)");
169    println!();
170
171    println!("🔐 Required Environment Variables:");
172    println!("  ## Cognito Configuration");
173    println!("  - COGNITO_CLIENT_ID: Your Cognito app client ID");
174    println!(
175        "  - COGNITO_CLIENT_SECRET: Your Cognito app client secret (optional for public clients)"
176    );
177    println!(
178        "  - COGNITO_DOMAIN: Your Cognito domain (e.g., mydomain.auth.us-east-1.amazoncognito.com)"
179    );
180    println!("  - COGNITO_REGION: AWS region (e.g., us-east-1)");
181    println!("  - COGNITO_USER_POOL_ID: Your Cognito user pool ID (e.g., us-east-1_XXXXXXXXX)");
182    println!("  - COGNITO_SCOPE: OAuth scopes (default: 'openid email profile phone')");
183    println!();
184    println!("  ## AWS Configuration (for DynamoDB)");
185    println!("  - AWS_ACCESS_KEY_ID: Your AWS access key ID");
186    println!("  - AWS_SECRET_ACCESS_KEY: Your AWS secret access key");
187    println!("  - AWS_REGION: AWS region (should match COGNITO_REGION)");
188    println!();
189    println!("  ## Server Configuration");
190    println!("  - MCP_HOST: Server host (default: localhost)");
191    println!("  - MCP_PORT: Server port (default: 8080)");
192    println!("  - DYNAMODB_TABLE_NAME: DynamoDB table name (default: oauth-storage)");
193    println!("  - DYNAMODB_CREATE_TABLE: Whether to auto-create table (default: true)");
194    println!();
195
196    println!("🌐 OAuth 2.0 Endpoints:");
197    let cognito_domain = get_cognito_domain().unwrap_or_else(|_| "Not configured".to_string());
198    println!(
199        "  - Authorization: https://{}/oauth2/authorize",
200        cognito_domain
201    );
202    println!(
203        "  - Token: https://{}/oauth2/token",
204        cognito_domain
205    );
206    println!(
207        "  - JWKS: https://{}/oauth2/jwks",
208        cognito_domain
209    );
210    println!(
211        "  - UserInfo: https://{}/oauth2/userInfo",
212        cognito_domain
213    );
214    println!();
215
216    println!("💾 DynamoDB Table Configuration:");
217    println!("  - Table Structure: Single table design with 'pk' primary key");
218    println!("  - TTL Support: Automatic cleanup using 'expires_at' attribute");
219    println!("  - Billing Mode: Pay-per-request (on-demand)");
220    println!("  - Encryption: Server-side encryption enabled");
221    println!();
222
223    println!("🏗️  Microkernel Architecture:");
224    println!("  - Independent handlers that can operate standalone");
225    println!("  - Runtime composition of services");
226    println!("  - Single responsibility per handler");
227    println!("  - Easy testing and maintenance");
228    println!();
229}