Skip to content

fix: make Browserbase sessions re-attachable by ID (STG-2446)#195

Open
sameelarif wants to merge 1 commit into
mainfrom
sameelarif/stg-2446-escalation-3-seperate-users-me-mcp-fails-in-chatgpt
Open

fix: make Browserbase sessions re-attachable by ID (STG-2446)#195
sameelarif wants to merge 1 commit into
mainfrom
sameelarif/stg-2446-escalation-3-seperate-users-me-mcp-fails-in-chatgpt

Conversation

@sameelarif

@sameelarif sameelarif commented Jul 1, 2026

Copy link
Copy Markdown
Member

Important

Re: STG-2446 escalation. The "No active session" failure in the ChatGPT Workspace is served by the hosted MCP in browserbase/core (apps/stagehand-api-v3/mcp/), which does not use this OSS package. The escalation fix (reattach-by-sessionId, ported into the code that actually serves mcp.browserbase.com) is browserbase/core#10363, validated on local dev.

This PR remains valuable as self-hosted / OSS parity for @browserbasehq/mcp, but it does not affect the hosted endpoint. Recommend unlinking it from STG-2446 so it doesn't auto-close the escalation on merge.


Problem

Users hit "No active session" when driving the Browserbase MCP server from ChatGPT: start creates a session successfully, but the follow-up navigate fails even though the session is live.

Root cause: browser-session state was connection-scoped and unrecoverable.

  • No tool accepted a session identifier. navigate/act/observe/extract all resolved their target via context.getStagehand()SessionManager.activeSessionId.
  • The only thing linking startnavigate was the in-process SessionManager staying alive across MCP calls, keyed by the Mcp-Session-Id transport header.
  • On the hosted, autoscaled server (mcp.browserbase.com), a follow-up request can land on a different replica — or a client can rotate/drop the Mcp-Session-Id. Either way the call hits a fresh SessionManager with no record of the session start created, and getStagehand throws No session found for ID: … → surfaced to ChatGPT as "No active session."
  • The reconnect capability already existed (createNewBrowserSession(..., resumeSessionId)) but was dead code — never called with a resume ID and never exposed through any tool.

Fix

Make sessions re-attachable by Browserbase session ID so correctness no longer depends on sticky routing:

  • start accepts an optional sessionId to resume an existing Browserbase session (returns the id as before).
  • navigate / act / observe / extract accept an optional sessionId, passed through to context.getStagehand(sessionId). Omitting it preserves the existing active-session behavior.
  • SessionManager.getSession reconnects to an unknown non-default session by ID (via the existing browserbaseSessionID resume path) instead of returning null, so any replica can rehydrate the session the client holds.

Verification

  • tsc --noEmit, eslint, and pnpm build all clean; vitest run — 17/17 pass (added schema coverage for the new sessionId param).
  • Ran the HTTP server locally and reproduced the ChatGPT failure mode: start on transport A, then navigate on a fresh transport B (no knowledge of A) with sessionId set. Server logs confirm the reconnect path:
[SessionManager] Session 78d08715-… not tracked locally, attempting to reconnect to Browserbase session.
[SessionManager] Resuming Stagehand session 78d08715-…
[SessionManager] Session created and active: 78d08715-…

Navigation succeeded where it previously threw "No session found for ID".

Note

This removes the dependency on sticky routing for correctness, but the hosted transport's streamableSessions map is still per-process in-memory. Pairing this with sticky sessions or a shared session store at the infra layer (outside this repo) is still recommended so start-created sessions aren't stranded on one replica.

🤖 Generated with Claude Code

Tool calls carried no session identifier, so navigate/act/observe/extract
relied entirely on the in-process SessionManager's activeSessionId being
preserved across MCP calls. On the hosted, multi-replica server (or when a
client rotates/drops the Mcp-Session-Id) a follow-up call lands on a fresh
SessionManager that has no record of the session created by `start`, and
getStagehand throws "No session found for ID" — surfacing to clients like
ChatGPT as "No active session".

- start now accepts an optional sessionId to resume an existing Browserbase
  session (via the already-present resumeSessionId path).
- navigate/act/observe/extract accept an optional sessionId, passed through
  to context.getStagehand(sessionId).
- SessionManager.getSession reconnects to an unknown non-default session by
  ID (browserbaseSessionID resume) instead of returning null, so any replica
  can rehydrate the session the client holds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant