use std::env;
#[derive(Debug, Clone)]
pub struct Config {
pub server: ServerConfig,
pub oauth: OAuthConfig,
pub github: GitHubConfig,
pub cognito: CognitoConfig,
pub logging: LoggingConfig,
}
#[derive(Debug, Clone)]
pub struct ServerConfig {
pub bind_address: String,
pub host: String,
pub port: u16,
pub name: String,
pub version: String,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct OAuthConfig {
pub authorization_endpoint: String,
pub token_endpoint: String,
pub client_registration_endpoint: Option<String>,
pub scopes_supported: Vec<String>,
pub allow_implicit_flow: bool,
pub allow_public_client_registration: bool,
pub cors_max_age: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct GitHubConfig {
pub client_id: String,
pub client_secret: String,
pub redirect_uri: String,
pub scope: String,
}
#[derive(Debug, Clone)]
pub struct CognitoConfig {
pub client_id: String,
pub client_secret: Option<String>,
pub redirect_uri: String,
pub scope: String,
pub cognito_domain: String,
pub region: String,
pub user_pool_id: String,
}
#[derive(Debug, Clone)]
pub struct LoggingConfig {
pub level: String,
pub format: String,
}
impl Config {
pub fn default() -> Self {
Config {
server: ServerConfig {
bind_address: "0.0.0.0:8080".to_string(),
host: "localhost".to_string(),
port: 8080,
name: "MCP OAuth Server".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
description: "MCP server with OAuth authentication capabilities".to_string(),
},
oauth: OAuthConfig {
authorization_endpoint: "/oauth/authorize".to_string(),
token_endpoint: "/oauth/token".to_string(),
client_registration_endpoint: Some("/oauth/register".to_string()),
scopes_supported: vec!["read".to_string(), "write".to_string()],
allow_implicit_flow: false,
allow_public_client_registration: true,
cors_max_age: Some(86400),
},
github: GitHubConfig {
client_id: "".to_string(),
client_secret: "".to_string(),
redirect_uri: "http://localhost:8080/oauth/callback".to_string(),
scope: "read:user".to_string(),
},
cognito: CognitoConfig {
client_id: "".to_string(),
client_secret: None,
redirect_uri: "http://localhost:8080/oauth/callback".to_string(),
scope: "openid email profile phone".to_string(),
cognito_domain: "mydomain.auth.us-east-1.amazoncognito.com".to_string(),
region: "us-east-1".to_string(),
user_pool_id: "us-east-1_XXXXXXXXX".to_string(),
},
logging: LoggingConfig {
level: "info".to_string(),
format: "json".to_string(),
},
}
}
pub fn from_env() -> Result<Self, ConfigError> {
let host = env::var("MCP_HOST").unwrap_or_else(|_| "localhost".to_string());
let port = env::var("MCP_PORT")
.unwrap_or_else(|_| "8080".to_string())
.parse::<u16>()
.map_err(|_| ConfigError::InvalidPort)?;
let bind_address =
env::var("MCP_BIND_ADDRESS").unwrap_or_else(|_| format!("0.0.0.0:{}", port));
let github_client_id =
env::var("GITHUB_CLIENT_ID").map_err(|_| ConfigError::MissingGitHubClientId)?;
let github_client_secret =
env::var("GITHUB_CLIENT_SECRET").map_err(|_| ConfigError::MissingGitHubClientSecret)?;
let cognito_client_id =
env::var("COGNITO_CLIENT_ID").map_err(|_| ConfigError::MissingCognitoClientId)?;
let cognito_client_secret = env::var("COGNITO_CLIENT_SECRET").ok();
let cognito_domain =
env::var("COGNITO_DOMAIN").map_err(|_| ConfigError::MissingCognitoDomain)?;
let cognito_region =
env::var("COGNITO_REGION").map_err(|_| ConfigError::MissingCognitoRegion)?;
let cognito_user_pool_id =
env::var("COGNITO_USER_POOL_ID").map_err(|_| ConfigError::MissingCognitoUserPoolId)?;
Ok(Config {
server: ServerConfig {
bind_address,
host: host.clone(),
port,
name: "MCP OAuth Server".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
description: "MCP server with OAuth authentication capabilities".to_string(),
},
oauth: OAuthConfig {
authorization_endpoint: "/oauth/authorize".to_string(),
token_endpoint: "/oauth/token".to_string(),
client_registration_endpoint: Some("/oauth/register".to_string()),
scopes_supported: vec!["read".to_string(), "write".to_string()],
allow_implicit_flow: false,
allow_public_client_registration: true,
cors_max_age: Some(86400),
},
github: GitHubConfig {
client_id: github_client_id,
client_secret: github_client_secret,
redirect_uri: format!("http://{}:{}/oauth/callback", host, port),
scope: env::var("GITHUB_SCOPE").unwrap_or_else(|_| "read:user".to_string()),
},
cognito: CognitoConfig {
client_id: cognito_client_id,
client_secret: cognito_client_secret,
redirect_uri: format!("http://{}:{}/oauth/callback", host, port),
scope: env::var("COGNITO_SCOPE")
.unwrap_or_else(|_| "openid email profile phone".to_string()),
cognito_domain,
region: cognito_region,
user_pool_id: cognito_user_pool_id,
},
logging: LoggingConfig {
level: env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()),
format: env::var("LOG_FORMAT").unwrap_or_else(|_| "json".to_string()),
},
})
}
pub fn github_oauth_config(&self) -> oauth_provider_rs::GitHubOAuthConfig {
oauth_provider_rs::GitHubOAuthConfig {
client_id: self.github.client_id.clone(),
client_secret: self.github.client_secret.clone(),
redirect_uri: self.github.redirect_uri.clone(),
scope: self.github.scope.clone(),
provider_name: "github".to_string(),
}
}
pub fn github_oauth_provider_config(
&self,
) -> oauth_provider_rs::provider_trait::OAuthProviderConfig {
oauth_provider_rs::provider_trait::OAuthProviderConfig {
client_id: self.github.client_id.clone(),
client_secret: self.github.client_secret.clone(),
redirect_uri: self.github.redirect_uri.clone(),
scope: self.github.scope.clone(),
provider_name: "github".to_string(),
}
}
pub fn cognito_oauth_config(&self) -> oauth_provider_rs::CognitoOAuthConfig {
oauth_provider_rs::CognitoOAuthConfig {
client_id: self.cognito.client_id.clone(),
client_secret: self.cognito.client_secret.clone().unwrap_or_default(),
redirect_uri: self.cognito.redirect_uri.clone(),
scope: self.cognito.scope.clone(),
provider_name: "cognito".to_string(),
}
}
pub fn cognito_oauth_provider_config(
&self,
) -> oauth_provider_rs::provider_trait::OAuthProviderConfig {
oauth_provider_rs::provider_trait::OAuthProviderConfig {
client_id: self.cognito.client_id.clone(),
client_secret: self.cognito.client_secret.clone().unwrap_or_default(),
redirect_uri: self.cognito.redirect_uri.clone(),
scope: self.cognito.scope.clone(),
provider_name: "cognito".to_string(),
}
}
pub fn oauth_provider_config(
&self,
) -> oauth_provider_rs::http_integration::OAuthProviderConfig {
oauth_provider_rs::http_integration::OAuthProviderConfig {
authorization_endpoint: self.oauth.authorization_endpoint.clone(),
token_endpoint: self.oauth.token_endpoint.clone(),
client_registration_endpoint: self.oauth.client_registration_endpoint.clone(),
scopes_supported: Some(self.oauth.scopes_supported.clone()),
allow_implicit_flow: self.oauth.allow_implicit_flow,
api_routes: vec!["/api".to_string()],
allow_public_client_registration: self.oauth.allow_public_client_registration,
cors_allowed_origins: vec![],
cors_allowed_methods: vec![],
cors_allowed_headers: vec![],
cors_max_age: self.oauth.cors_max_age,
error_callback: None,
}
}
pub fn bind_socket_addr(&self) -> Result<std::net::SocketAddr, ConfigError> {
self.server
.bind_address
.parse()
.map_err(|_| ConfigError::InvalidBindAddress)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("Missing GITHUB_CLIENT_ID environment variable")]
MissingGitHubClientId,
#[error("Missing GITHUB_CLIENT_SECRET environment variable")]
MissingGitHubClientSecret,
#[error("Missing COGNITO_CLIENT_ID environment variable")]
MissingCognitoClientId,
#[error("Missing COGNITO_DOMAIN environment variable")]
MissingCognitoDomain,
#[error("Missing COGNITO_REGION environment variable")]
MissingCognitoRegion,
#[error("Missing COGNITO_USER_POOL_ID environment variable")]
MissingCognitoUserPoolId,
#[error("Invalid port number")]
InvalidPort,
#[error("Invalid bind address")]
InvalidBindAddress,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_defaults() {
let config = Config::default();
assert_eq!(config.server.name, "MCP OAuth Server");
assert_eq!(config.oauth.scopes_supported, vec!["read", "write"]);
assert!(!config.oauth.allow_implicit_flow);
assert_eq!(config.github.scope, "read:user");
}
#[test]
fn test_missing_github_config() {
unsafe {
env::remove_var("GITHUB_CLIENT_ID");
env::remove_var("GITHUB_CLIENT_SECRET");
}
let result = Config::from_env();
assert!(result.is_err());
}
}