mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-05 22:22:06 +00:00
78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
|
|
/**
|
||
|
|
* 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;
|
||
|
|
}
|