api-config

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.

8 stars

Best use case

api-config is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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.

Teams using api-config should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/api-config/SKILL.md --create-dirs "https://raw.githubusercontent.com/cyanheads/pubchem-mcp-server/main/skills/api-config/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/api-config/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How api-config Compares

Feature / Agentapi-configStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

## 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` overrides 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 |
| `PACKAGE_NAME` | `pkg.name` | `package.json` `name` | Rarely needed |
| `PACKAGE_VERSION` | `pkg.version` | `package.json` `version` | Rarely needed |
| `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_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 |
| `OPENROUTER_APP_NAME` | `openrouterAppName` | `package.json` `name` | Reported to OpenRouter |
| `LLM_DEFAULT_MODEL` | `llmDefaultModel` | `google/gemini-2.5-flash-preview-05-20` | OpenRouter model ID |
| `LLM_DEFAULT_TEMPERATURE` | `llmDefaultTemperature` | — | Float |
| `LLM_DEFAULT_TOP_P` | `llmDefaultTopP` | — | Float |
| `LLM_DEFAULT_MAX_TOKENS` | `llmDefaultMaxTokens` | — | Integer |
| `LLM_DEFAULT_TOP_K` | `llmDefaultTopK` | — | Integer |
| `LLM_DEFAULT_MIN_P` | `llmDefaultMinP` | — | Float |

---

### Telemetry

| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `OTEL_ENABLED` | `openTelemetry.enabled` | `false` | Enable OpenTelemetry export |
| `OTEL_SERVICE_NAME` | `openTelemetry.serviceName` | `package.json` `name` | |
| `OTEL_SERVICE_VERSION` | `openTelemetry.serviceVersion` | `package.json` `version` | |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `openTelemetry.tracesEndpoint` | — | OTLP traces endpoint URL |
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | `openTelemetry.metricsEndpoint` | — | OTLP metrics endpoint URL |
| `OTEL_TRACES_SAMPLER_ARG` | `openTelemetry.samplingRatio` | `1.0` | 0–1; fraction of traces to export |
| `OTEL_LOG_LEVEL` | `openTelemetry.logLevel` | `INFO` | OTel SDK internal log level: `NONE` \| `ERROR` \| `WARN` \| `INFO` \| `DEBUG` \| `VERBOSE` \| `ALL` |

---

### Tasks

| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `TASK_STORE_TYPE` | `tasks.storeType` | `in-memory` | `in-memory` \| `storage`; aliases: `mem`/`memory`→`in-memory`, `persistent`→`storage` |
| `TASK_STORE_TENANT_ID` | `tasks.tenantId` | `system-tasks` | Tenant ID for task state storage |
| `TASK_STORE_DEFAULT_TTL_MS` | `tasks.defaultTtlMs` | — | TTL for completed tasks (ms); null = no expiry |

---

### Speech (optional sub-object)

Activated when `SPEECH_TTS_ENABLED` or `SPEECH_STT_ENABLED` is set.

#### TTS (Text-to-Speech)

| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `SPEECH_TTS_ENABLED` | `speech.tts.enabled` | `false` | Enable TTS |
| `SPEECH_TTS_PROVIDER` | `speech.tts.provider` | `elevenlabs` | Currently only `elevenlabs` |
| `SPEECH_TTS_API_KEY` | `speech.tts.apiKey` | — | Provider API key |
| `SPEECH_TTS_BASE_URL` | `speech.tts.baseUrl` | — | Override provider base URL |
| `SPEECH_TTS_DEFAULT_VOICE_ID` | `speech.tts.defaultVoiceId` | — | Default voice identifier |
| `SPEECH_TTS_DEFAULT_MODEL_ID` | `speech.tts.defaultModelId` | — | Default model identifier |
| `SPEECH_TTS_TIMEOUT` | `speech.tts.timeout` | — | Request timeout (ms) |

#### STT (Speech-to-Text)

| Env Var | `AppConfig` field | Default | Notes |
|:--------|:-----------------|:--------|:------|
| `SPEECH_STT_ENABLED` | `speech.stt.enabled` | `false` | Enable STT |
| `SPEECH_STT_PROVIDER` | `speech.stt.provider` | `openai-whisper` | Currently only `openai-whisper` |
| `SPEECH_STT_API_KEY` | `speech.stt.apiKey` | — | Provider API key |
| `SPEECH_STT_BASE_URL` | `speech.stt.baseUrl` | — | Override provider base URL |
| `SPEECH_STT_DEFAULT_MODEL_ID` | `speech.stt.defaultModelId` | — | Default model identifier |
| `SPEECH_STT_TIMEOUT` | `speech.stt.timeout` | — | Request timeout (ms) |

---

## Server config (separate schema)

Define your own Zod schema for domain-specific env vars. **Never merge with core's schema.**

Use the lazy init/accessor pattern — do not parse `process.env` at module top-level.

```ts
// src/config/server-config.ts
import { z } from '@cyanheads/mcp-ts-core';
import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';

const ServerConfigSchema = z.object({
  apiKey: z.string().describe('External API key'),
  maxResults: z.coerce.number().default(100),
});

export type ServerConfig = z.infer<typeof ServerConfigSchema>;

let _config: ServerConfig | undefined;

export function getServerConfig(): ServerConfig {
  _config ??= parseEnvConfig(ServerConfigSchema, {
    apiKey: 'MY_API_KEY',
    maxResults: 'MY_MAX_RESULTS',
  });
  return _config;
}
```

**Why `parseEnvConfig`?** It maps Zod schema paths to env var names so validation errors name the actual variable at fault. A missing `MY_API_KEY` produces:

```
Server config validation failed:
  - MY_API_KEY (apiKey): Invalid input: expected string, received undefined
```

Instead of a raw `ZodError` dump at startup. The framework catches the resulting `ConfigurationError` and prints a clean banner (full stack behind `DEBUG=true`).

Direct `ServerConfigSchema.parse(...)` still works — the framework intercepts raw `ZodError` thrown from `setup()` and converts it — but error messages won't know about env var names, so they show the Zod path (`apiKey`) instead of the variable name (`MY_API_KEY`).

**Workers:** Do not parse `process.env` at module top-level. In Workers, env bindings are injected at request time via `injectEnvVars()`, after all static imports. Lazy parsing is required.

Related Skills

tool-defs-analysis

8
from cyanheads/pubchem-mcp-server

Read-only audit of MCP definition language across an existing surface — tools, resources, prompts. Walks every definition file and checks 12 categories the LLM reads to decide whether and how to call: voice & tense, internal leaks, audience leaks, defaults, recovery hints, output descriptions, cross-references, sparsity, examples, structure, mutator observability, unit-bearing numeric names. Produces grouped findings with file:line citations and a numbered options list. Use during polish, after a refactor, or before a release. Complements `field-test` (behavior testing) and `security-pass` (security audit).

setup

8
from cyanheads/pubchem-mcp-server

Post-init orientation for an MCP server built on @cyanheads/mcp-ts-core. Use after running `@cyanheads/mcp-ts-core init` to understand the project structure, conventions, and skill sync model. Also use when onboarding to an existing project for the first time.

security-pass

8
from cyanheads/pubchem-mcp-server

Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell / sampling / schema strictness / ReDoS), tenant isolation, leakage through errors and telemetry, unbounded resources, and HTTP-mode deployment surface. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.

report-issue-local

8
from cyanheads/pubchem-mcp-server

File a bug or feature request against this MCP server's own repo. Use for server-specific issues — tool logic, service integrations, config problems, or domain bugs that aren't caused by the framework.

report-issue-framework

8
from cyanheads/pubchem-mcp-server

File a bug or feature request against @cyanheads/mcp-ts-core when you hit a framework issue. Use when a builder, utility, context method, or config behaves contrary to the documented API — not for server-specific application bugs.

release-and-publish

8
from cyanheads/pubchem-mcp-server

Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.

polish-docs-meta

8
from cyanheads/pubchem-mcp-server

Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.

orchestrations

8
from cyanheads/pubchem-mcp-server

Pick and run a multi-phase workflow that chains foundational task skills (`git-wrapup`, `release-and-publish`, `maintenance`, `field-test`, `setup`, etc.) end-to-end. Routes user intent to a workflow file under `workflows/` — greenfield builds, maintenance + release, field-test + fix, or known-work + release. Single source for the universal rules (no commits without authorization, no destructive git, no marketing language), the orchestrator posture (own the goal, ground sub-agents in primary sources, verify against the goal), and the sub-agent strategy (orient block, parallel fanout, isolation, normalization) that apply across every workflow. Sub-agents are an optional capability — workflows run linearly when fanout isn't available.

maintenance

8
from cyanheads/pubchem-mcp-server

Investigate, adopt, and verify dependency updates — with special handling for `@cyanheads/mcp-ts-core`. Captures what changed, understands why, cross-references against the codebase, adopts framework improvements, syncs project skills, and runs final checks. Supports two entry modes: run the full flow end-to-end, or review updates you already applied.

git-wrapup

8
from cyanheads/pubchem-mcp-server

Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.

field-test

8
from cyanheads/pubchem-mcp-server

Exercise tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the catalog, runs real and adversarial inputs, and produces a tight report with concrete findings and numbered follow-up options. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface.

devcheck

8
from cyanheads/pubchem-mcp-server

Lint, format, typecheck, and verify the project is clean. Use after making changes, before committing, or when the user asks to verify quality.