You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: describe session lifetime and limits in one place
The idle timeout and the session cap were explained piecemeal on three
pages. legacy-clients.md gains a "Session lifetime and limits" section
with both settings, what counts as in flight, what the client sees and
how to turn each off; the options list in run/index.md and the
troubleshooting entry now point at it instead of restating it.
Copy file name to clipboardExpand all lines: docs/run/index.md
+5-6Lines changed: 5 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,12 +70,11 @@ Each transport has its own keyword arguments, all on `run()`:
70
70
*`max_request_body_size`: largest accepted request body in bytes. Defaults to 4 MiB; larger requests
71
71
receive HTTP 413 before parsing or session creation. Raise it only when legitimate MCP messages
72
72
exceed that size.
73
-
*`session_idle_timeout`: how long, in seconds, a [legacy](legacy-clients.md) (session-based)
74
-
client's session may sit with no request in flight before the server closes it. Defaults to 1800
75
-
(30 minutes); `None` keeps sessions until the client deletes them. A client with an open `GET`
76
-
stream or a request still being answered is never idle.
77
-
*`max_sessions`: how many such sessions one app holds at once. Defaults to 10 000; while that many
78
-
are open, a request that would open another gets HTTP 503. `None` removes the limit.
73
+
*`session_idle_timeout`: seconds a legacy session may sit with nothing in flight before the
74
+
server closes it. Default 1800. `None` disables it. See
75
+
[Session lifetime and limits](legacy-clients.md#session-lifetime-and-limits).
76
+
*`max_sessions`: how many legacy sessions one process holds at once. Default 10 000. `None`
77
+
removes the limit. Covered in the same section.
79
78
*`event_store`, `retry_interval`, `transport_security`: resumability and DNS-rebinding protection. They can wait, until you deploy somewhere other than localhost; **[Deploy & scale](deploy.md)** covers `transport_security`.
Copy file name to clipboardExpand all lines: docs/run/legacy-clients.md
+33-6Lines changed: 33 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,12 +56,39 @@ On one worker that is invisible. On two, it is the whole problem: a request that
56
56
events to a client reconnecting to the *same* session), not a session store. It never makes a
57
57
session reachable from another process.
58
58
59
-
The record is not kept forever. A client that ends its session (`DELETE`) frees it at once;
60
-
a session that has had no request in flight for `session_idle_timeout` seconds (default 1800; an
61
-
open `GET` stream or a request being answered counts as in flight) is closed, and its next request
62
-
gets the same `404` a stray ID gets, so the client has to `initialize` again. Each worker process
63
-
holds at most `max_sessions` of them (default 10 000) and answers `503` to a request that would
64
-
open one more. Both are `run()` / `streamable_http_app()` options.
59
+
## Session lifetime and limits
60
+
61
+
A legacy session does not live forever, and one process does not hold an unlimited number of
62
+
them. Two settings control this. Both are keyword arguments on `run()`, `streamable_http_app()`
63
+
and `Server.streamable_http_app()`. Modern (`2026-07-28`) connections and `stateless_http=True`
64
+
have no sessions, so neither setting applies to them.
65
+
66
+
| Setting | Default | What it does | What the client sees | Turn it off |
67
+
|---|---|---|---|---|
68
+
|`session_idle_timeout`|`1800` (30 min) | Closes a session that has had nothing in flight for that long. |`404 Session not found`. It has to `initialize` again. |`None`|
69
+
|`max_sessions`|`10_000`| Refuses to open a session beyond that many. Existing sessions are untouched and nothing is evicted. |`503 Too many open sessions` with JSON-RPC code `-32603`. |`None`|
70
+
71
+
What counts as "in flight":
72
+
73
+
* An open `GET` stream. The SDK clients keep one open, so a connected client's session never
74
+
expires.
75
+
* A request that is still being answered. A tool call that runs longer than the timeout is not
76
+
interrupted, and the countdown only starts once it finishes.
77
+
* Nothing else. Between requests the clock runs. Any request on the session restarts it,
78
+
`ping` included. Once a session has expired, nothing revives it.
79
+
80
+
A client that ends its session with `DELETE` frees it immediately. So does a client whose
The server does not recognise the `Mcp-Session-Id` your client sent, because the server **restarted** (or you were routed to a different instance), or because the session **expired**: a legacy session with no request in flight for `session_idle_timeout` (30 minutes by default; an open `GET` stream or a request being answered counts as in flight) is closed, as is one the client ended with `DELETE`. Sessions live in that one process's memory.
249
+
The server does not recognise the `Mcp-Session-Id` your client sent. Either the server **restarted** (or you were routed to a different instance), or the session **expired** because nothing was in flight for `session_idle_timeout`, which is 30 minutes by default. See [Session lifetime and limits](run/legacy-clients.md#session-lifetime-and-limits). Sessions live in that one process's memory.
250
250
251
251
There is no server bug to find. The HTTP response is a `404` whose body *is* JSON-RPC, so, unlike the `421` above, the python `Client` shows you this one verbatim:
0 commit comments