
# Sessions

> Opt-in `Mcp-Session-Id` lifecycle, legacy only.

> [!NOTE]
> Protocol-level sessions were removed in `2026-07-28`. Modern servers are stateless; carry cross-call state in explicit handles passed as ordinary tool arguments.

Sessions are off by default. Enable them and `initialize` starts issuing session ids that clients must return on every subsequent request:

```ts
defineMcpHandler({
  name: "my-server",
  version: "1.0.0",

  // Simple — uses crypto.randomUUID()
  session: true,

  // Or configured
  session: {
    generateId: (event) => `session-${Date.now()}`,
    maxSessions: 1000,
  },

  tools: [/* ... */],
});
```

## Lifecycle

1. `initialize` creates a session and returns it in the `mcp-session-id` response header.
2. Every later `POST`, `GET`, and `DELETE` must carry that header.
3. `DELETE` removes it from the store.

| Situation                | Response |
| ------------------------ | -------- |
| Missing `mcp-session-id` | `400`    |
| Unknown session id       | `404`    |
| `maxSessions` exceeded   | `503`    |

Session ids are format-checked before they are looked up or stored — bounded length, alphanumerics plus `-` and `_`. A custom `generateId` must stay inside that alphabet.

## The Store Is In-Memory

The built-in store is a bounded `Map` in the handler's closure. That has consequences worth being explicit about:

- **It does not survive a restart.** Clients get `404` and must re-`initialize`.
- **It is per-process.** Behind a load balancer without sticky sessions, requests will land on a process that has never seen the id.
- **`maxSessions` is unset by default.** The store is a client-growable, server-lifetime collection: without a cap, anything that can reach `initialize` can grow it. Set `maxSessions` on any endpoint that is not fully trusted — exceeding it returns `503` rather than consuming more memory.

For anything multi-process, prefer keeping real state in your own store keyed by the session id, so losing the id costs a handshake rather than data.

## Reading the Session

```ts
defineMcpTool({
  name: "remember",
  handler: (event) => {
    const sessionId = event.context.mcp?.sessionId;
    return { content: [{ type: "text", text: `session: ${sessionId ?? "none"}` }] };
  },
});
```

> [!IMPORTANT]
> A session id is a bearer credential: whoever holds it continues that session. It is not an authentication mechanism — pair sessions with [transport auth](/security/auth) rather than treating the id as proof of identity.
