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 */,
  },

  session?: {
    sessionMode?: 'stateless' | 'stateful' | ((issuer) => ...),  // default 'stateless'
    transportIdMode?: 'uuid' | 'jwt' | ((issuer) => ...),        // default 'uuid'
  },

  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 controls

Every authentication mode (public, transparent, orchestrated) accepts an auth.transport block so you can explicitly enable the MCP transports that match your deployment surface. Tightening or relaxing these switches lets you keep production locked down while leaving demos and tests convenient.
OptionDefaultWhen to enableNotes
enableStreamableHttptrueModern MCP clients (Claude Desktop, Cursor, Rendezvous)Primary transport that streams JSON-RPC over HTTP; turn it off only when debugging pure JSON flows.
enableStatefulHttpfalseLocal previews, OAuth consent screens, Jest suitesAdds a JSON-only endpoint so requests resolve without an SSE stream—handy for @frontmcp/testing, health checks, and the auth demos.
enableStatelessHttpfalsePublic mode or CI agents that cannot run initializeCreates singleton transports (shared for anonymous traffic, per-token for authenticated users) so requests can skip the initialization handshake.
enableSseListenertrueManual SSE subscribers or dashboardsControls the GET /message endpoint. Disable to reduce long-lived sockets when everything uses Streamable HTTP.
enableLegacySSEfalseSupporting pre-Streamable clients temporarilyKeeps the legacy HTTP + SSE combo alive—plan to remove once all clients upgrade.
requireSessionForStreamabletruePublic demos or scripted tests that cannot persist a session IDSet to false to let Streamable HTTP requests bootstrap themselves (the auth demo projects use this).
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({
  auth: {
    mode: 'public',
    transport: {
      enableStreamableHttp: true,
      enableStatefulHttp: true,      // JSON fallback for previews
      enableStatelessHttp: false,    // keep anonymous traffic scoped to explicit sessions
      requireSessionForStreamable: false,
    },
  },
});
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.

Sessions & Transport IDs

session: {
  sessionMode?: 'stateless' | 'stateful' | ((issuer) => SessionMode);
  transportIdMode?: 'uuid' | 'jwt' | ((issuer) => TransportIdMode);
}
FieldDescription
sessionMode'stateless' (JWT) or 'stateful' (server-side store)
transportIdMode'uuid' (per-node) or 'jwt' (signed, distributed)
  • sessionMode (default 'stateless'):
  • 'stateless' → session data is carried in a signed/encrypted JWT. Simple, client-portable; no token refresh of nested providers.
  • 'stateful' → server-side store (e.g., Redis). Minimal JWTs, safer for nested tokens, supports refresh.
  • transportIdMode (default 'uuid'):
  • 'uuid' → per-node, strict transport identity.
  • 'jwt' → signed transport IDs for distributed setups; ties into session verification.
You can supply functions for sessionMode / transportIdMode to decide per issuer.

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
  • Configure appropriate session mode for your token refresh needs
  • Set log level to Info or higher in production
Don’t:
  • Configure server-level auth when using splitByApp: true
  • Disable serve unless you’re managing bootstrap manually
  • Use stateless session mode with short-lived upstream tokens
  • Leave enableConsole: true in containerized production (use transports)

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