Skip to main content
FrontMCP servers are defined with a single decorator, @FrontMcp({ ... }). This page shows the minimal config and then every top-level option you can use. Deep dives live in the pages listed under Servers.

Minimal server

import { FrontMcp } from '@frontmcp/sdk';
import MyApp from './my.app';

@FrontMcp({
  info: { name: 'My Server', version: '0.1.0' },
  apps: [MyApp],
})
export default class Server {}
Required:
  • info.name (string)
  • info.version (string)
  • apps (at least one app)
Everything else is optional with sensible defaults.

Full configuration (at a glance)

@FrontMcp({
  /** Required */
  info: {
    name: 'Expense MCP Server',
    version: '1.0.0',
    title?: 'Human title',
    websiteUrl?: 'https://example.com',
    icons?: Icon[], // MCP Icon[]
  },
  apps: [/* App classes */],
Info field descriptions:
FieldDescription
nameServer name shown in MCP discovery
versionSemantic version for clients to track
titleOptional human-readable title
websiteUrlLink to documentation or homepage
iconsMCP Icon array for visual branding
@FrontMcp({
  /** Optional */
  serve?: true,                 // default true (auto-boot)
  splitByApp?: false,           // app composition mode
  providers?: [/* provider classes/factories/values */],

  http?: {
    port?: 3001,                // default 3001
    entryPath?: '',             // MUST match PRM resourcePath in .well-known
    hostFactory?: /* custom host */,
  },

  /** Transport & session lifecycle (NEW in v0.6) */
  transport?: {
    // Session lifecycle
    sessionMode?: 'stateful' | 'stateless',     // default 'stateful'
    transportIdMode?: 'uuid' | 'jwt',           // default 'uuid'
    platformDetection?: { customOnly?: boolean },

    // Protocol switches
    enableLegacySSE?: false,
    enableSseListener?: true,
    enableStreamableHttp?: true,
    enableStatelessHttp?: false,
    enableStatefulHttp?: false,
    requireSessionForStreamable?: true,

    // Session persistence (Redis-backed)
    persistence?: {
      enabled?: false,
      defaultTtlMs?: 3600000,   // 1 hour
    },
  },

  /** Shared Redis config (NEW in v0.6) */
  redis?: {
    host: 'localhost',
    port?: 6379,
    password?: string,
    db?: 0,
    tls?: false,
    keyPrefix?: 'mcp:',
    defaultTtlMs?: 3600000,
  },

  logging?: {
    level?: LogLevel.Info,      // Debug | VERBOSE | Info | Warn | Error | Off
    enableConsole?: true,       // default true
    prefix?: string,
    transports?: [/* custom log transports */],
  },

  /** Server-level default auth (omit if splitByApp: true) */
  auth?: (
    | { type: 'remote', name: string, baseUrl: string, ... }
    | { type: 'local',  id: string,   name: string,   ... }
  ),
})

Composition mode

FrontMCP can host many apps. Choose how they’re exposed:
  • Multi-App (default): splitByApp: false One server scope. You may configure server-level auth and all apps inherit it (apps can still override with app-level auth).
  • Split-By-App: splitByApp: true Each app is isolated under its own scope/base path (for example /billing). Streamable HTTP, the /message SSE endpoint, and OAuth issuers reuse that scope automatically. Server-level auth is disallowed; configure auth per app. (See Authentication → Overview.)
If you’re offering multiple products or tenants, splitByApp: true gives clean separation and per-app auth.

HTTP transport

http: {
  port?: number;      // default 3001
  entryPath?: string; // default ''; MUST match the PRM resourcePath in .well-known
  hostFactory?: FrontMcpServer | ((cfg) => FrontMcpServer);
}
FieldDescription
portHTTP listening port (default: 3001)
entryPathJSON-RPC entry path; must match .well-known discovery
hostFactoryCustom host implementation for advanced setups
  • Port: listening port for Streamable HTTP.
  • entryPath: your MCP JSON-RPC entry ('' or '/mcp'). Must align with discovery.
  • hostFactory: advanced — provide/construct a custom host implementation.
  • Split-by-app scopes: when splitByApp is enabled, clients hit <entryPath>/<appId> (for example /mcp/billing) and subscribe via <entryPath>/<appId>/message; FrontMCP handles the prefixing.

Transport

New in v0.6: Transport configuration has moved from auth.transport and session to a dedicated top-level transport property. This separates transport/session lifecycle concerns from authentication. See Migration below.
The transport config controls session lifecycle, protocol switches, and session persistence. Configure it at the server level or per-app when using splitByApp: true.
@FrontMcp({
  transport: {
    // Session lifecycle
    sessionMode: 'stateful',
    transportIdMode: 'uuid',
    platformDetection: { customOnly: false },

    // Protocol switches
    enableLegacySSE: false,
    enableSseListener: true,
    enableStreamableHttp: true,
    enableStatelessHttp: false,
    enableStatefulHttp: false,
    requireSessionForStreamable: true,

    // Session persistence
    persistence: {
      enabled: true,
      defaultTtlMs: 3600000,
    },
  },
})

Session Lifecycle

OptionDefaultDescription
sessionMode'stateful''stateful' (server-side store) or 'stateless' (JWT-based)
transportIdMode'uuid''uuid' (per-node identity) or 'jwt' (signed, distributed)
platformDetectionundefinedPlatform detection config; set customOnly: true to skip built-in mappings
transport: {
  sessionMode: 'stateful',    // Server-side session store
  transportIdMode: 'uuid',    // Random UUID per transport
}

Protocol Switches

Choose which MCP transports to enable based on your deployment:
OptionDefaultWhen to enableNotes
enableStreamableHttptrueModern MCP clients (Claude Desktop, Cursor, Rendezvous)Primary transport; streams JSON-RPC over HTTP.
enableStatefulHttpfalseLocal previews, OAuth consent screens, Jest suitesJSON-only endpoint for requests without SSE—handy for testing and health checks.
enableStatelessHttpfalsePublic mode or CI agents that cannot run initializeSingleton transports that skip initialization handshake.
enableSseListenertrueManual SSE subscribers or dashboardsControls GET /message endpoint. Disable to reduce long-lived sockets.
enableLegacySSEfalseSupporting pre-Streamable clients temporarilyLegacy HTTP + SSE combo—plan to remove once clients upgrade.
requireSessionForStreamabletruePublic demos or scripted testsSet false to let Streamable HTTP requests bootstrap themselves.
Leave enableStatelessHttp off in production unless you fully trust the client surface. Stateless transports reuse process-wide connections, which is perfect for CI but can bypass fine-grained session teardown.
@FrontMcp({
  transport: {
    enableStreamableHttp: true,
    enableStatefulHttp: true,      // JSON fallback for previews
    enableStatelessHttp: false,    // keep anonymous traffic scoped
    requireSessionForStreamable: false,
  },
})

Session Persistence

Enable Redis-backed session persistence for distributed deployments or session recovery:
@FrontMcp({
  redis: {
    host: 'redis.example.com',
    port: 6379,
  },
  transport: {
    persistence: {
      enabled: true,
      defaultTtlMs: 7200000, // 2 hours
    },
  },
})
OptionDefaultDescription
enabledfalseEnable session persistence
defaultTtlMs3600000Session TTL in milliseconds (1 hour)
Session persistence uses the top-level redis config. Configure redis once and reference it from both transport.persistence and auth.tokenStorage.
You can apply different transport policies per app when splitByApp: true. Sensitive apps stay stream-only with strict session IDs, while demo or health-check apps can enable stateful/stateless HTTP for easier automation.

Redis

New in v0.6: Redis configuration has moved to a dedicated top-level redis property, shared by both transport.persistence and auth.tokenStorage.
Configure Redis once at the server level for use across features:
@FrontMcp({
  redis: {
    host: 'redis.example.com',
    port: 6379,
    password: 'secret',
    db: 0,
    tls: true,
    keyPrefix: 'mcp:',
    defaultTtlMs: 3600000,
  },
})
OptionDefaultDescription
host(required)Redis server hostname
port6379Redis server port
password-Optional authentication password
db0Redis database index
tlsfalseEnable TLS/SSL connection
keyPrefix'mcp:'Prefix for all Redis keys
defaultTtlMs3600000Default TTL for cached data (1 hour)

Usage with Features

The redis config is automatically used by:
  • Session persistence (transport.persistence) — stores session state for recovery
  • Token storage (auth.tokenStorage: { type: 'redis' }) — stores refresh tokens securely
@FrontMcp({
  redis: {
    host: 'localhost',
    keyPrefix: 'myapp:',
  },
  transport: {
    persistence: { enabled: true },
  },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    tokenStorage: { type: 'redis' },
  },
})

Migration from auth.transport

Deprecated: The auth.transport and session properties are deprecated and will be removed in v1.0.0. Migrate to the top-level transport config.
FrontMCP automatically migrates old configs at runtime with a deprecation warning. Update your config manually to remove the warning:
@FrontMcp({
  session: {
    sessionMode: 'stateful',
    transportIdMode: 'uuid',
  },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    transport: {
      enableStreamableHttp: true,
      enableStatefulHttp: true,
      recreation: {
        enabled: true,
        redis: { host: 'localhost' },
      },
    },
  },
})

Migration Mapping

Old PathNew Path
session.sessionModetransport.sessionMode
session.transportIdModetransport.transportIdMode
session.platformDetectiontransport.platformDetection
auth.transport.enableLegacySSEtransport.enableLegacySSE
auth.transport.enableSseListenertransport.enableSseListener
auth.transport.enableStreamableHttptransport.enableStreamableHttp
auth.transport.enableStatelessHttptransport.enableStatelessHttp
auth.transport.enableStatefulHttptransport.enableStatefulHttp
auth.transport.requireSessionForStreamabletransport.requireSessionForStreamable
auth.transport.recreationtransport.persistence
auth.transport.recreation.redisredis (top-level)

Logging

logging: {
  level?: LogLevel;        // default Info
  enableConsole?: boolean; // default true
  prefix?: string;
  transports?: LogTransportType[]; // custom sinks via @FrontMcpLogTransport
}
FieldDescription
levelMinimum log level: Debug, Verbose, Info, Warn, Error, Off
enableConsoleWhether to output to stdout (default: true)
prefixOptional prefix for all log messages
transportsCustom log transport implementations
Use custom log transports for shipping logs to external systems; console remains on by default.

Global providers

providers: [
  /* Provider classes/factories/values */
];
Define DI-style singletons available to all apps (and their tools/plugins). Scopes are supported at the provider level (GLOBAL, SESSION, REQUEST).

Authentication (server level)

Server-level auth sets the default auth for all apps (unless splitByApp: true, where auth must be per-app).

Remote OAuth (encapsulated external IdP)

auth: {
  type: 'remote',
  name: 'frontegg',
  baseUrl: 'https://auth.example.com',
  dcrEnabled?: boolean,
  clientId?: string | ((info) => string), // for non-DCR via local proxy
  mode?: 'orchestrated' | 'transparent',
  allowAnonymous?: boolean,
  consent?: boolean,
  scopes?: string[],
  grantTypes?: ['authorization_code','refresh_token'],
  authEndpoint?: string,
  tokenEndpoint?: string,
  registrationEndpoint?: string,
  userInfoEndpoint?: string,
  jwks?: JSONWebKeySet,
  jwksUri?: string,
}

Local OAuth (built-in AS)

auth: {
  type: 'local',
  id: 'local',
  name: 'Local Auth',
  scopes?: string[],
  grantTypes?: ['authorization_code','refresh_token'],
  allowAnonymous?: boolean,  // default true
  consent?: boolean,         // show tool/resource/prompt consent
  jwks?: JSONWebKeySet,      // inline keys (optional)
  signKey?: JWK | Uint8Array // private key (optional; auto-gen if omitted)
}
Apps can also define their own auth (and mark themselves standalone) to expose an isolated auth surface — useful when mixing public and private apps under one server.

Bootstrapping & discovery

  • Version safety: on boot, FrontMCP checks that all @frontmcp/* packages are aligned and throws a clear “version mismatch” error otherwise.
If you disable serve, you’re responsible for calling the core bootstrap yourself.

Common starting points

  • Single app, default everything: minimal sample above.
  • Multiple apps, shared auth: omit splitByApp, set server-level auth.
  • Isolated apps with per-app auth: set splitByApp: true, configure auth in each app.

Best Practices

Do:
  • Start with minimal config and add options as needed
  • Use splitByApp: true for multi-tenant deployments
  • Use top-level transport config (not auth.transport or session)
  • Configure redis at top-level for shared use across features
  • Use stateful session mode with Redis for production deployments
  • Enable persistence for distributed or high-availability setups
  • Set log level to Info or higher in production
Don’t:
  • Configure server-level auth when using splitByApp: true
  • Use deprecated auth.transport or session — migrate to transport
  • Disable serve unless you’re managing bootstrap manually
  • Use stateless session mode with short-lived upstream tokens
  • Enable enableStatelessHttp in production without trust boundaries
  • Leave enableConsole: true in containerized production (use transports)

Next up: learn how to structure Apps, Tools, Resources, and Prompts in the Core Components section.