Skip to content
overterm
Documentation

Browser and MCP

A browser column per terminal tab, and the MCP server that lets the agent in that tab drive it.

A browser beside the terminal, so a dev server’s page, its console and the agent driving it are in the same window as the code. It is the system’s WebKit — a WKWebView per page, nothing bundled.

⇧⌘B opens and closes the column, as does the globe in the title bar and View ▸ Browser. Settings ▸ Browser ▸ A browser beside the terminal (browser.enabled, on by default) turns the whole thing off: no column, and links open in your own browser instead.

A column per terminal tab

Each terminal tab has its own browser with its own pages, shown as a tab strip under the toolbar. The column shows the selected terminal tab’s browser; switching tabs switches what the browser shows.

A background tab’s pages are parked in an off-screen window rather than suspended, so they keep running, timers keep firing, and an agent’s screenshot of a page in a background tab is not blank.

target=_blank and window.open open a new page in the same strip, built so the opener keeps its handle on it.

Keys while the browser has the keyboard

Key What it does
⌘T New page, focusing the address bar
⌘W Close the page
⌘L Focus the address bar
⌘R Reload
⌘[ ⌘] Back, forward
⌃⇥ ⌃⇧⇥ Next, previous page, wrapping
⌘1…⌘9 Go to page N — ⌘9 is the last page

In the address bar, ↩ navigates and hands focus to the page; ⎋ puts the current URL back and hands focus to the page.

The address bar takes more than URLs. :3000 becomes http://localhost:3000, a bare host gets a scheme, and anything that is not host-shaped becomes a DuckDuckGo search.

Storage, and the Web Inspector

Cookies and storage use WebKit’s default data store, so they persist and are shared by every page in every tab. (An isolated end-to-end run gets a non-persistent store, so tests never touch your browsing data.)

The hammer in the toolbar opens the Web Inspector; right-clicking a page offers Inspect Element. Developer extras are enabled for these views, so the inspector is the real one.

The console strip

An injected script reports every console.log, info, warn, error and debug call, plus uncaught errors and unhandled promise rejections. The strip across the bottom of the column counts them — “✕ 3”, “⚠ 1”, or “12 logged” — and clicking it opens a 160 pt log panel. A trash button clears it. Up to 500 messages are kept per page, each truncated at 4000 characters, and the buffer is cleared when the page navigates.

Reload on save

Settings ▸ Browser ▸ Reload the page when a file is saved (browser.reloadOnSave, off by default) reloads a local page when you save in the editor, or when the checkout’s files change under an agent (the latter debounced, so a burst of edits is one reload). It is for a server without hot reload; with one, leave it off.

Settings ▸ Browser ▸ Links clicked in the terminal open (browser.openLinks):

Value Behaviour
local (default) Here when the address is on this machine, else in your own browser
all Here, always
none In your own browser, always

“On this machine” means localhost, 127.0.0.1, 0.0.0.0, ::1, the .localhost, .local and .test suffixes, and the private IPv4 ranges (10/8, 127/8, 192.168/16, 172.16–172.31). The same policy applies to a link ⌘-clicked in the terminal, a port opened from Tab Info, and a port pill in a pane header. Anything that is not a web URL — mailto:, tel:, an app scheme — always goes to the system.

With no page open, the column offers the tab’s own listening ports as buttons, labelled with the port and the process holding it.

Ask an agent about the page

The ✦ button sends the page to an agent the same way ⌘L sends a selection: its title, its URL, its console errors and a screenshot written to a temporary file. The sheet is the same one, with an extra target for a new agent tab in the current folder. See Ask an agent.

The MCP server

Claude Code started from Overterm can drive its own tab’s browser. The mechanism is an MCP server Overterm runs itself.

Transport Streamable HTTP — JSON-RPC over HTTP POST, JSON answers
Bound to 127.0.0.1 only, on a random port
Authentication Authorization: Bearer <token>; a fresh random token per launch. Without it, 401
Path one per pane: /mcp/<the pane's OVERTERM_TAB_ID>
Server name overterm-browser

The per-pane path is the whole point: an agent reaches the browser of the tab it is running in, and no other.

Each pane gets OVERTERM_TAB_ID in its environment, and a config file written for it at ~/Library/Application Support/Overterm/mcp/<tab id>.json, whose path is exported as OVERTERM_MCP_CONFIG:

{
  "mcpServers": {
    "overterm-browser": {
      "type": "http",
      "url": "http://127.0.0.1:<port>/mcp/<tab id>",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

The file is written with permissions 0600 — the token is a key to the browser, and it is yours alone. The mcp directory is emptied when the server starts, and a pane’s config is deleted when its tab closes.

Overterm then starts Claude with that config:

claude --mcp-config "$OVERTERM_MCP_CONFIG"

Nothing is added to Codex’s command line, and nothing is written into your own Claude configuration.

The tools

Thirteen, named so they read in a tool list:

browser_navigate · browser_snapshot · browser_click · browser_type · browser_screenshot · browser_console · browser_evaluate · browser_back · browser_reload · browser_tabs · browser_tab_new · browser_tab_select · browser_tab_close

A snapshot returns the page’s text and its controls, each with a short ref (e1, e2…) that the click and type tools take. Refs are only valid until the next snapshot, and the error message says so. After a click, a type, a back or a reload the server waits for the page to settle and returns a fresh snapshot, so the agent does not have to ask twice. A screenshot comes back as a PNG.

Turning it off

Settings ▸ Browser ▸ Agents can use the browser (browser.agentControl, on by default) is the switch. Off, the server is never started, no config file is written, OVERTERM_MCP_CONFIG is not in any pane’s environment, and --mcp-config is not added to claude. The browser column itself is unaffected.

See Privacy for what this does and does not expose.