Skip to main content
FrontMCP uses a typed configuration file to define project settings, deployment targets, server configuration, and security policies. The file supports TypeScript, JavaScript, and JSON formats with full IDE autocomplete.

Quick Start

Create frontmcp.config.ts in your project root:
import { defineConfig } from 'frontmcp';

export default defineConfig({
  name: 'my-server',
  deployments: [{ target: 'node' }],
});
The defineConfig() helper is a pass-through that enables IDE type hints and autocomplete.

File Resolution Order

FrontMCP locates the config file in this precedence order (issue #400):
  1. Explicit --config <path> flag on any command.
  2. FRONTMCP_CONFIG env var.
  3. Upward walk from cwd to the nearest ancestor containing a frontmcp.config.* file (caps at 10 levels — monorepo nested apps no longer require cd <repo-root>).
  4. Fallback: derives minimal config from package.json (name, default node target).
Within a directory, the matching extensions are tried in this order:
  1. frontmcp.config.ts
  2. frontmcp.config.js
  3. frontmcp.config.json
  4. frontmcp.config.mjs
  5. frontmcp.config.cjs

Override precedence

For every CLI option that’s also expressible in the config, the effective value is computed as:
explicit CLI flag  >  FRONTMCP_<NAME> env var  >  frontmcp.config field  >  built-in default
Example: frontmcp dev --port 5000 always wins, regardless of transport.http.port in the config. With no flag, the resolver reads transport.http.port, then falls back to the framework default (3000).
When using JSON format, add "$schema" for autocomplete in VS Code and WebStorm:
{
  "$schema": "./node_modules/@frontmcp/cli/frontmcp.schema.json",
  "name": "my-server",
  "deployments": [{ "target": "node" }]
}

Top-Level Fields

FieldTypeRequiredDescription
namestringYesServer name (kebab-case, no spaces)
versionstringNoServer version
entrystringNoCustom entry file path (consumed by dev, inspector, pm start/socket)
nodeVersionstringNoTarget Node.js version
deploymentsDeploymentTarget[]YesOne or more deployment targets
buildobjectNoBuild-specific options (esbuild config, dependencies)
transportTransportConfigNoPer-protocol defaults consumed by dev / inspector / pm (issue #400)
envEnvOverlaysNoshareddev / test / ship overlays merged into the spawned child env (issue #400)
clientsClientsConfigNoPer-client connection snippets emitted by frontmcp eject-mcp-config <client> (issue #400)
testTestConfigNofrontmcp test defaults overridden by CLI flags (issue #400)
skillsSkillsCliConfigNofrontmcp skills install / export defaults (issue #400)

Per-command consumption (issue #400)

CommandConfig fields consumed
buildname, version, entry, deployments, build, nodeVersion
deventry, transport.http.port, env.sharedenv.dev
testtest.timeoutMs, test.runInBand, test.coverage, test.testMatch, env.sharedenv.test
inspectortransport.default, transport.http.port, transport.stdio.command/args
pm start / socket / servicename, entry, transport.http.port, transport.http.socketPath, env.sharedenv.ship
skills install / exportskills.provider, skills.bundle, skills.install, skills.exportTarget
eject-mcp-config <client>clients.<client>, name, transport, env.ship

Transport defaults

transport: {
  default: 'http',                     // 'http' | 'sse' | 'stdio'
  http: { port: 3000, path: '/mcp', host: '127.0.0.1' },
  stdio: { command: 'node', args: ['dist/main.js'] },
}

Env overlays

shared applies everywhere; mode overlays (dev, test, ship) layer on top:
env: {
  shared: { LOG_LEVEL: 'info' },
  dev:    { NODE_ENV: 'development' },
  test:   { NODE_ENV: 'test', JEST_WORKER_ID: '1' },
  ship:   { NODE_ENV: 'production' },
}
Note: .env and .env.local (loaded by dev) still win over config overlays — file-based env is the deployment escape hatch.

Client snippets

clients: {
  'claude-code':    { name: 'my-server', transport: 'http', url: 'http://127.0.0.1:3000/mcp' },
  'claude-desktop': { transport: 'stdio', command: 'npx', args: ['-y', 'my-server'] },
  cursor:           { transport: 'http', url: 'http://127.0.0.1:3000/mcp' },
  windsurf:         { transport: 'stdio', command: 'npx', args: ['-y', 'my-server'] },
  vscode:           { transport: 'stdio', command: 'npx', args: ['-y', 'my-server'] },
}
Emit a ready-to-paste snippet:
frontmcp eject-mcp-config claude-code              # prints to stdout
frontmcp eject-mcp-config claude-code --out ~/.config/claude/mcp.json
frontmcp eject-mcp-config claude-code --out ~/.config/claude/mcp.json --dry-run

Deployment Targets

Each entry in deployments defines a build target with independent settings:
TargetDescriptionTransportStorage
nodeStandalone Node.js serverStreamable HTTP, SSE, stdioRedis, SQLite, memory
distributedMulti-pod with HAStreamable HTTPRedis (required)
vercelVercel FunctionsStreamable HTTPVercel KV
lambdaAWS LambdaStreamable HTTPDynamoDB, ElastiCache
cloudflareCloudflare WorkersStreamable HTTPKV, Durable Objects
browserBrowser bundleIn-memoryMemory only
cliStandalone binarystdioSQLite, memory
sdkLibrary for embeddingDirect (in-process)Configurable
mcpbMCP Bundle archive (see MCPB)stdioSQLite, memory

Deployment Target Fields

FieldTypeDescription
targetstringOne of the 9 target types above
serverServerConfigHTTP, CORS, CSP, and security header settings
haHaConfigHA settings (distributed target only)
cliCliConfigCLI-specific settings (cli target only)
jsbooleanGenerate .js bundle instead of native binary (cli target)
envRecordPer-target environment variables

MCPB-Only Fields

The mcpb target supports additional fields for MCP Bundle metadata:
FieldTypeDescription
displayNamestringHuman-friendly bundle title shown in the installer
longDescriptionstringMarkdown description for the extension details page
author{ name, email?, url? }Overrides parsed package.json.author
licensestringSPDX license identifier
homepagestringProject homepage URL
repositorystring | { type, url }Source repository
documentationstringDocumentation URL
supportstringSupport / issues URL
iconstringIcon path relative to project root (PNG)
keywordsstring[]Keywords for client search
privacyPoliciesstring[]Privacy policy URLs for external services this server talks to
compatibilityobject{ claude_desktop?, platforms?, runtimes? } constraints
userConfigRecordMCPB user_config overrides (type, min/max, etc.) — merged with translated setup.steps
sea{ enabled?, mergeFrom? }Build SEA binary for the host and/or merge pre-built binaries
includeNodeModulesbooleanInclude server/node_modules/ in the archive (off by default)
deterministicbooleanProduce byte-identical archives across runs (defaults to true)

Server Configuration

export default defineConfig({
  name: 'my-server',
  deployments: [{
    target: 'node',
    server: {
      http: {
        port: 3000,
        socketPath: '/tmp/mcp.sock',  // Unix socket (alternative to port)
        entryPath: '/mcp',
        cors: {
          origins: ['https://app.example.com'],
          credentials: true,
          maxAge: 600,
        },
      },
      csp: {
        enabled: true,
        directives: {
          'default-src': "'self'",
          'script-src': "'self' https://cdn.example.com",
        },
        reportUri: 'https://report.example.com/csp',
        reportOnly: false,
      },
      headers: {
        hsts: 'max-age=31536000; includeSubDomains',
        contentTypeOptions: 'nosniff',
        frameOptions: 'DENY',
      },
      cookies: {
        affinity: '__frontmcp_node',
        domain: 'example.com',
        sameSite: 'Lax',
      },
    },
  }],
});

HTTP Options

FieldTypeDefaultDescription
portnumber3000HTTP listen port (node / distributed only)
socketPathstring---Unix socket path (alternative to port; node / distributed only)
entryPathstring''Base path for the MCP endpoint (e.g. '/mcp')
corsobject---CORS configuration — { origins?, credentials?, maxAge? }
bodyLimitnumber | string'4mb'Max body size for express.json(). Bytes (number) or body-parser string ('4mb', '500kb', …). Oversized requests get HTTP 413 with a JSON-RPC envelope. See Transport Security → Request Body Limits.
urlencodedLimitnumber | stringfalls back to bodyLimitMax body size for application/x-www-form-urlencoded.

CSP Options

FieldTypeDefaultDescription
enabledbooleanfalseEnable CSP headers
directivesRecord---Directive name to value(s) map (e.g., { 'default-src': "'self'" })
reportUristring---URI for CSP violation reports
reportOnlybooleanfalseUse Content-Security-Policy-Report-Only header

Security Headers

FieldTypeDefaultDescription
hstsstring---Strict-Transport-Security value
contentTypeOptionsstringnosniffX-Content-Type-Options value
frameOptionsstringDENYX-Frame-Options value

HA Configuration

FieldTypeDefaultDescription
heartbeatIntervalMsnumber10000Heartbeat write interval
heartbeatTtlMsnumber30000Heartbeat TTL (2-3x interval)
takeoverGracePeriodMsnumber5000Grace period before takeover
redisKeyPrefixstringmcp:ha:Redis key prefix

Multi-Target Example

Deploy the same server to Node.js, distributed, and Vercel:
import { defineConfig } from 'frontmcp';

export default defineConfig({
  name: 'my-server',
  version: '1.0.0',
  deployments: [
    {
      target: 'node',
      server: { http: { port: 3000 } },
    },
    {
      target: 'distributed',
      ha: { heartbeatIntervalMs: 5000 },
      server: {
        csp: {
          enabled: true,
          directives: { 'default-src': "'self'", 'upgrade-insecure-requests': '' },
        },
        headers: { hsts: 'max-age=31536000; includeSubDomains' },
      },
    },
    {
      target: 'vercel',
    },
  ],
});
Build each target independently:
frontmcp build --target node
frontmcp build --target distributed
frontmcp build --target vercel

Project-Defined CLI Commands

The optional cli.commands block registers project-specific verbs that ship alongside the built-in frontmcp commands. Each verb spawns a runner module from the project (TS or JS) as a child process, so the project’s own code stays out of the CLI’s process. Verb names must match /^[a-zA-Z][a-zA-Z0-9:_-]*$/ — start with a letter, then letters, digits, :, _, or -. Examples: deploy, db-migrate, project:init. Names that match a built-in verb (see Reserved Verbs below) are rejected at config load. Each command entry accepts:
FieldTypeRequiredDescription
entrystringyesPath to the runner module (TS or JS).
descriptionstringnoShown in frontmcp --help and --list-commands.
argumentsarraynoPositional argument definitions (variadic must be last).
optionsarraynoLong/short flag definitions with optional defaults.
hiddenbooleannoWhen true, the verb still runs but is omitted from help output and --list-commands. Useful for internal scripts you don’t want surfaced to end users.
// frontmcp.config.ts
import { defineConfig } from 'frontmcp';

export default defineConfig({
  name: 'my-server',
  deployments: [{ target: 'node' }],
  cli: {
    commands: {
      deploy: {
        entry: './scripts/deploy.ts',
        description: 'Push the current build to staging',
        arguments: [{ name: 'env', required: true, description: 'Target env' }],
        options: [
          { flags: '-n, --dry-run', description: 'Skip writes' },
          { flags: '-c, --concurrency <num>', default: 4 },
        ],
      },
      'db-migrate': {
        entry: './scripts/migrate.ts',
        description: 'Run pending database migrations',
      },
      'internal:reindex': {
        entry: './scripts/reindex.ts',
        description: 'Rebuild local search index',
        hidden: true, // omitted from --help and --list-commands
      },
    },
  },
});
After saving the config, the new verbs show up under Project Commands in frontmcp --help:
frontmcp deploy prod --concurrency 8
frontmcp db-migrate
frontmcp --list-commands   # Lists every verb with [built-in]/[project] tag

Reserved Verbs

Verb names that collide with a built-in command (dev, build, test, start, skills, etc.) are rejected at config-load time with a helpful message. Use a project-namespaced prefix (e.g. project:init, db-migrate) to avoid the collision.

Runner Selection

Entry extensionRunner
.ts, .tsx, .mts, .ctsnode --import tsx <entry>
.js, .mjs, .cjsnode <entry>
The runner receives the parsed positionals as argv plus a FRONTMCP_PROJECT_COMMAND env var containing a JSON payload with verb, positionals, options, and cwd for richer scripting.

Helper Functions

import { defineConfig, loadFrontMcpConfig, findDeployment, getDeploymentTargets } from 'frontmcp';

// Load config from a directory
const config = await loadFrontMcpConfig(process.cwd());

// Find a specific deployment target
const distributed = findDeployment(config, 'distributed');

// List all configured targets
const targets = getDeploymentTargets(config); // ['node', 'distributed', 'vercel']

Production Build

Build and deploy guide

High Availability

Multi-pod deployment with session failover

Security Headers

CSP and security header configuration

Runtime Modes

Standalone, distributed, and serverless modes