Browser Session MCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| STATE_DIR | Yes | Directory where the server keeps sessions, logs and saved logins. | |
| BROWSER_URL | Yes | The URL of the Chrome DevTools Protocol endpoint (e.g. http://localhost:9222) that the server drives. | |
| TAKEOVER_BASE_URL | No | Base URL used to build human-takeover links. Needed by the `takeover` role for the human takeover tools. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| open_browser_sessionA | Open a new isolated browser session and return its sessionId; call this first, then pass the id to every other tool. Each session is its own Chrome BrowserContext with separate cookies, storage and tabs, so sessions never see each other, and it starts with one blank tab. Don't open a new session per page: reuse one and use new_page for more tabs. To start logged in, follow with load_browser_state before navigate. Set |
| close_browser_sessionA | Close a browser session for good: closes all its tabs, discards its cookies and storage, and deletes its console and network logs and saved .cpuprofile files. Call it when the task is done so the session doesn't sit open until the reaper closes it. Use close_tab instead to close one tab and keep the session, and save_browser_state first if the login should outlive the session. Idempotent: closing an unknown or already-closed session succeeds, so it is safe in cleanup code. Returns a confirmation. |
| list_browser_sessionsA | List every open browser session on the shared Chrome, sorted by sessionId, including ones opened by other agents; it takes no arguments. Use it to recover a lost sessionId or to find sessions left open; don't use it to see the tabs inside one session (list_pages) or saved logins (list_browser_states). activeUrl is null for a session with no tabs. The list is empty if none are open. |
| new_pageA | Open a new tab in the session and make it the active tab, so later navigate/click/type/screenshot calls act on it. Use navigate instead to load a URL in the current tab, and switch_tab to go back to an existing one. Tabs stay open until close_tab or close_browser_session, so close ones you no longer need. With |
| list_pagesA | List the session's tabs in a stable order; it changes nothing. The active tab (the one navigate/evaluate/screenshot/click target by default) is marked with |
| switch_tabA | Make the tab at |
| close_tabA | Close the tab at |
| navigateA | Load a URL in the session's active tab and return once the page's load event fires. Use new_page instead to keep the current page and load the URL in a new tab, and wait_for after this when content arrives after the load event. If every tab was closed, a new one is opened. |
| take_screenshotA | Capture a PNG of the session's active tab and return it as an image. Use it when visual layout, styling or images matter; use take_snapshot for a cheaper text view of the page's structure, and evaluate to read specific values. |
| take_snapshotA | Return the accessibility tree of the session's active tab as indented text: each node's role and name (links, buttons, headings, form fields and their labels, and current values), nested by structure. Use it to find what's on the page and what to click or type into, far more cheaply than take_screenshot; use take_screenshot when visual layout matters, and evaluate for data not shown as text. It reads the page without changing it and returns the whole tree, so a large page gives long output. |
| clickA | Click in the session's active tab with real CDP mouse input (trusted events — fires handlers reliably and grants user activation, so it can open popups/new tabs that a JS click can't). Pass a CSS |
| typeA | Type text into the first element matching a CSS selector in the session's active tab: waits up to |
| press_keyA | Press a key via CDP keyboard input in the session's active tab. |
| scrollA | Scroll the session's active tab with a real mouse-wheel event at a viewport position; whatever scrollable element is under ( |
| move_mouseA | Move the mouse to viewport coordinates |
| wait_forA | Wait until a CSS selector matches an element, or a text string appears in the page's visible text, in the session's active tab, polling every 100ms. Use it after navigate, click or scroll when content loads asynchronously, instead of sleeping. Pass |
| evaluateA | Run JavaScript in a tab and return its value as JSON in { result }. A bare expression returns its value; for several statements use |
| start_profilingA | Start the V8 CPU profiler on a tab (active tab by default, or |
| stop_profilingA | Stop the CPU profiler started by start_profiling, save a .cpuprofile under $STATE_DIR/logs//profiles/ (its |
| list_visitsA | List the session's page visits, oldest first. Each visit is one top-level navigation in one tab, numbered by |
| list_console_messagesA | List console messages (console.log, warnings, errors, uncaught exceptions) the session's pages emitted, oldest first, from the most recent |
| list_network_requestsA | List the network requests the session's pages made, oldest first, from the most recent |
| set_stealthA | Toggle stealth for a session. |
| get_stealthA | Return whether stealth is on for a session; it changes nothing. Check it before evaluate or list_console_messages, whose behavior it changes; use set_stealth to change it. A new session starts with stealth off. |
| request_human_takeoverA | Hand the session's active page to a human to log in themselves (passwords/passkeys the agent must not see). Use it only when a login needs a person; for a site you've logged into before, try load_browser_state first. Show the returned url to the user and keep the token for await_human_takeover. Does NOT block. STRONGLY ADVISED: after showing the URL, run await_human_takeover in a BACKGROUND task (subagent/job) — not your main loop — because the human may take many minutes and you should stay responsive. The URL is a bearer credential that gives live control of the tab until |
| await_human_takeoverA | Wait for the human to click Done in the takeover page. |
| save_browser_stateA | Save the session's cookies under a name so a future session can load them with load_browser_state and resume without logging in again. Call it after a successful login (for example once await_human_takeover completes). Saving under an existing name replaces that state, so use a distinct name per site or account. Only cookies are saved, not localStorage, so sites that keep their login in localStorage won't resume. States are stored on disk as plaintext and include session credentials. Fails on a name with characters other than letters, digits, '.', '_' and '-'. |
| load_browser_stateA | Load cookies saved by save_browser_state into this session, usually right after open_browser_session and before navigate, so the session starts logged in. Pages already open need a reload or navigate to use them. Loaded cookies replace any with the same name, domain and path; others are kept. Expired logins won't work again; if the site still asks to log in, use request_human_takeover and save again. Fails if no state has that name; list_browser_states shows the names. |
| list_browser_statesA | List the saved browser states (cookie sets saved by save_browser_state) that load_browser_state and delete_browser_state accept, sorted by name; it takes no arguments and changes nothing. Use it to pick a name to load; these are saved logins on disk, not open sessions, so use list_browser_sessions for those. The list is empty if nothing has been saved. |
| delete_browser_stateA | Delete a saved browser state by name, permanently; open sessions that already loaded it keep their cookies. Use it to forget a login that is stale or no longer wanted; to update a state, call save_browser_state with the same name instead. Use list_browser_states to see the names. Deleting a name that doesn't exist succeeds with removed false, so it is safe to repeat. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 30 tools
Every tool has a sharply distinct purpose, and the descriptions explicitly cross-reference siblings to disambiguate (navigate vs new_page, list_pages vs list_browser_sessions vs list_visits vs list_browser_states, take_screenshot vs take_snapshot vs evaluate, click vs evaluate-based clicks). Even the closely-related logging and state tools each carve out a clear, non-overlapping role. An agent could reliably select the right tool without guessing.
Names are uniformly snake_case and mostly use a predictable verb_noun pattern (open_browser_session, close_tab, list_pages, take_screenshot, save_browser_state, request_human_takeover). A few break the pattern as bare verbs (navigate, click, type, scroll, evaluate) and 'new_page' should arguably be 'open_page' for symmetry with open_browser_session/close_tab. Still highly readable and internally coherent.
30 tools is heavy and stretches past a comfortable surface, though the breadth reflects genuine scope (sessions, tabs, interaction, perception, logging, profiling, stealth, takeover, cookie state). Each tool earns its place individually, but the set is large enough that an agent must navigate many options for a single task. Borderline over-scoped rather than well-scoped.
Covers the core lifecycle well: session/tab management, navigation, trusted input, multiple perception modes, JS eval, console/network logs, profiling, stealth, human takeover, and cookie persistence. Gaps remain around browser history (back/forward), file upload/download, drag, iframe targeting, and non-cookie storage (localStorage/auth beyond cookie snapshots), but these are minor workarounds rather than dead ends.