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.

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

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,
  oidc: oidcConfig,
};

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,
  rateLimit: {
    windowMs: parseInt(process.env.DCR_RATE_LIMIT_WINDOW_MS ?? "3600000", 10),
    maxRequests: parseInt(process.env.DCR_RATE_LIMIT_MAX ?? "10", 10),
  },
};

DCR Settings

VariableTypeDefaultDescription
REDIS_URLstringredis://redis:6379Redis connection URL
DCR_CLIENT_TTLnumber2592000Client TTL in seconds (30 days)
DCR_RATE_LIMIT_WINDOW_MSnumber3600000Rate limit window in milliseconds (1 hour)
DCR_RATE_LIMIT_MAXnumber10Max registrations per window

Example:

bash
REDIS_URL=redis://redis.example.com:6379
DCR_CLIENT_TTL=604800                # 7 days
DCR_RATE_LIMIT_WINDOW_MS=1800000     # 30 minutes
DCR_RATE_LIMIT_MAX=5                 # 5 registrations per window

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

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

# CORS Configuration (Optional)
CORS_EXTRA_ORIGINS=https://app1.example.com,https://app2.example.com

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

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
// In src/services/jwks.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 must be valid URLs:

typescript
try {
  new URL(config.oidc.tokenUrl);
  new URL(config.oidc.authorizationUrl);
} catch (error) {
  throw new Error("Invalid OAuth endpoint URL");
}

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

# Standard 30-day client TTL
DCR_CLIENT_TTL=2592000

# Rate limiting
DCR_RATE_LIMIT_WINDOW_MS=3600000
DCR_RATE_LIMIT_MAX=10

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;

TypeScript Types

typescript
interface Config {
  port: number;
  baseUrl: string;
  authRequired: boolean;
  server: {
    name: string;
    version: string;
  };
  cors: CorsConfig;
  dcr: DcrConfig;
  oidc: OidcConfig;
}

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

Implementation Files

  • Main Config: src/config/index.ts
  • OIDC Config: src/config/oidc.ts
  • CORS Config: src/config/cors.ts
  • DCR Config: src/config/dcr.ts
  • Environment Template: .env.example

Released under the MIT License.