Configuration System
Seed uses a centralized, environment-based configuration system that consolidates all settings in a single location. Configuration is loaded from environment variables with sensible defaults and comprehensive startup validation.
Overview
The configuration system provides:
- Centralized Configuration: All settings in
src/config/index.ts - Environment-Based: Values loaded from environment variables
- Typed and Validated: TypeScript interfaces with runtime validation
- Startup Validation: ✅ IMPLEMENTED (2026-01-06) - Comprehensive validation on server start
- Modular Structure: Separate files for different concerns
- Sensible Defaults: Works out-of-the-box for development
Configuration Structure
src/config/
├── index.ts # Main configuration export
├── oidc.ts # OIDC/OAuth settings
├── cors.ts # CORS configuration
├── dcr.ts # Dynamic Client Registration settings
├── session.ts # MCP session management settings
├── rate-limit.ts # Rate limiting configuration (MCP + DCR)
├── security.ts # Origin validation and security settings
├── helmet.ts # Helmet security headers (CSP, HSTS, etc.)
├── logging.ts # Logging configuration (level, format)
└── metrics.ts # Metrics collection settingsMain Configuration
File: src/config/index.ts
export const config = {
// Server Configuration
port: parseInt(process.env.PORT ?? "3000", 10),
baseUrl: process.env.BASE_URL ?? "",
authRequired: process.env.AUTH_REQUIRED !== "false",
// Server Metadata
server: {
name: "seed",
version: "0.1.3", // From package.json
},
// Module Configurations
cors: corsConfig,
dcr: dcrConfig,
helmet: helmetConfig,
helmetApi: helmetApiConfig,
oidc: oidcConfig,
session: sessionConfig,
rateLimit: rateLimitConfig,
security: securityConfig,
logging: loggingConfig,
metrics: metricsConfig,
};Server Settings
| Variable | Type | Default | Description |
|---|---|---|---|
PORT | number | 3000 | HTTP server port |
BASE_URL | string | "" | Public base URL for OAuth redirects |
AUTH_REQUIRED | boolean | true | Enable/disable authentication globally |
Example:
PORT=8080
BASE_URL=https://seed.example.com
AUTH_REQUIRED=trueOIDC Configuration
File: src/config/oidc.ts
export const oidcConfig = {
issuer: process.env.OIDC_ISSUER ?? "",
audience: process.env.OIDC_AUDIENCE ?? "",
tokenUrl: process.env.OAUTH_TOKEN_URL ?? "",
authorizationUrl: process.env.OAUTH_AUTHORIZATION_URL ?? "",
jwksUrl: process.env.OIDC_JWKS_URL ?? "",
jwks: {
cacheTtlMs: 60 * 60 * 1000, // 1 hour
refreshBeforeExpiryMs: 5 * 60 * 1000, // 5 minutes
},
};OIDC Settings
| Variable | Required | Description |
|---|---|---|
OIDC_ISSUER | Yes | Identity provider issuer URL (e.g., https://auth.example.com/application/o/my-app/) |
OIDC_AUDIENCE | Yes | Client ID for JWT audience validation (also used as static IdP client) |
OAUTH_TOKEN_URL | Yes | OAuth token endpoint URL |
OAUTH_AUTHORIZATION_URL | Yes | OAuth authorization endpoint URL |
OIDC_JWKS_URL | No | JWKS endpoint URL (auto-discovered from issuer if not provided) |
Example:
OIDC_ISSUER=https://auth.example.com/application/o/my-app/
OIDC_AUDIENCE=my-client-id
OAUTH_TOKEN_URL=https://auth.example.com/application/o/token/
OAUTH_AUTHORIZATION_URL=https://auth.example.com/application/o/authorize/
# OIDC_JWKS_URL is optional, will be discovered from OIDC_ISSUERJWKS Settings
Hardcoded Configuration:
jwks: {
cacheTtlMs: 3600000, // 1 hour cache
refreshBeforeExpiryMs: 300000, // Refresh 5 minutes before expiry
}These settings control JWKS caching behavior and cannot be changed via environment variables.
CORS Configuration
File: src/config/cors.ts
export const corsConfig = {
origin: [
/^http:\/\/localhost(:\d+)?$/, // http://localhost:*
"https://claude.ai", // Claude web app
/^https:\/\/.*\.anthropic\.com$/, // Anthropic domains
...(process.env.CORS_EXTRA_ORIGINS?.split(",") || []),
],
credentials: true,
allowedHeaders: [
"Content-Type",
"Accept",
"Authorization",
"Mcp-Session-Id",
"Last-Event-ID",
],
exposedHeaders: [
"Content-Type",
"Mcp-Session-Id",
],
};CORS Settings
| Variable | Type | Default | Description |
|---|---|---|---|
CORS_EXTRA_ORIGINS | string | "" | Comma-separated list of additional allowed origins |
Default Allowed Origins:
http://localhost:*(any port) - For local developmenthttps://claude.ai- Claude web applicationhttps://*.anthropic.com- Any Anthropic subdomain
Example:
CORS_EXTRA_ORIGINS=https://app1.example.com,https://app2.example.comCORS Headers
Allowed Request Headers:
Content-Type: For JSON requestsAccept: Content negotiationAuthorization: Bearer tokensMcp-Session-Id: MCP session trackingLast-Event-ID: Server-sent events (future use)
Exposed Response Headers:
Content-Type: Response formatMcp-Session-Id: Session identifier
Dynamic Client Registration Configuration
File: src/config/dcr.ts
export const dcrConfig = {
redisUrl: process.env.REDIS_URL ?? "redis://redis:6379",
clientTtlSeconds: parseInt(process.env.DCR_CLIENT_TTL ?? "2592000", 10),
keyPrefix: "dcr:client:",
clientIdPrefix: "seed-",
maxRedirectUris: 10,
};DCR Settings
| Variable | Type | Default | Description |
|---|---|---|---|
REDIS_URL | string | redis://redis:6379 | Redis connection URL |
DCR_CLIENT_TTL | number | 2592000 | Client TTL in seconds (30 days) |
Example:
REDIS_URL=redis://redis.example.com:6379
DCR_CLIENT_TTL=604800 # 7 daysNote: DCR rate limiting configuration has been moved to Rate Limiting Configuration.
Hardcoded DCR Settings
These settings are not configurable via environment:
- keyPrefix:
"dcr:client:"- Redis key prefix for clients - clientIdPrefix:
"seed-"- Prefix for generated client IDs - maxRedirectUris:
10- Maximum redirect URIs per client
Session Configuration
File: src/config/session.ts
export const sessionConfig = {
keyPrefix: process.env.MCP_SESSION_KEY_PREFIX ?? "mcp:session:",
ttlSeconds: parseInt(process.env.MCP_SESSION_TTL_SECONDS ?? "86400", 10),
};Session Settings
| Variable | Type | Default | Description |
|---|---|---|---|
MCP_SESSION_KEY_PREFIX | string | mcp:session: | Redis key prefix for session metadata |
MCP_SESSION_TTL_SECONDS | number | 86400 | Session TTL in seconds (24 hours) |
Example:
MCP_SESSION_KEY_PREFIX=mcp:session:
MCP_SESSION_TTL_SECONDS=86400 # 24 hours
# MCP_SESSION_TTL_SECONDS=43200 # 12 hours
# MCP_SESSION_TTL_SECONDS=3600 # 1 hourBehavior:
- Sessions automatically expire after TTL of inactivity
- TTL is refreshed on each session access (sliding window)
- Expired sessions are automatically cleaned up by Redis
- See Session Management for details
Rate Limiting Configuration
File: src/config/rate-limit.ts
export const rateLimitConfig = {
enabled: process.env.RATE_LIMIT_ENABLED !== "false",
mcp: {
windowMs: parseInt(process.env.MCP_RATE_LIMIT_WINDOW_MS ?? "60000", 10),
maxRequests: parseInt(process.env.MCP_RATE_LIMIT_MAX ?? "100", 10),
globalMax: parseInt(process.env.MCP_GLOBAL_RATE_LIMIT_MAX ?? "10000", 10),
},
dcr: {
windowMs: parseInt(process.env.DCR_RATE_LIMIT_WINDOW_MS ?? "3600000", 10),
maxRequests: parseInt(process.env.DCR_RATE_LIMIT_MAX ?? "10", 10),
globalMax: parseInt(process.env.DCR_GLOBAL_RATE_LIMIT_MAX ?? "1000", 10),
},
};Rate Limiting Settings
| Variable | Type | Default | Description |
|---|---|---|---|
RATE_LIMIT_ENABLED | boolean | true | Enable/disable rate limiting globally |
MCP_RATE_LIMIT_WINDOW_MS | number | 60000 | MCP rate limit window in milliseconds (1 minute) |
MCP_RATE_LIMIT_MAX | number | 100 | Max MCP requests per window per IP |
MCP_GLOBAL_RATE_LIMIT_MAX | number | 10000 | Global max MCP requests per window (all IPs) |
DCR_RATE_LIMIT_WINDOW_MS | number | 3600000 | DCR rate limit window in milliseconds (1 hour) |
DCR_RATE_LIMIT_MAX | number | 10 | Max DCR registrations per window per IP |
DCR_GLOBAL_RATE_LIMIT_MAX | number | 1000 | Global max DCR registrations per window (all IPs) |
Example:
# Global rate limiting toggle
RATE_LIMIT_ENABLED=true
# MCP endpoint rate limiting
MCP_RATE_LIMIT_WINDOW_MS=60000 # 1 minute
MCP_RATE_LIMIT_MAX=100 # 100 requests per IP
MCP_GLOBAL_RATE_LIMIT_MAX=10000 # 10,000 requests globally
# DCR endpoint rate limiting
DCR_RATE_LIMIT_WINDOW_MS=3600000 # 1 hour
DCR_RATE_LIMIT_MAX=10 # 10 registrations per IP
DCR_GLOBAL_RATE_LIMIT_MAX=1000 # 1,000 registrations globallyBehavior:
- Per-IP Limits: Prevents individual IPs from overwhelming the service
- Global Limits: Protects against distributed attacks across many IPs
- Sliding Window: Uses Redis-backed sliding window algorithm
- Graceful Degradation: If Redis fails, rate limiting is disabled
- Returns
429 Too Many Requestswhen limits exceeded
Security Configuration
File: src/config/security.ts
export const securityConfig = {
originValidation: {
enabled: process.env.ORIGIN_VALIDATION_ENABLED !== "false",
},
allowedOrigins: [
"http://localhost",
"http://localhost:*",
"http://127.0.0.1",
"http://127.0.0.1:*",
"https://localhost",
"https://localhost:*",
"https://127.0.0.1",
"https://127.0.0.1:*",
"https://claude.ai",
"*.anthropic.com",
...(process.env.ALLOWED_ORIGINS?.split(",").map((o) => o.trim()) ?? []),
],
};Security Settings
| Variable | Type | Default | Description |
|---|---|---|---|
ORIGIN_VALIDATION_ENABLED | boolean | true | Enable/disable origin validation |
ALLOWED_ORIGINS | string | "" | Comma-separated list of additional allowed origins |
Default Allowed Origins:
http://localhost:*andhttps://localhost:*(any port)http://127.0.0.1:*andhttps://127.0.0.1:*(any port)https://claude.ai- Claude web application*.anthropic.com- Any Anthropic subdomain
Example:
# Enable origin validation (default)
ORIGIN_VALIDATION_ENABLED=true
# Add custom allowed origins
ALLOWED_ORIGINS=https://app1.example.com,https://app2.example.comBehavior:
- Origin validation is separate from CORS (used for additional security checks)
- Wildcard patterns supported (
*.example.com) - Port wildcards supported (
http://localhost:*) - Used in conjunction with CORS configuration
Helmet Configuration
File: src/config/helmet.ts
Helmet provides security headers to protect against common web vulnerabilities.
export const helmetConfig: HelmetOptions = {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
frameAncestors: ["'none'"],
},
},
strictTransportSecurity: process.env.NODE_ENV === "production"
? {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true,
}
: false,
frameguard: { action: "deny" },
noSniff: true,
referrerPolicy: { policy: "strict-origin-when-cross-origin" },
dnsPrefetchControl: { allow: false },
ieNoOpen: true,
permittedCrossDomainPolicies: { permittedPolicies: "none" },
hidePoweredBy: true,
crossOriginEmbedderPolicy: false,
crossOriginOpenerPolicy: { policy: "same-origin" },
crossOriginResourcePolicy: { policy: "cross-origin" },
originAgentCluster: true,
};
// Relaxed configuration for API routes (no CSP)
export const helmetApiConfig: HelmetOptions = {
contentSecurityPolicy: false, // Disabled for JSON API responses
// ... other headers same as above
};Helmet Settings
Environment Detection:
- Uses
NODE_ENV === "production"to enable HSTS - No environment variables for configuration (all hardcoded for security)
Security Headers Provided:
- Content-Security-Policy: Prevents XSS attacks
- Strict-Transport-Security (HSTS): Forces HTTPS in production
- X-Frame-Options: Prevents clickjacking
- X-Content-Type-Options: Prevents MIME sniffing
- Referrer-Policy: Controls referrer information
- Cross-Origin-*-Policy: Controls cross-origin behavior
Two Configurations:
- helmetConfig: Full CSP for HTML/documentation routes
- helmetApiConfig: Relaxed CSP for JSON API routes
Example:
# Enable HSTS in production
NODE_ENV=production
# HSTS is automatically disabled in development
NODE_ENV=developmentLogging Configuration
File: src/config/logging.ts
export const loggingConfig = {
level: process.env.LOG_LEVEL ?? "info",
format: process.env.LOG_FORMAT ?? "json",
};Logging Settings
| Variable | Type | Default | Description |
|---|---|---|---|
LOG_LEVEL | string | info | Logging level: error, warn, info, debug |
LOG_FORMAT | string | json | Log output format: json or simple |
Example:
# Production: JSON logs with info level
LOG_LEVEL=info
LOG_FORMAT=json
# Development: Simple logs with debug level
LOG_LEVEL=debug
LOG_FORMAT=simple
# Troubleshooting: Debug level with JSON
LOG_LEVEL=debug
LOG_FORMAT=jsonLog Levels (from most to least verbose):
- error: Only errors
- warn: Errors and warnings
- info: Errors, warnings, and informational messages (default)
- debug: All messages including detailed debugging info
Log Formats:
- json: Structured JSON logs (recommended for production, log aggregation)
- simple: Human-readable colorized logs (good for development)
Metrics Configuration
File: src/config/metrics.ts
export const metricsConfig = {
enabled: process.env.METRICS_ENABLED !== "false",
} as const;Metrics Settings
| Variable | Type | Default | Description |
|---|---|---|---|
METRICS_ENABLED | boolean | true | Enable/disable Prometheus metrics collection |
Example:
# Enable metrics (default)
METRICS_ENABLED=true
# Disable metrics entirely
METRICS_ENABLED=falseMetrics Behavior
When metrics are enabled (METRICS_ENABLED=true or unset):
- Prometheus metrics are collected for all requests
/metricsendpoint is exposed (in Prometheus format)- Default Node.js metrics are collected (memory, CPU, event loop)
- Custom application metrics are tracked:
- HTTP request duration and counts
- MCP session metrics
- Authentication attempts
- JWKS refresh operations
- Redis operation metrics
- Rate limiting hits
When metrics are disabled (METRICS_ENABLED=false):
- No metrics are collected
/metricsendpoint is not registered- Metrics middleware is not applied
- Lower memory footprint and reduced overhead
Production Recommendation
Keep metrics enabled in production for observability. Secure the /metrics endpoint via:
- Network-level access control (firewall rules)
- Reverse proxy authentication (Traefik BasicAuth)
- Internal-only network access
Metrics Endpoint Security
The /metrics endpoint is publicly accessible when metrics are enabled. For production deployments, restrict access using one of these methods:
- IP Whitelisting (via Traefik or reverse proxy)
- Internal Network Only (Docker Swarm internal network)
- Disable Metrics (set
METRICS_ENABLED=false) - HTTP BasicAuth (via reverse proxy)
See Production Deployment Guide for detailed configuration examples.
Environment File Template
.env.example
# Server Configuration
PORT=3000
BASE_URL=https://seed.example.com
AUTH_REQUIRED=true
# OIDC Configuration (Required)
OIDC_ISSUER=https://auth.example.com/application/o/my-app/
OIDC_AUDIENCE=my-client-id
OAUTH_TOKEN_URL=https://auth.example.com/application/o/token/
OAUTH_AUTHORIZATION_URL=https://auth.example.com/application/o/authorize/
# OIDC Configuration (Optional)
OIDC_JWKS_URL=https://auth.example.com/application/o/my-app/jwks/
# Redis Configuration
REDIS_URL=redis://redis:6379
# Dynamic Client Registration
DCR_CLIENT_TTL=2592000 # 30 days
DCR_RATE_LIMIT_WINDOW_MS=3600000 # 1 hour
DCR_RATE_LIMIT_MAX=10
DCR_GLOBAL_RATE_LIMIT_MAX=1000 # Global max across all IPs
# MCP Session Management
MCP_SESSION_KEY_PREFIX=mcp:session:
MCP_SESSION_TTL_SECONDS=86400 # 24 hours
# Rate Limiting
RATE_LIMIT_ENABLED=true
MCP_RATE_LIMIT_WINDOW_MS=60000 # 1 minute
MCP_RATE_LIMIT_MAX=100 # Per IP
MCP_GLOBAL_RATE_LIMIT_MAX=10000 # Global max across all IPs
# Security Configuration
ORIGIN_VALIDATION_ENABLED=true
ALLOWED_ORIGINS=https://app1.example.com,https://app2.example.com
# CORS Configuration (Optional)
CORS_EXTRA_ORIGINS=https://app1.example.com,https://app2.example.com
# Logging Configuration
LOG_LEVEL=info # error, warn, info, debug
LOG_FORMAT=json # json or simple
# Metrics Configuration
METRICS_ENABLED=true
# Node Environment (affects HSTS)
NODE_ENV=production # production or developmentConfiguration Loading
Loading Order
- Environment variables: Loaded from process.env
- Default values: Used if environment variable not set
- Type conversion: Strings parsed to numbers/booleans as needed
- Validation: Runtime checks for required values
Type Conversion
String to Number:
parseInt(process.env.PORT ?? "3000", 10)String to Boolean:
process.env.AUTH_REQUIRED !== "false" // true unless explicitly "false"String to Array:
process.env.CORS_EXTRA_ORIGINS?.split(",") || []Configuration Validation
✅ IMPLEMENTED (2026-01-06) - Comprehensive startup validation of all configuration values.
Validation Process
Configuration validation happens at server startup in src/config/validate.ts:
- Required Fields Check: Validates required environment variables are set
- Format Validation: Validates URL formats, port ranges, numeric limits
- Production Security: Additional validation in production mode
- Exit on Failure: Server exits with detailed error message if validation fails
Required Variables
When AUTH_REQUIRED=true, the following must be set:
OIDC_ISSUERorOIDC_JWKS_URLOIDC_AUDIENCEOAUTH_TOKEN_URLOAUTH_AUTHORIZATION_URL
Validation Logic:
// src/config/validate.ts
if (config.authRequired && !config.oidc.issuer && !config.oidc.jwksUrl) {
throw new Error(
"OIDC_ISSUER or OIDC_JWKS_URL must be configured when AUTH_REQUIRED=true"
);
}URL Validation
OAuth endpoints and other URLs are validated for correct format:
// URL format validation
function validateUrl(url: string, fieldName: string): void {
try {
new URL(url);
} catch (error) {
throw new Error(`${fieldName} must be a valid URL: ${url}`);
}
}
// Validate OIDC URLs
validateUrl(config.oidc.issuer, "OIDC_ISSUER");
validateUrl(config.oidc.tokenUrl, "OAUTH_TOKEN_URL");
validateUrl(config.oidc.authorizationUrl, "OAUTH_AUTHORIZATION_URL");Port and Numeric Validation
Port numbers and TTL values are validated for reasonable ranges:
// Port range validation
if (config.port < 1 || config.port > 65535) {
throw new Error(`PORT must be between 1 and 65535, got: ${config.port}`);
}
// TTL validation
if (config.session.ttlSeconds < 60) {
throw new Error("MCP_SESSION_TTL_SECONDS must be at least 60 seconds");
}
if (config.dcr.clientTtlSeconds < 3600) {
throw new Error("DCR_CLIENT_TTL must be at least 3600 seconds (1 hour)");
}Production Security Validation
Additional validation in production mode:
if (process.env.NODE_ENV === "production") {
// Require HTTPS for BASE_URL
if (config.baseUrl && !config.baseUrl.startsWith("https://")) {
throw new Error("BASE_URL must use HTTPS in production");
}
// Require authentication
if (!config.authRequired) {
console.warn("WARNING: Authentication disabled in production");
}
// Validate Redis URL
if (!config.dcr.redisUrl.startsWith("redis://") &&
!config.dcr.redisUrl.startsWith("rediss://")) {
throw new Error("REDIS_URL must use redis:// or rediss:// scheme");
}
}Validation Output
Success:
✓ Configuration validated successfullyFailure:
✗ Configuration validation failed:
- OIDC_ISSUER must be configured when AUTH_REQUIRED=true
- PORT must be between 1 and 65535, got: 99999
- BASE_URL must use HTTPS in production
Server startup aborted. Please fix configuration errors.Tests
Configuration validation is fully tested with 42 test cases and 96.26% coverage:
- Basic validation (port, URLs, TTLs)
- Auth-required validation
- Production-specific validation
- Edge cases and boundary conditions
See src/config/validate.test.ts for complete test suite.
Configuration Best Practices
Development
Minimal configuration for local development:
# Disable authentication for testing
AUTH_REQUIRED=false
# Use default port
PORT=3000
# No OIDC required when auth disabledTesting
Configuration for automated tests:
# Disable auth for unit tests
AUTH_REQUIRED=false
# Use test Redis instance
REDIS_URL=redis://localhost:6379
# Short TTLs for faster test execution
DCR_CLIENT_TTL=60Production
Production-ready configuration:
# Always require authentication
AUTH_REQUIRED=true
# Set public base URL
BASE_URL=https://seed.example.com
# Use production IdP
OIDC_ISSUER=https://auth.example.com/application/o/my-app/
OIDC_AUDIENCE=production-client-id
OAUTH_TOKEN_URL=https://auth.example.com/application/o/token/
OAUTH_AUTHORIZATION_URL=https://auth.example.com/application/o/authorize/
# Use production Redis
REDIS_URL=redis://redis.prod:6379
# Session management
MCP_SESSION_TTL_SECONDS=86400
MCP_SESSION_KEY_PREFIX=mcp:session:
# Standard 30-day client TTL
DCR_CLIENT_TTL=2592000
# Rate limiting
RATE_LIMIT_ENABLED=true
MCP_RATE_LIMIT_WINDOW_MS=60000
MCP_RATE_LIMIT_MAX=100
MCP_GLOBAL_RATE_LIMIT_MAX=10000
DCR_RATE_LIMIT_WINDOW_MS=3600000
DCR_RATE_LIMIT_MAX=10
DCR_GLOBAL_RATE_LIMIT_MAX=1000
# Security
ORIGIN_VALIDATION_ENABLED=true
# Logging (JSON for production)
LOG_LEVEL=info
LOG_FORMAT=json
# Metrics enabled
METRICS_ENABLED=true
# Production environment (enables HSTS)
NODE_ENV=productionConfiguration Security
Sensitive Values
The following should be kept secret:
REDIS_URL(if it contains credentials)- IdP endpoints (may reveal internal infrastructure)
Not sensitive (can be public):
PORTBASE_URLOIDC_ISSUER(public OIDC metadata)OIDC_AUDIENCE(client ID, not a secret)
Environment Variable Storage
Recommended practices:
- Use
.envfiles for local development (add to.gitignore) - Use Docker secrets for containerized deployments
- Use Kubernetes ConfigMaps/Secrets for k8s
- Use cloud provider secret management (AWS Secrets Manager, etc.)
Default Value Security
All defaults are safe:
- No hardcoded credentials
- Localhost-only for development
- Authentication enabled by default
- Secure CORS origins
Docker Configuration
docker-compose.yml
version: '3.8'
services:
seed:
image: seed-mcp-server
environment:
- PORT=3000
- BASE_URL=https://seed.example.com
- AUTH_REQUIRED=true
- OIDC_ISSUER=${OIDC_ISSUER}
- OIDC_AUDIENCE=${OIDC_AUDIENCE}
- OAUTH_TOKEN_URL=${OAUTH_TOKEN_URL}
- OAUTH_AUTHORIZATION_URL=${OAUTH_AUTHORIZATION_URL}
- REDIS_URL=redis://redis:6379
ports:
- "3000:3000"
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"Environment File
# .env (for docker-compose)
OIDC_ISSUER=https://auth.example.com/application/o/my-app/
OIDC_AUDIENCE=my-client-id
OAUTH_TOKEN_URL=https://auth.example.com/application/o/token/
OAUTH_AUTHORIZATION_URL=https://auth.example.com/application/o/authorize/Configuration Access
In Application Code
import { config } from "./config/index.js";
// Access server settings
const port = config.port;
const baseUrl = config.baseUrl;
// Access OIDC settings
const issuer = config.oidc.issuer;
const audience = config.oidc.audience;
// Access DCR settings
const redisUrl = config.dcr.redisUrl;
const clientTtl = config.dcr.clientTtlSeconds;
// Access CORS settings
const allowedOrigins = config.cors.origin;
// Access session settings
const sessionTtl = config.session.ttlSeconds;
const sessionKeyPrefix = config.session.keyPrefix;
// Access rate limiting settings
const rateLimitEnabled = config.rateLimit.enabled;
const mcpMaxRequests = config.rateLimit.mcp.maxRequests;
// Access security settings
const originValidationEnabled = config.security.originValidation.enabled;
const allowedOrigins = config.security.allowedOrigins;
// Access helmet settings
const helmetConfig = config.helmet;
const helmetApiConfig = config.helmetApi;
// Access logging settings
const logLevel = config.logging.level;
const logFormat = config.logging.format;
// Access metrics settings
const metricsEnabled = config.metrics.enabled;TypeScript Types
interface Config {
port: number;
baseUrl: string;
authRequired: boolean;
server: {
name: string;
version: string;
};
cors: CorsConfig;
dcr: DcrConfig;
helmet: HelmetOptions;
helmetApi: HelmetOptions;
oidc: OidcConfig;
session: SessionConfig;
rateLimit: RateLimitConfig;
security: SecurityConfig;
logging: LoggingConfig;
metrics: MetricsConfig;
}Troubleshooting
Common Issues
Issue: "OIDC_ISSUER must be configured"
- Solution: Set
OIDC_ISSUERor disable auth withAUTH_REQUIRED=false
Issue: Redis connection refused
- Solution: Verify
REDIS_URLpoints to running Redis instance
Issue: CORS errors in browser
- Solution: Add origin to
CORS_EXTRA_ORIGINS
Issue: JWT validation fails
- Solution: Verify
OIDC_ISSUERandOIDC_AUDIENCEmatch IdP configuration
Issue: Rate limiting not working
- Solution: Verify
RATE_LIMIT_ENABLED=trueand Redis is accessible
Issue: Sessions expiring too quickly
- Solution: Increase
MCP_SESSION_TTL_SECONDS(default is 86400 = 24 hours)
Issue: HSTS warnings in development
- Solution: Set
NODE_ENV=developmentto disable HSTS
Issue: Logs not showing debug information
- Solution: Set
LOG_LEVEL=debugfor detailed logging
Implementation Files
- Main Config:
src/config/index.ts- Central configuration export - OIDC Config:
src/config/oidc.ts- OIDC/OAuth provider settings - CORS Config:
src/config/cors.ts- Cross-origin resource sharing - DCR Config:
src/config/dcr.ts- Dynamic client registration - Session Config:
src/config/session.ts- MCP session management - Rate Limit Config:
src/config/rate-limit.ts- Rate limiting for MCP and DCR - Security Config:
src/config/security.ts- Origin validation and security - Helmet Config:
src/config/helmet.ts- Security headers (CSP, HSTS, etc.) - Logging Config:
src/config/logging.ts- Log level and format - Metrics Config:
src/config/metrics.ts- Prometheus metrics toggle - Environment Template:
.env.example- Complete environment variable template
Related Documentation
- Authentication Flow - OIDC configuration usage
- OAuth 2.1 Implementation - OAuth endpoint configuration
- Session Management - Redis configuration
- MCP Server Design - Server metadata configuration