Skip to content

Sessions

Kelly Ferrone edited this page Sep 16, 2026 · 6 revisions

Sessions

Name your session, and the browser that belongs to that name is yours.

Every session is named, by you

There is no session id anywhere in this API. No tool takes one, no endpoint takes one, no result carries one. You say who you are and the server hands you the browser that name is holding.

How to name it Looks like Use it when
a URL parameter …/mcp?session=research-bot the usual case — one credential, each caller named in its own URL
a header X-Session-Key: research-bot an operator pins one session to one credential
stdio nothing to do one process serves one client, and it is named stdio

Sending both is a 400. Not a precedence one of them wins: a request carrying two names has two ideas about who is calling, and quietly picking one hides that from whoever wired it up.

Sending neither is a 400 on anything touching a browser, with a message saying how to fix it. The one exception is the flow library, which falls back to the shared global library — which everyone may read and run, and nobody may write.

The name has to be usable as a directory name, because it is one: it is where your flows and your kept files live. Letters, digits, dots, dashes and underscores, starting with a letter or digit, 64 characters at most. stdio and global are reserved.

n8n opens a new MCP transport for every tool call, so nothing the transport negotiates is ever the same twice. Name the session in the URL and it simply works. See Installing.

A session name is a credential. The bearer token is the only thing guarding it, so anyone who can call this server can name your session and drive your browser. Sharing one is deliberate and by name; doing it by accident is now easy, which is worth knowing.

open_session always comes first

Nothing opens a browser implicitly. That is deliberate: open_session is the only place a window size and the timeouts can be chosen, and hiding the call hid the settings with it.

Call it again later with the same name — after a reconnect, a client restart, a week — and you are back on the same browser at the same page.

Where am I?

session://current reports the session name, which browser it is running, the page it is on, whether one is open at all (live), whether you are inside a frame, and the window size. Reading it never opens a browser.

A client whose model cannot read MCP resources reads it with read_resource(uri="session://current"). Over HTTP it is GET /browser.

What happens when the browser goes away

The Grid reaps idle browsers on its own timeout, so a remembered id can name one that is already gone. The next call notices, reopens, and returns to the page it was last on with the same settings — the refresh is invisible.

"The same settings" includes which browser it was. That is why the choice is stored as a setting rather than beside them: a Firefox session that came back as Chrome would be exactly the silent change of shape the stored settings exist to prevent. session://current reports it as browser.

A session is not a browser

The session outlives the browsers it holds. live is false when it holds none — after the Grid reaps one, or an operator ends one from the admin UI — and that is an ordinary state.

What survives is the context: the browser choice, the window, the last page. So the recovery is always the same one call:

open_session()      # same browser, same window, back where you were

Which is also why you are never told "your browser was taken". You are told you have no browser, which is the branch you already handle.

Sessions themselves expire on SESSION_TTL, slid forward on every use — a day by default. Nothing else removes one.

There is one distinction that matters here, because getting it wrong strands browsers. A session that fails to answer is not necessarily gone:

The Grid says Means
200 healthy
500 unexpected alert open alive, and blocked by a dialog — answer it with dialog
404 invalid session id genuinely reaped

Only the last one counts as gone. Anything else, including a Grid that cannot be reached right now, is assumed alive: a wrong "alive" surfaces as a real error on the next call, while a wrong "dead" silently abandons a working browser and leaks its slot.

How long things last

Who owns it Typical
how long a browser lives the Grid — SE_NODE_SESSION_TIMEOUT 300s idle
how long a session is remembered SESSION_TTL 3600s, slid forward on every call
where that is remembered SESSION_STORE memory, or redis to share it

Nothing runs a cleanup loop, and nothing should — the Grid expires browsers, the store expires its own keys.

Always close

end_browser frees the slot, and slots are the scarce resource: the Grid runs a handful of browsers in total and an abandoned one holds its place until it is reaped. Close on failure paths too.

If you are mid-task and the caller may come back, leaving it open is the right call — just know you are holding capacity while you do.

Clone this wiki locally