> ## 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.

# The FrontMCP Server

> The core FrontMCP server — start with the minimum and scale up with HTTP, sessions, logging, providers, and authentication.

<Note>
  This page covers **Server Mode** (HTTP). For embedded SDK usage or serverless handlers, see [Runtime Modes](/frontmcp/deployment/runtime-modes).
</Note>

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

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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)

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@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    |

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@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?: 'stateful' | 'stateless',     // default 'stateful'
    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,
  },

  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*.)

<Tip>If you’re offering multiple products or tenants, `splitByApp: true` gives clean separation and per-app auth.</Tip>

***

## HTTP transport

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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)
}
```

| 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                               |
| `socketPath`  | Unix socket path; when set, server listens on a socket instead of a TCP port |
| `cors`        | CORS configuration (see [CORS](#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:

| Value         | Behavior                                                                     |
| ------------- | ---------------------------------------------------------------------------- |
| `undefined`   | **Default** — permissive CORS enabled (`origin: true`, `credentials: false`) |
| `false`       | CORS disabled entirely (no CORS headers are sent)                            |
| `CorsOptions` | Custom CORS configuration                                                    |

```typescript theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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
}
```

| Field         | Type                                        | Default | Description                                                                                                                                            |
| ------------- | ------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `origin`      | `boolean \| string \| string[] \| function` | —       | Allowed origins; `true` reflects the request `Origin` header. Defaults to `true` only via the built-in `DEFAULT_CORS` when `cors` is omitted entirely. |
| `credentials` | `boolean`                                   | `false` | Whether to allow credentials (cookies, authorization headers)                                                                                          |
| `maxAge`      | `number`                                    | —       | How long (in seconds) browsers may cache preflight responses                                                                                           |

<Warning>
  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.
</Warning>

<Tip>
  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 }`.
</Tip>

<Info>
  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.
</Info>

<CodeGroup>
  ```ts Default (permissive) theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    http: { port: 3001 },
    // cors is undefined → permissive CORS (origin: true, credentials: false)
  })
  ```

  ```ts Disabled theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    http: {
      port: 3001,
      cors: false,  // No CORS headers
    },
  })
  ```

  ```ts Custom theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    http: {
      port: 3001,
      cors: {
        origin: ['https://app.example.com', 'https://admin.example.com'],
        credentials: true,
        maxAge: 86400,  // cache preflight for 24 hours
      },
    },
  })
  ```
</CodeGroup>

***

## Transport

<Info>
  **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](#migration-from-authttransport) below.
</Info>

The `transport` config controls session lifecycle, protocol presets, and session persistence. Configure it at the server level or per-app when using `splitByApp: true`.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  transport: {
    // Session lifecycle
    sessionMode: 'stateful',
    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

| Option              | Default      | Description                                                                 |
| ------------------- | ------------ | --------------------------------------------------------------------------- |
| `sessionMode`       | `'stateful'` | `'stateful'` (server-side store) or `'stateless'` (JWT-based)               |
| `platformDetection` | `undefined`  | Platform detection config; set `customOnly: true` to skip built-in mappings |

<CodeGroup>
  ```ts Stateful Sessions theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  transport: {
    sessionMode: 'stateful',    // Server-side session store (default)
  }
  ```

  ```ts Stateless Sessions theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  transport: {
    sessionMode: 'stateless',   // JWT-based sessions (client-portable)
  }
  ```

  ```ts Dynamic (per issuer) theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  transport: {
    sessionMode: (issuer) => issuer === 'internal' ? 'stateful' : 'stateless',
  }
  ```
</CodeGroup>

### Protocol Presets

Use protocol presets for simplified configuration, or provide a custom config object for fine-grained control:

| Preset          | Description                                  | Legacy SSE | SSE | Streamable | JSON | Stateless | Strict Session |
| --------------- | -------------------------------------------- | ---------- | --- | ---------- | ---- | --------- | -------------- |
| `legacy`        | **Default** - Modern + legacy SSE support    | ✅          | ✅   | ✅          | ❌    | ❌         | ✅              |
| `modern`        | SSE + streamable HTTP with strict sessions   | ❌          | ✅   | ✅          | ❌    | ❌         | ✅              |
| `stateless-api` | No sessions, pure request/response           | ❌          | ❌   | ❌          | ❌    | ✅         | ❌              |
| `full`          | All protocols enabled, maximum compatibility | ✅          | ✅   | ✅          | ✅    | ✅         | ❌              |

<CodeGroup>
  ```ts Using a Preset theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    transport: {
      protocol: 'legacy',  // Default - backwards compatible
    },
  })
  ```

  ```ts Custom Protocol Config theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    transport: {
      protocol: {
        sse: true,
        streamable: true,
        json: true,        // Enable JSON-only responses
        stateless: false,
        legacy: true,
        strictSession: false,
      },
    },
  })
  ```
</CodeGroup>

### Protocol Options (for custom config)

| Option          | Default (legacy) | Description                                               |
| --------------- | ---------------- | --------------------------------------------------------- |
| `sse`           | `true`           | Enable SSE listener for server-initiated messages         |
| `streamable`    | `true`           | Enable streamable HTTP transport (POST with SSE response) |
| `json`          | `false`          | Enable JSON-only responses (stateful HTTP)                |
| `stateless`     | `false`          | Enable stateless HTTP mode (no session required)          |
| `legacy`        | `true`           | Enable legacy SSE transport for older clients             |
| `strictSession` | `true`           | Require session ID for streamable HTTP                    |

<Tip>
  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.
</Tip>

### 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.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  redis: {
    host: 'redis.example.com',
    port: 6379,
  },
  // persistence auto-enabled using global redis config
})
```

To explicitly disable persistence when redis is configured:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  redis: { host: 'localhost' },
  transport: {
    persistence: false,  // Explicitly disable
  },
})
```

To customize persistence TTL:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  redis: { host: 'localhost' },
  transport: {
    persistence: {
      defaultTtlMs: 7200000, // 2 hours (default: 1 hour)
    },
  },
})
```

| Option         | Default   | Description                          |
| -------------- | --------- | ------------------------------------ |
| `defaultTtlMs` | `3600000` | Session TTL in milliseconds (1 hour) |

<Info>
  Session persistence uses the top-level `redis` config. Configure `redis` once and it's automatically shared by `transport.persistence` and `auth.tokenStorage`.
</Info>

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

<Info>
  **New in v0.6**: Redis configuration has moved to a dedicated top-level `redis` property, shared by both `transport.persistence` and `auth.tokenStorage`.
</Info>

Configure Redis once at the server level for use across features:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  redis: {
    host: 'redis.example.com',
    port: 6379,
    password: 'secret',
    db: 0,
    tls: true,
    keyPrefix: 'mcp:',
    defaultTtlMs: 3600000,
  },
})
```

| Option         | Default    | Description                          |
| -------------- | ---------- | ------------------------------------ |
| `host`         | (required) | Redis server hostname                |
| `port`         | `6379`     | Redis server port                    |
| `password`     | -          | Optional authentication password     |
| `db`           | `0`        | Redis database index                 |
| `tls`          | `false`    | Enable TLS/SSL connection            |
| `keyPrefix`    | `'mcp:'`   | Prefix for all Redis keys            |
| `defaultTtlMs` | `3600000`  | Default 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

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  redis: {
    host: 'localhost',
    keyPrefix: 'myapp:',
  },
  transport: {
    persistence: { defaultTtlMs: 3600000 },
  },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    tokenStorage: { type: 'redis' },
  },
})
```

***

## Migration from `auth.transport`

<Warning>
  **Deprecated**: The `auth.transport` and `session` properties are deprecated and will be removed in v1.0.0. Migrate to the top-level `transport` config.
</Warning>

FrontMCP automatically migrates old configs at runtime with a deprecation warning. Update your config manually to remove the warning:

<CodeGroup>
  ```ts Before (deprecated) theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    session: {
      sessionMode: 'stateful',
    },
    auth: {
      mode: 'orchestrated',
      type: 'local',
      transport: {
        enableStreamableHttp: true,
        enableStatefulHttp: true,
        recreation: {
          enabled: true,
          redis: { host: 'localhost' },
        },
      },
    },
  })
  ```

  ```ts After (v0.6+) theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
  @FrontMcp({
    redis: {
      host: 'localhost',
    },
    transport: {
      sessionMode: 'stateful',
      protocol: 'full',  // Or use custom: { streamable: true, json: true, ... }
      // persistence auto-enabled when redis is configured
    },
    auth: {
      mode: 'orchestrated',
      type: 'local',
    },
  })
  ```
</CodeGroup>

### Migration Mapping

| Old Path                                     | New Path                           |
| -------------------------------------------- | ---------------------------------- |
| `session.sessionMode`                        | `transport.sessionMode`            |
| `session.platformDetection`                  | `transport.platformDetection`      |
| `auth.transport.enableLegacySSE`             | `transport.protocol.legacy`        |
| `auth.transport.enableSseListener`           | `transport.protocol.sse`           |
| `auth.transport.enableStreamableHttp`        | `transport.protocol.streamable`    |
| `auth.transport.enableStatelessHttp`         | `transport.protocol.stateless`     |
| `auth.transport.enableStatefulHttp`          | `transport.protocol.json`          |
| `auth.transport.requireSessionForStreamable` | `transport.protocol.strictSession` |
| `auth.transport.recreation`                  | `transport.persistence`            |
| `auth.transport.recreation.redis`            | `redis` (top-level)                |

<Tip>
  Instead of individual flags, use a **protocol preset**: `'legacy'` (default), `'modern'`, `'stateless-api'`, or `'full'`.
</Tip>

***

## Logging

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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)

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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)

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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)
}
```

<Info>
  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.
</Info>

***

## Bootstrapping & discovery

* **Version safety**: on boot, FrontMCP checks that all `@frontmcp/*` packages are aligned and throws a clear “version mismatch” error otherwise.

<Warning>If you disable `serve`, you’re responsible for calling the core bootstrap yourself.</Warning>

***

## 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 `stateful` session mode with Redis for 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 `stateless` session mode 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.
