- name
- api-config
- description
- Reference for core and server configuration in `@cyanheads/mcp-ts-core`. Covers env var tables with defaults, priority order, server-specific Zod schema pattern, and Workers lazy-parsing requirement.
- metadata
- {"author":"cyanheads","version":"1.9","audience":"external","type":"reference"}
## Overview
Configuration has two layers: **core config** (managed by the framework, env-driven) and **server config** (your own Zod schema for domain-specific env vars). Never merge them.
Import: `AppConfig`, `config`, `parseConfig`, `resetConfig`, `ConfigSchema` from `@cyanheads/mcp-ts-core/config`.
---
## Core config
Managed by `@cyanheads/mcp-ts-core`. Validated via Zod from environment variables. Uses a lazy proxy — parsing is deferred until the first property read.
**Priority (highest to lowest):**
1. `name`/`version`/`title`/`websiteUrl`/`description`/`icons` options passed to `createApp()` or `createWorkerHandler()`
2. Environment variables
3. `package.json` fields
---
### Identity
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `MCP_SERVER_NAME` | `mcpServerName` | `package.json` `name` | Overrides package name |
| `MCP_SERVER_VERSION` | `mcpServerVersion` | `package.json` `version` | Overrides package version |
| `MCP_SERVER_DESCRIPTION` | `mcpServerDescription` | `package.json` `description` | Optional; `createApp({ description })` wins when set |
| `PACKAGE_NAME` | `pkg.name` | `package.json` `name` | Rarely needed |
| `PACKAGE_VERSION` | `pkg.version` | `package.json` `version` | Rarely needed |
**SDK identity fields** (API-only, no env var equivalent — passed to `createApp()` / `createWorkerHandler()`, forwarded to `initialize` and `/.well-known/mcp.json`):
| Option | Type | Notes |
|:-------|:-----|:------|
| `title` | `string?` | Human-readable display name shown in client listings |
| `websiteUrl` | `string?` | Canonical homepage / repository URL |
| `description` | `string?` | One-line description; wins over `MCP_SERVER_DESCRIPTION` when set |
| `icons` | `Implementation['icons']?` | Array of icon objects: `{ src, mimeType?, sizes?: string[], theme?: 'light'\|'dark' }` |
| `NODE_ENV` | `environment` | `development` | Aliases: `dev`→`development`, `prod`→`production`, `test`→`testing` |
| `MCP_LOG_LEVEL` | `logLevel` | `debug` | Aliases: `warn`→`warning`, `err`→`error`, `fatal`/`silent`→`emerg`, `trace`→`debug`, `information`→`info` |
| `LOGS_DIR` | `logsPath` | `<project-root>/logs` | Node.js only; absolute or relative to project root |
---
### Transport
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `MCP_TRANSPORT_TYPE` | `mcpTransportType` | `stdio` | `stdio` \| `http` |
| `MCP_HTTP_PORT` | `mcpHttpPort` | `3010` | Port for HTTP transport |
| `MCP_HTTP_HOST` | `mcpHttpHost` | `127.0.0.1` | Bind address |
| `MCP_HTTP_ENDPOINT_PATH` | `mcpHttpEndpointPath` | `/mcp` | HTTP endpoint path |
| `MCP_HTTP_MAX_BODY_BYTES` | `mcpHttpMaxBodyBytes` | `1048576` (1 MiB) | Max **inbound** JSON-RPC request body; oversized requests get `413` before per-request allocation. Does **not** cap upstream data staged into a canvas or response sizes. `0` disables (defer to runtime/proxy). |
| `MCP_HTTP_MAX_PORT_RETRIES` | `mcpHttpMaxPortRetries` | `15` | Retry count if port is busy |
| `MCP_HTTP_PORT_RETRY_DELAY_MS` | `mcpHttpPortRetryDelayMs` | `50` | Delay between port retries (ms) |
| `MCP_SESSION_MODE` | `mcpSessionMode` | `auto` | `stateless` \| `stateful` \| `auto` |
| `MCP_STATEFUL_SESSION_STALE_TIMEOUT_MS` | `mcpStatefulSessionStaleTimeoutMs` | `1800000` | 30 min; stale session eviction |
| `MCP_RESPONSE_VERBOSITY` | `mcpResponseVerbosity` | `standard` | `minimal` \| `standard` \| `full` |
| `MCP_ALLOWED_ORIGINS` | `mcpAllowedOrigins` | — | Comma-separated list; omit to allow all |
| `MCP_SERVER_RESOURCE_IDENTIFIER` | `mcpServerResourceIdentifier` | — | RFC 8707 resource indicator URL |
| `MCP_PUBLIC_URL` | `mcpPublicUrl` | — | Public-facing origin for reverse proxies (Cloudflare Tunnel, nginx, ALB) so emitted URLs carry the correct scheme |
| `MCP_HEARTBEAT_INTERVAL_MS` | `mcpHeartbeatIntervalMs` | `0` (disabled) | Heartbeat ping interval; 0 disables |
| `MCP_HEARTBEAT_MISS_THRESHOLD` | `mcpHeartbeatMissThreshold` | `3` | Missed heartbeats before session is considered stale |
| `MCP_GC_PRESSURE_INTERVAL_MS` | `mcpGcPressureIntervalMs` | `0` (disabled) | Bun-only opt-in forced GC loop for HTTP deployments with heap growth |
---
### Auth
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `MCP_AUTH_MODE` | `mcpAuthMode` | `none` | `none` \| `jwt` \| `oauth` |
| `MCP_AUTH_SECRET_KEY` | `mcpAuthSecretKey` | — | Required for `jwt` mode; min 32 chars |
| `MCP_AUTH_DISABLE_SCOPE_CHECKS` | `mcpAuthDisableScopeChecks` | `false` | When `true`, bypasses both `withRequiredScopes` (declared `auth: [...]`) and `checkScopes` (runtime/tenant scopes). Token validation (sig/aud/iss/exp) intact. Logs a `WARNING` at startup. See `api-auth` skill. |
| `OAUTH_ISSUER_URL` | `oauthIssuerUrl` | — | Required for `oauth` mode |
| `OAUTH_AUDIENCE` | `oauthAudience` | — | Required for `oauth` mode |
| `OAUTH_JWKS_URI` | `oauthJwksUri` | — | Override JWKS endpoint (otherwise derived from issuer) |
| `OAUTH_JWKS_COOLDOWN_MS` | `oauthJwksCooldownMs` | `300000` | 5 min; min time between JWKS refetches |
| `OAUTH_JWKS_TIMEOUT_MS` | `oauthJwksTimeoutMs` | `5000` | JWKS fetch timeout (ms) |
| `DEV_MCP_AUTH_BYPASS` | `devMcpAuthBypass` | `false` | Skip auth in development; blocked in `production` |
| `MCP_JWT_EXPECTED_ISSUER` | `mcpJwtExpectedIssuer` | — | Optional issuer validation for JWT mode |
| `MCP_JWT_EXPECTED_AUDIENCE` | `mcpJwtExpectedAudience` | — | Optional audience validation for JWT mode |
| `DEV_MCP_CLIENT_ID` | `devMcpClientId` | — | Dev-only: override client ID |
| `DEV_MCP_SCOPES` | `devMcpScopes` | — | Dev-only: comma-separated scope overrides |
#### OAuth proxy (optional sub-object)
Activated when `OAUTH_PROXY_AUTHORIZATION_URL` or `OAUTH_PROXY_TOKEN_URL` is set.
| Env Var | `AppConfig` field | Notes |
|:--------|:-----------------|:------|
| `OAUTH_PROXY_AUTHORIZATION_URL` | `oauthProxy.authorizationUrl` | Proxy authorization endpoint |
| `OAUTH_PROXY_TOKEN_URL` | `oauthProxy.tokenUrl` | Proxy token endpoint |
| `OAUTH_PROXY_REVOCATION_URL` | `oauthProxy.revocationUrl` | Optional |
| `OAUTH_PROXY_ISSUER_URL` | `oauthProxy.issuerUrl` | Optional |
| `OAUTH_PROXY_SERVICE_DOCUMENTATION_URL` | `oauthProxy.serviceDocumentationUrl` | Optional |
| `OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS` | `oauthProxy.defaultClientRedirectUris` | Comma-separated list |
---
### Storage
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `STORAGE_PROVIDER_TYPE` | `storage.providerType` | `in-memory` | `in-memory` \| `filesystem` \| `supabase` \| `cloudflare-r2` \| `cloudflare-kv` \| `cloudflare-d1`; aliases: `mem`, `fs` |
| `STORAGE_FILESYSTEM_PATH` | `storage.filesystemPath` | `./.storage` | Used only when `providerType` is `filesystem` |
---
### Canvas (DataCanvas primitive — Tier 3, optional peer dep `@duckdb/node-api`)
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `CANVAS_PROVIDER_TYPE` | `canvas.providerType` | `none` | `none` \| `duckdb`. Set to `duckdb` to enable `core.canvas`. Fails closed on Cloudflare Workers (DuckDB has no V8-isolate build). |
| `CANVAS_DEFAULT_MEMORY_LIMIT_MB` | `canvas.defaultMemoryLimitMb` | `1024` | Per-canvas DuckDB `memory_limit` PRAGMA value, in MB. |
| `CANVAS_EXPORT_PATH` | `canvas.exportRootPath` | `./.canvas-exports` | Sandbox root for path-targeted exports. Absolute paths and `..` traversal are rejected. |
| `CANVAS_TEMP_PATH` | `canvas.tempRootPath` | `<os.tmpdir()>/mcp-canvas` | Scratch root: DuckDB's `temp_directory` for queries that spill past `memory_limit`, plus the transient files behind stream exports and the spillover round-trip. Never resolves to the process cwd — DuckDB's own cwd-relative `.tmp` default fails on a non-root or read-only container rootfs. |
| `CANVAS_MAX_CANVASES_PER_TENANT` | `canvas.maxCanvasesPerTenant` | `100` | Active canvas cap per tenant; throws `RateLimited` when exceeded. |
| `CANVAS_TTL_MS` | `canvas.ttlMs` | `86400000` | Sliding TTL (24 h). Every operation extends the expiry. |
| `CANVAS_ABSOLUTE_CAP_MS` | `canvas.absoluteCapMs` | `604800000` | Absolute cap from creation (7 d). Sliding window clamps to this. |
| `CANVAS_SWEEPER_INTERVAL_MS` | `canvas.sweeperIntervalMs` | `60000` | Background sweep interval. Set to `0` to disable. |
| `CANVAS_DEFAULT_ROW_LIMIT` | `canvas.defaultRowLimit` | `10000` | Default cap on rows materialized into a query response. |
| `CANVAS_SCHEMA_SNIFF_ROWS` | `canvas.schemaSniffRows` | `100` | Rows to materialize for schema inference when `schema` is omitted. |
**Platform support:** Linux/macOS/Windows × x64 supported, Linux/macOS arm64 supported. Windows arm64 unsupported (DuckDB upstream). See `api-canvas` skill for the full DataCanvas reference.
#### Supabase (optional sub-object)
Activated when both `SUPABASE_URL` and `SUPABASE_ANON_KEY` are set.
| Env Var | `AppConfig` field | Notes |
|:--------|:-----------------|:------|
| `SUPABASE_URL` | `supabase.url` | Required to activate |
| `SUPABASE_ANON_KEY` | `supabase.anonKey` | Required to activate |
| `SUPABASE_SERVICE_ROLE_KEY` | `supabase.serviceRoleKey` | Optional; elevated access |
---
### LLM
| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `OPENROUTER_API_KEY` | `openrouterApiKey` | — | Optional; enables LLM provider |
| `OPENROUTER_APP_URL` | `openrouterAppUrl` | `http://localhost:3000` | Reported to OpenRouter |
عرض على GitHub