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:
| Field | Description |
|---|
name | Server name shown in MCP discovery |
version | Semantic version for clients to track |
title | Optional human-readable title |
websiteUrl | Link to documentation or homepage |
icons | MCP 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);
}
| Field | Description |
|---|
port | HTTP listening port (default: 3001) |
entryPath | JSON-RPC entry path; must match .well-known discovery |
hostFactory | Custom 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.
| Option | Default | When to enable | Notes |
|---|
enableStreamableHttp | true | Modern MCP clients (Claude Desktop, Cursor, Rendezvous) | Primary transport that streams JSON-RPC over HTTP; turn it off only when debugging pure JSON flows. |
enableStatefulHttp | false | Local previews, OAuth consent screens, Jest suites | Adds a JSON-only endpoint so requests resolve without an SSE stream—handy for @frontmcp/testing, health checks, and the auth demos. |
enableStatelessHttp | false | Public mode or CI agents that cannot run initialize | Creates singleton transports (shared for anonymous traffic, per-token for authenticated users) so requests can skip the initialization handshake. |
enableSseListener | true | Manual SSE subscribers or dashboards | Controls the GET /message endpoint. Disable to reduce long-lived sockets when everything uses Streamable HTTP. |
enableLegacySSE | false | Supporting pre-Streamable clients temporarily | Keeps the legacy HTTP + SSE combo alive—plan to remove once all clients upgrade. |
requireSessionForStreamable | true | Public demos or scripted tests that cannot persist a session ID | Set 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);
}
| Field | Description |
|---|
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
}
| Field | Description |
|---|
level | Minimum log level: Debug, Verbose, Info, Warn, Error, Off |
enableConsole | Whether to output to stdout (default: true) |
prefix | Optional prefix for all log messages |
transports | Custom 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.