Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt

Use this file to discover all available pages before exploring further.

This page covers Server Mode (HTTP). For embedded SDK usage or serverless handlers, see Runtime Modes.
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 */,
    socketPath?: '/tmp/mcp.sock',  // Unix socket path (overrides port)
    cors?: CorsOptions | false, // default: permissive with built-in adapter (all origins + credentials)
  },

  /** Transport & session lifecycle (NEW in v0.6) */
  transport?: {
    // Session lifecycle
    // sessionMode has been removed in v1.0 — configure via transport.protocol presets
    platformDetection?: { customOnly?: boolean },

    // Protocol configuration (use preset or custom config)
    protocol?: 'legacy' | 'modern' | 'stateless-api' | 'full' | ProtocolConfig,
    // default: 'legacy' (SSE + streamable + legacy SSE support)

    // Session persistence (Redis-backed, auto-enabled when redis is configured)
    persistence?: false | {
      defaultTtlMs?: 3600000,   // 1 hour
    },

    // Distributed mode
    distributedMode?: boolean | 'auto',  // default: false
  },

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

  /** Default ESM package loader config (affects all App.esm() apps) */
  loader?: {
    url?: string,           // Base URL for registry + bundle fetching
    registryUrl?: string,   // Separate registry URL (if different)
    token?: string,         // Bearer token for auth
    tokenEnvVar?: string,   // Env var name containing bearer token (takes precedence over `token`)
  },

  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?: (
    | { mode: 'remote', name: string, baseUrl: string, ... }
    | { mode: '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);
  socketPath?: string;          // Unix socket path (overrides port)
  cors?: CorsOptions | false;   // default: permissive with built-in adapter (all origins + credentials)
}
FieldDescription
portHTTP listening port (default: 3001)
entryPathJSON-RPC entry path; must match .well-known discovery
hostFactoryCustom host implementation for advanced setups
socketPathUnix socket path; when set, server listens on a socket instead of a TCP port
corsCORS configuration (see CORS below)
  • 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.
  • socketPath: listen on a Unix socket instead of a TCP port. The entire HTTP feature set (streamable HTTP, SSE, elicitation, sessions) works unchanged over Unix sockets.
  • 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.

CORS

CORS is configured via the http.cors option. It supports three modes:
ValueBehavior
undefinedDefault — permissive CORS enabled (origin: true, credentials: false)
falseCORS disabled entirely (no CORS headers are sent)
CorsOptionsCustom CORS configuration
interface CorsOptions {
  origin?:
    | boolean               // true = reflect request origin
    | string                // single allowed origin
    | string[]              // multiple allowed origins
    | ((origin: string | undefined, cb: (err: Error | null, allow?: boolean) => void) => void);
  credentials?: boolean;    // allow cookies / auth headers (default: false)
  maxAge?: number;          // preflight cache duration in seconds
}
FieldTypeDefaultDescription
originboolean | string | string[] | functionAllowed origins; true reflects the request Origin header. Defaults to true only via the built-in DEFAULT_CORS when cors is omitted entirely.
credentialsbooleanfalseWhether to allow credentials (cookies, authorization headers)
maxAgenumberHow long (in seconds) browsers may cache preflight responses
The permissive default (origin: true, credentials: false) only applies when using the built-in Express adapter (i.e. no hostFactory). When a custom hostFactory is provided, the factory receives the full http config (including cors) but is responsible for applying CORS itself — FrontMCP does not install CORS middleware automatically.
An empty cors: {} object (without an origin key) will not enable CORS middleware — the adapter requires an explicit origin value. To enable permissive CORS, omit cors entirely or pass cors: { origin: true }.
FrontMCP automatically adds Mcp-Session-Id (alongside WWW-Authenticate) to the Access-Control-Expose-Headers response header when CORS is enabled. This ensures Streamable HTTP clients can read the session ID from cross-origin responses without additional configuration.
@FrontMcp({
  http: { port: 3001 },
  // cors is undefined → permissive CORS (origin: true, credentials: false)
})

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 presets, and session persistence. Configure it at the server level or per-app when using splitByApp: true.
@FrontMcp({
  transport: {
    // Session lifecycle
    platformDetection: { customOnly: false },

    // Protocol preset (or custom config)
    protocol: 'legacy',  // default - SSE + streamable + legacy SSE support

    // Session persistence (auto-enabled when top-level redis is configured)
    persistence: {
      defaultTtlMs: 3600000,
    },

    // Distributed mode for serverless/multi-instance deployments
    distributedMode: false,
  },
})

Session Lifecycle

OptionDefaultDescription
platformDetectionundefinedPlatform detection config; set customOnly: true to skip built-in mappings

Protocol Presets

Use protocol presets for simplified configuration, or provide a custom config object for fine-grained control:
PresetDescriptionLegacy SSESSEStreamableJSONStatelessStrict Session
legacyDefault - Modern + legacy SSE support
modernSSE + streamable HTTP with strict sessions
stateless-apiNo sessions, pure request/response
fullAll protocols enabled, maximum compatibility
@FrontMcp({
  transport: {
    protocol: 'legacy',  // Default - backwards compatible
  },
})

Protocol Options (for custom config)

OptionDefault (legacy)Description
ssetrueEnable SSE listener for server-initiated messages
streamabletrueEnable streamable HTTP transport (POST with SSE response)
jsonfalseEnable JSON-only responses (stateful HTTP)
statelessfalseEnable stateless HTTP mode (no session required)
legacytrueEnable legacy SSE transport for older clients
strictSessiontrueRequire session ID for streamable HTTP
Use protocol: 'legacy' (the default) for maximum backwards compatibility with older MCP clients. Use protocol: 'modern' when you only need to support newer Streamable HTTP clients.

Session Persistence

Session persistence is automatically enabled when you configure top-level redis. Sessions are persisted to Redis/Vercel KV and transports can be recreated after server restart.
@FrontMcp({
  redis: {
    host: 'redis.example.com',
    port: 6379,
  },
  // persistence auto-enabled using global redis config
})
To explicitly disable persistence when redis is configured:
@FrontMcp({
  redis: { host: 'localhost' },
  transport: {
    persistence: false,  // Explicitly disable
  },
})
To customize persistence TTL:
@FrontMcp({
  redis: { host: 'localhost' },
  transport: {
    persistence: {
      defaultTtlMs: 7200000, // 2 hours (default: 1 hour)
    },
  },
})
OptionDefaultDescription
defaultTtlMs3600000Session TTL in milliseconds (1 hour)
Session persistence uses the top-level redis config. Configure redis once and it’s automatically shared by 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: { redis: { ... } }) — stores refresh tokens securely
@FrontMcp({
  redis: {
    host: 'localhost',
    keyPrefix: 'myapp:',
  },
  transport: {
    persistence: { defaultTtlMs: 3600000 },
  },
  auth: {
    mode: 'local',
    tokenStorage: '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({
  auth: {
    mode: 'local',
    transport: {
      enableStreamableHttp: true,
      enableStatefulHttp: true,
      recreation: {
        enabled: true,
        redis: { host: 'localhost' },
      },
    },
  },
})

Migration Mapping

Old PathNew Path
session.platformDetectiontransport.platformDetection
auth.transport.enableLegacySSEtransport.protocol.legacy
auth.transport.enableSseListenertransport.protocol.sse
auth.transport.enableStreamableHttptransport.protocol.streamable
auth.transport.enableStatelessHttptransport.protocol.stateless
auth.transport.enableStatefulHttptransport.protocol.json
auth.transport.requireSessionForStreamabletransport.protocol.strictSession
auth.transport.recreationtransport.persistence
auth.transport.recreation.redisredis (top-level)
Instead of individual flags, use a protocol preset: 'legacy' (default), 'modern', 'stateless-api', or 'full'.

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: {
  mode: 'remote',
  name: 'frontegg',
  baseUrl: 'https://auth.example.com',
  dcrEnabled?: boolean,
  clientId?: string | ((info) => string), // for non-DCR via local proxy
  mode?: 'local' | 'remote' | '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: {
  mode: '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)
  • Use protocol presets ('legacy', 'modern', etc.) instead of individual flags
  • Configure redis at top-level for shared use across features (auto-enables persistence)
  • Use Redis for session persistence in production deployments
  • 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 protocol: 'stateless-api' with short-lived upstream tokens
  • Use protocol: 'stateless-api' 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.