Skip to content

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 settings

Main Configuration

File: src/config/index.ts

typescript
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

VariableTypeDefaultDescription
PORTnumber3000HTTP server port
BASE_URLstring""Public base URL for OAuth redirects
AUTH_REQUIREDbooleantrueEnable/disable authentication globally

Example:

bash
PORT=8080
BASE_URL=https://seed.example.com
AUTH_REQUIRED=true

OIDC Configuration

File: src/config/oidc.ts

typescript
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

VariableRequiredDescription
OIDC_ISSUERYesIdentity provider issuer URL (e.g., https://auth.example.com/application/o/my-app/)
OIDC_AUDIENCEYesClient ID for JWT audience validation (also used as static IdP client)
OAUTH_TOKEN_URLYesOAuth token endpoint URL
OAUTH_AUTHORIZATION_URLYesOAuth authorization endpoint URL
OIDC_JWKS_URLNoJWKS endpoint URL (auto-discovered from issuer if not provided)

Example:

bash
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_ISSUER

JWKS Settings

Hardcoded Configuration:

typescript
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

typescript
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

VariableTypeDefaultDescription
CORS_EXTRA_ORIGINSstring""Comma-separated list of additional allowed origins

Default Allowed Origins:

  1. http://localhost:* (any port) - For local development
  2. https://claude.ai - Claude web application
  3. https://*.anthropic.com - Any Anthropic subdomain

Example:

bash
CORS_EXTRA_ORIGINS=https://app1.example.com,https://app2.example.com

CORS Headers

Allowed Request Headers:

  • Content-Type: For JSON requests
  • Accept: Content negotiation
  • Authorization: Bearer tokens
  • Mcp-Session-Id: MCP session tracking
  • Last-Event-ID: Server-sent events (future use)

Exposed Response Headers:

  • Content-Type: Response format
  • Mcp-Session-Id: Session identifier

Dynamic Client Registration Configuration

File: src/config/dcr.ts

typescript
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

VariableTypeDefaultDescription
REDIS_URLstringredis://redis:6379Redis connection URL
DCR_CLIENT_TTLnumber2592000Client TTL in seconds (30 days)

Example:

bash
REDIS_URL=redis://redis.example.com:6379
DCR_CLIENT_TTL=604800                # 7 days

Note: 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

typescript
export const sessionConfig = {
  keyPrefix: process.env.MCP_SESSION_KEY_PREFIX ?? "mcp:session:",
  ttlSeconds: parseInt(process.env.MCP_SESSION_TTL_SECONDS ?? "86400", 10),
};

Session Settings

VariableTypeDefaultDescription
MCP_SESSION_KEY_PREFIXstringmcp:session:Redis key prefix for session metadata
MCP_SESSION_TTL_SECONDSnumber86400Session TTL in seconds (24 hours)

Example:

bash
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 hour

Behavior:

  • 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

typescript
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

VariableTypeDefaultDescription
RATE_LIMIT_ENABLEDbooleantrueEnable/disable rate limiting globally
MCP_RATE_LIMIT_WINDOW_MSnumber60000MCP rate limit window in milliseconds (1 minute)
MCP_RATE_LIMIT_MAXnumber100Max MCP requests per window per IP
MCP_GLOBAL_RATE_LIMIT_MAXnumber10000Global max MCP requests per window (all IPs)
DCR_RATE_LIMIT_WINDOW_MSnumber3600000DCR rate limit window in milliseconds (1 hour)
DCR_RATE_LIMIT_MAXnumber10Max DCR registrations per window per IP
DCR_GLOBAL_RATE_LIMIT_MAXnumber1000Global max DCR registrations per window (all IPs)

Example:

bash
# 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 globally

Behavior:

  • 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 Requests when limits exceeded

Security Configuration

File: src/config/security.ts

typescript
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

VariableTypeDefaultDescription
ORIGIN_VALIDATION_ENABLEDbooleantrueEnable/disable origin validation
ALLOWED_ORIGINSstring""Comma-separated list of additional allowed origins

Default Allowed Origins:

  1. http://localhost:* and https://localhost:* (any port)
  2. http://127.0.0.1:* and https://127.0.0.1:* (any port)
  3. https://claude.ai - Claude web application
  4. *.anthropic.com - Any Anthropic subdomain

Example:

bash
# Enable origin validation (default)
ORIGIN_VALIDATION_ENABLED=true

# Add custom allowed origins
ALLOWED_ORIGINS=https://app1.example.com,https://app2.example.com

Behavior:

  • 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.

typescript
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:

  1. helmetConfig: Full CSP for HTML/documentation routes
  2. helmetApiConfig: Relaxed CSP for JSON API routes

Example:

bash
# Enable HSTS in production
NODE_ENV=production

# HSTS is automatically disabled in development
NODE_ENV=development

Logging Configuration

File: src/config/logging.ts

typescript
export const loggingConfig = {
  level: process.env.LOG_LEVEL ?? "info",
  format: process.env.LOG_FORMAT ?? "json",
};

Logging Settings

VariableTypeDefaultDescription
LOG_LEVELstringinfoLogging level: error, warn, info, debug
LOG_FORMATstringjsonLog output format: json or simple

Example:

bash
# 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=json

Log 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

typescript
export const metricsConfig = {
  enabled: process.env.METRICS_ENABLED !== "false",
} as const;

Metrics Settings

VariableTypeDefaultDescription
METRICS_ENABLEDbooleantrueEnable/disable Prometheus metrics collection

Example:

bash
# Enable metrics (default)
METRICS_ENABLED=true

# Disable metrics entirely
METRICS_ENABLED=false

Metrics Behavior

When metrics are enabled (METRICS_ENABLED=true or unset):

  • Prometheus metrics are collected for all requests
  • /metrics endpoint 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
  • /metrics endpoint 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:

  1. IP Whitelisting (via Traefik or reverse proxy)
  2. Internal Network Only (Docker Swarm internal network)
  3. Disable Metrics (set METRICS_ENABLED=false)
  4. HTTP BasicAuth (via reverse proxy)

See Production Deployment Guide for detailed configuration examples.

Environment File Template

.env.example

bash
# 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 development

Configuration Loading

Loading Order

  1. Environment variables: Loaded from process.env
  2. Default values: Used if environment variable not set
  3. Type conversion: Strings parsed to numbers/booleans as needed
  4. Validation: Runtime checks for required values

Type Conversion

String to Number:

typescript
parseInt(process.env.PORT ?? "3000", 10)

String to Boolean:

typescript
process.env.AUTH_REQUIRED !== "false"  // true unless explicitly "false"

String to Array:

typescript
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:

  1. Required Fields Check: Validates required environment variables are set
  2. Format Validation: Validates URL formats, port ranges, numeric limits
  3. Production Security: Additional validation in production mode
  4. Exit on Failure: Server exits with detailed error message if validation fails

Required Variables

When AUTH_REQUIRED=true, the following must be set:

  • OIDC_ISSUER or OIDC_JWKS_URL
  • OIDC_AUDIENCE
  • OAUTH_TOKEN_URL
  • OAUTH_AUTHORIZATION_URL

Validation Logic:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
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 successfully

Failure:

✗ 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:

bash
# Disable authentication for testing
AUTH_REQUIRED=false

# Use default port
PORT=3000

# No OIDC required when auth disabled

Testing

Configuration for automated tests:

bash
# 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=60

Production

Production-ready configuration:

bash
# 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=production

Configuration 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):

  • PORT
  • BASE_URL
  • OIDC_ISSUER (public OIDC metadata)
  • OIDC_AUDIENCE (client ID, not a secret)

Environment Variable Storage

Recommended practices:

  1. Use .env files for local development (add to .gitignore)
  2. Use Docker secrets for containerized deployments
  3. Use Kubernetes ConfigMaps/Secrets for k8s
  4. 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

yaml
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

bash
# .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

typescript
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

typescript
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_ISSUER or disable auth with AUTH_REQUIRED=false

Issue: Redis connection refused

  • Solution: Verify REDIS_URL points to running Redis instance

Issue: CORS errors in browser

  • Solution: Add origin to CORS_EXTRA_ORIGINS

Issue: JWT validation fails

  • Solution: Verify OIDC_ISSUER and OIDC_AUDIENCE match IdP configuration

Issue: Rate limiting not working

  • Solution: Verify RATE_LIMIT_ENABLED=true and 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=development to disable HSTS

Issue: Logs not showing debug information

  • Solution: Set LOG_LEVEL=debug for 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

Released under the MIT License.