> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-use.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://mcp-use.com/docs/feedback

```json
{
  "path": "/typescript/server/configuration",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Configuration

> Advanced configuration and deployment options

This guide covers the full `MCPServer` configuration API, session and transport options, and deployment strategies.

## Server Configuration

### Basic Configuration

`MCPServer` accepts a `ServerConfig` object at construction time:

```typescript theme={null}
import { MCPServer } from 'mcp-use/server'

const server = new MCPServer({
  // ── Required ──────────────────────────────────────────────
  name: 'my-server',
  version: '1.0.0',

  // ── Identity (shown in Inspector / MCP clients) ───────────
  title: 'My Server',                   // Display name (defaults to name)
  description: 'What this server does', // Shown during discovery
  websiteUrl: 'https://myserver.com',
  favicon: 'favicon.ico',               // Relative to public/ dir
  icons: [
    {
      src: 'icon.svg',
      mimeType: 'image/svg+xml',
      sizes: ['512x512']
    }
  ],

  // ── Network ────────────────────────────────────────────────
  host: 'localhost',                    // Bind hostname (default: 'localhost')
  baseUrl: 'https://mcp.example.com',   // Public URL for widget/asset URLs
                                        // Overrides host:port; also read from MCP_URL env var

  // ── CORS ───────────────────────────────────────────────────
  cors: {
    origin: ['https://myapp.com'],
    allowMethods: ['GET', 'POST', 'OPTIONS'],
  },

  // ── DNS Rebinding Protection ───────────────────────────────
  allowedOrigins: ['https://myapp.com'],  // Enables Host header validation

  // ── Transport ─────────────────────────────────────────────
  stateless: false,                     // See "Stateless Mode" below

  // ── Sessions ──────────────────────────────────────────────
  sessionIdleTimeoutMs: 3600000,        // 1 hour (default: 86400000 = 1 day)
  sessionStore: new InMemorySessionStore(),   // See "Session Storage" below
  streamManager: new InMemoryStreamManager(), // See "Stream Manager" below

  // ── Auth ─────────────────────────────────────────────────
  oauth: oauthAuth0Provider({ ... }),   // See OAuth docs
})

await server.listen(3000)
```

**Port resolution order:** `listen(port)` argument → `--port` CLI flag → `PORT` env var → `3000`

**Base URL resolution order:** `baseUrl` config → `MCP_URL` env var → `http://{host}:{port}`

### Environment Variables

The server reads the following environment variables at runtime:

| Variable          | Effect                                                            | Default                |
| ----------------- | ----------------------------------------------------------------- | ---------------------- |
| `PORT`            | HTTP server port                                                  | `3000`                 |
| `HOST`            | Bind hostname                                                     | `localhost`            |
| `MCP_URL`         | Full public base URL (for widget/asset URLs)                      | `http://{HOST}:{PORT}` |
| `NODE_ENV`        | `production` disables dev features (inspector, type generation)   | —                      |
| `MCP_DEBUG_LEVEL` | Request log verbosity: `info`, `debug`, or `trace`                | `info`                 |
| `CSP_URLS`        | Comma-separated extra URLs added to widget CSP `resource_domains` | —                      |

**OAuth providers** also read zero-config env vars — see the OAuth docs for the full list.

## Transport & Sessions

### Stateless Mode

The server supports two transport modes:

| Mode          | Sessions | SSE | Best for                                            |
| ------------- | -------- | --- | --------------------------------------------------- |
| **Stateful**  | Yes      | Yes | Long-lived clients, notifications, sampling         |
| **Stateless** | No       | No  | Edge functions, serverless, simple request/response |

**Auto-detection (default):**

* **Deno / edge runtimes** → always stateless
* **Node.js** → detected per-request from the `Accept` header:
  * `Accept: application/json, text/event-stream` → stateful (SSE)
  * `Accept: application/json` only → stateless

```typescript theme={null}
// Force stateless mode (ignores Accept header)
const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: true,
})

// Force stateful mode
const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false,
})
```

<Tip>
  Leave `stateless` unset in most cases. Auto-detection lets the same server handle both SSE-capable and HTTP-only clients transparently.
</Tip>

### Session Storage

Session metadata (client capabilities, log level, etc.) is stored in a pluggable `SessionStore`. Three backends are available:

**In-memory (default)**

```typescript theme={null}
import { MCPServer, InMemorySessionStore } from 'mcp-use/server'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  sessionStore: new InMemorySessionStore(), // default — no config needed
})
```

Sessions are lost on server restart. Suitable for single-instance production servers.

**Filesystem (development)**

```typescript theme={null}
import { MCPServer, FileSystemSessionStore } from 'mcp-use/server'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  sessionStore: new FileSystemSessionStore({
    path: '.mcp-use/sessions.json', // default
    debounceMs: 100,                // write debounce (default: 100ms)
    maxAgeMs: 86400000,             // session TTL (default: 24h)
  }),
})
```

Sessions survive HMR reloads, so clients don't need to re-initialize when you save a file during development.

<Warning>
  `FileSystemSessionStore` is not designed for production or distributed deployments. Use Redis for those scenarios.
</Warning>

**Redis (production)**

```bash theme={null}
npm install redis
```

```typescript theme={null}
import { MCPServer, RedisSessionStore } from 'mcp-use/server'
import { createClient } from 'redis'

const redis = createClient({ url: process.env.REDIS_URL })
await redis.connect()

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  sessionStore: new RedisSessionStore({
    client: redis,
    prefix: 'mcp:session:',   // default
    defaultTTL: 3600,         // seconds (default: 3600 = 1 hour)
  }),
})
```

Sessions persist across server restarts and are shared across all instances, enabling horizontal scaling.

### Distributed Stream Management

The `streamManager` controls active SSE connections and is responsible for routing server-to-client push notifications. By default, SSE streams are in-memory, meaning notifications only reach clients connected to the same server instance.

For distributed deployments with multiple server instances behind a load balancer, use `RedisStreamManager` to fan-out notifications via Redis Pub/Sub:

```bash theme={null}
npm install redis
```

```typescript theme={null}
import { MCPServer, RedisSessionStore, RedisStreamManager } from 'mcp-use/server'
import { createClient } from 'redis'

// Redis Pub/Sub requires a dedicated client — it cannot be shared
const redis = createClient({ url: process.env.REDIS_URL })
const pubSubRedis = redis.duplicate()

await redis.connect()
await pubSubRedis.connect()

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  sessionStore: new RedisSessionStore({ client: redis }),
  streamManager: new RedisStreamManager({
    client: redis,           // for session availability checks
    pubSubClient: pubSubRedis, // dedicated Pub/Sub client (required)
    prefix: 'mcp:stream:',  // default
    heartbeatInterval: 10,  // seconds (default: 10)
  }),
})
```

**How it works:**

1. Client connects to Server A → SSE stream created, Server A subscribes to `mcp:stream:{sessionId}` in Redis
2. Client's next request hits Server B (load balancer)
3. Server B sends a notification → publishes to Redis channel
4. Server A receives the Redis message → pushes to the SSE stream → client gets the notification

## Middleware Configuration

`MCPServer` supports both **Hono middleware** and **Express middleware**. Express middleware is automatically detected and adapted to work with Hono.

### Using Middleware

You can add middleware using `server.use()` or `server.app.use()`:

```typescript theme={null}
import { MCPServer } from 'mcp-use/server'
import morgan from 'morgan'
import rateLimit from 'express-rate-limit'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
})

// Hono middleware
server.use(async (c, next) => {
  console.log(`${c.req.method} ${c.req.path}`)
  await next()
})

// Express middleware (automatically adapted)
server.use(morgan('combined'))
server.use('/api', rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
}))

// Mix both types
server.use(morgan('dev'), async (c, next) => {
  await next()
})
```

<Tip>
  Express middleware is detected by function signature (3-4 parameters) and pattern matching for Express-specific code patterns (e.g., `res.send`, `req.body`). Hono middleware uses 2 parameters and Hono-specific patterns (e.g., `c.req`, `c.json`). Both types can be mixed in the same application.
</Tip>

### CORS Customization

By default, CORS is permissive (`origin: "*"`) for developer ergonomics. Set `cors` in the server config to restrict it.

<Warning>
  The `cors` config option **replaces** the default CORS configuration entirely — there is no merge. If you override `cors`, include all headers your clients need (e.g. `mcp-session-id` in `exposeHeaders`).
</Warning>

Default CORS configuration:

```typescript theme={null}
{
  origin: '*',
  allowMethods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowHeaders: [
    'Content-Type', 'Accept', 'Authorization',
    'mcp-protocol-version', 'mcp-session-id',
    'X-Proxy-Token', 'X-Target-URL',
  ],
  exposeHeaders: ['mcp-session-id'],
}
```

Custom CORS example:

```typescript theme={null}
const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  cors: {
    origin: ['https://app.example.com'],
    allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
    allowHeaders: ['Content-Type', 'Authorization', 'mcp-protocol-version'],
    exposeHeaders: ['mcp-session-id'],
  },
})
```

### Route-Scoped Middleware

Add middleware or custom routes using `server.use()` or `server.app.use()`:

```typescript theme={null}
import { MCPServer } from 'mcp-use/server'
import rateLimit from 'express-rate-limit'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
})

// Hono middleware for specific routes
server.use('/api/admin/*', async (c, next) => {
  const apiKey = c.req.header('x-api-key')
  if (!apiKey || apiKey !== process.env.API_KEY) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  await next()
})

// Express middleware for specific routes (automatically adapted)
server.use('/api', rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
}))

// Custom HTTP endpoint
server.get('/health', (c) => c.json({ status: 'ok' }))
```

<Note>
  MCP protocol routes (`/mcp`, `/mcp-use/widgets/*`, `/inspector`) are registered by the server internally. Add your custom routes before calling `listen()` or `getHandler()`.
</Note>

## Security Configuration

### DNS Rebinding Protection

Use `allowedOrigins` to enable Host header validation and protect against DNS rebinding attacks.

**Behavior:**

* `allowedOrigins` not set (default) → no host validation, all `Host` values accepted
* `allowedOrigins: []` → no host validation (same as not set)
* `allowedOrigins: ['https://myapp.com']` → Host header must match one of the configured hostnames; applies globally to all routes

```typescript theme={null}
// Default: no host validation (development)
const devServer = new MCPServer({
  name: 'dev-server',
  version: '1.0.0',
})

// Production: restrict to known hostnames
const prodServer = new MCPServer({
  name: 'prod-server',
  version: '1.0.0',
  allowedOrigins: [
    'https://myapp.com',
    'https://app.myapp.com',
  ],
})

// Load from environment
const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  allowedOrigins: process.env.ALLOWED_ORIGINS?.split(','),
})
```

<Tip>
  `allowedOrigins` accepts full URLs (e.g. `"https://myapp.com"`) and normalizes them to hostnames for validation.
</Tip>

### Session Timeout

Control how long idle sessions are kept in memory:

```typescript theme={null}
const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  sessionIdleTimeoutMs: 3600000, // 1 hour (default: 86400000 = 1 day)
})
```

### Input Validation

Always validate tool inputs using Zod schemas. The schema is enforced automatically before your handler runs:

```typescript theme={null}
import { error, text } from 'mcp-use/server'
import { z } from 'zod'

server.tool({
  name: 'process_input',
  schema: z.object({
    email: z.string().email().describe('Email address'),
    url: z.string().url().describe('URL to process'),
  }),
}, async ({ email, url }) => {
  // Inputs are already validated by Zod at this point
  return text(`Processing ${email} and ${url}`)
})
```

## Next Steps

* [Creating an MCP Server](./creating-mcp-apps-server) - Getting started
* [OAuth / Authentication](./authentication) - Securing your server
* [UI Widgets](./mcp-apps) - Interactive widget components
* [Content Security Policy](./content-security-policy) - Per-widget CSP configuration
* [Deployment: Supabase](./deployment/supabase) - Full Supabase Edge Functions guide
