/** * sh-mcp-ops configuration — read entirely from the environment. * * Nothing is hardcoded (build-plan §7, design.md §2): client ids, issuer, JWKS * URL, table names, scope prefix all arrive via env. `SH_MCP_ENV` selects local * (dev clients + LocalAuthProvider) vs aws (real stub clients + Cognito). */ import { cognitoIssuer, cognitoJwks, type CognitoAuthConfig } from '@sh-mcp/shared'; export type Env = 'local' | 'aws'; export interface OpsConfig { env: Env; port: number; audience: string; /** Cognito config — only required/used in `aws` mode. */ cognito?: CognitoAuthConfig; // Real-client settings (aws mode only; unused locally). googleMapsApiKey?: string; reminderTargetArn?: string; schedulerRoleArn?: string; } const AUDIENCE = 'sh-mcp-ops'; const SCOPE_PREFIX = 'sh-mcp-ops'; function readEnv(name: string): string | undefined { const v = process.env[name]; return v !== undefined && v.length > 0 ? v : undefined; } /** Parse and validate the process environment into an {@link OpsConfig}. */ export function loadOpsConfig(): OpsConfig { // Fail CLOSED: SH_MCP_ENV must be set explicitly. An unset value must NEVER // silently select local mode (which wires LocalAuthProvider + static dev // bearer tokens). A misconfigured deploy should refuse to start, not run dev // auth on a finance/ops service. const rawEnv = readEnv('SH_MCP_ENV'); if (rawEnv !== 'local' && rawEnv !== 'aws') { throw new Error( `SH_MCP_ENV must be explicitly set to "local" or "aws" ` + `(got ${rawEnv === undefined ? 'unset' : `"${rawEnv}"`}); refusing to start.`, ); } const env = rawEnv; const port = Number(readEnv('PORT') ?? '8081'); const base: OpsConfig = { env, port, audience: AUDIENCE }; if (env === 'aws') { const region = readEnv('AWS_REGION') ?? 'us-east-1'; const userPoolId = required('COGNITO_USER_POOL_ID'); const allowedClientIds = required('COGNITO_ALLOWED_CLIENT_IDS') .split(',') .map((s) => s.trim()) .filter(Boolean); base.cognito = { issuer: cognitoIssuer(region, userPoolId), audience: AUDIENCE, allowedClientIds, scopePrefix: SCOPE_PREFIX, jwks: cognitoJwks(region, userPoolId), }; base.googleMapsApiKey = readEnv('GOOGLE_MAPS_API_KEY'); base.reminderTargetArn = readEnv('REMINDER_TARGET_ARN'); base.schedulerRoleArn = readEnv('SCHEDULER_ROLE_ARN'); } return base; } function required(name: string): string { const v = readEnv(name); if (!v) throw new Error(`Missing required env var ${name} for SH_MCP_ENV=aws.`); return v; }