Browsa

Browsa for Mac / Developers

Build for Browsa.

Everything runs inside WebKit, and everything is free. If you've made a browser extension, you already know most of it.

A plugin is an extension, plus a little more.

Add a browsa block to a normal Manifest V3 extension. Other browsers ignore it, so the same plugin still works anywhere else. Pick a part to see where it lands.

{
  "manifest_version": 3,
  "name": "Deploy Watch",
  "background": { "service_worker": "bg.js" },
  "browsa": {
    "sidebar": { "page": "sidebar.html" },
    "panel": { "page": "panel.html" },
    "commands": [{ "title": "Open the latest preview" }],
    "agentTools": [{ "name": "deploys_list" }],
    "badges": true
  }
}

Start buildingBrowsa reads the browsa block today, and its own plugins (Notes, Agents, Radar, Downloads) already plug into the window this way. The runtime that lets your plugin do the same is still being built, so details may change. When it opens up, plugins will be listed in the Library next to ours, and they'll be free.

Talking to Browsa.

Plugins send native messages to a built-in host, app.browsa. Each part of the browsa block is a permission people see before they add it, and plugins never run native code.

Your background scriptruntime.sendNativeMessage
app.browsaChecks the plugin's permissions
BrowsaUpdates the sidebar, panel or command bar
badge.setA count or dot on a tab
commands.registerRows in ⌘K
ui.toastA short message
tabs.openOpen a page in a space
agentTool.resultAnswer an agent's call
notes.listRead page notes

Let agents use the real browser.

Switch on the Agent Pack in the Library, or press Turn On Agents in the Agents panel, and Browsa listens on 127.0.0.1 for MCP. Add to Claude Code sets it up in one click, token and all. For Codex, Cursor or anything else that speaks MCP over HTTP, Connect an Agent gives you the lines to paste.

tabs_listtab_openpage_navigatepage_readpage_screenshotpage_clickpage_typepage_evalnotes_listnotes_addnotes_replynotes_resolve
  • Visible. Agents open pages in their own Agent space, with a yellow pointer and a caption on the page. Each call shows in the Agents panel as a sentence.
  • Your tabs ask first. Anything in the Agent space runs without a prompt. Using one of your own tabs, or listing them, asks you until Browsa quits. page_eval only runs on localhost.
  • Stoppable. Stop, in the sidebar or the panel, refuses everything that changes a page until it's resumed.
  • Local only. A bearer token is required, and requests carrying a web page's Origin are refused.
# What Add to Claude Code runs for you
$ claude mcp add --scope user --transport http browsa \
    http://127.0.0.1:47913/mcp \
    --header "Authorization: Bearer <your token>"
✓ Added browsa

> open localhost:3000 and check the sign-in form
● tab_open      localhost:3000
● page_read     Sign in · 2 fields
● page_type     email
● page_click    Continue

How Chrome extensions run.

Browsa installs extensions from the Chrome Web Store on Apple's WKWebExtension, checks for updates once a day, and asks again when an update wants more permissions. A small compatibility layer goes in front of each one, without changing the extension's own files.

Filled in for you

WebSocketReal sockets in service workers, which WebKit would freeze
navigator.locksLocks inside the worker
chrome.offscreenHidden documents for copying and translating
Keep-aliveBackgrounds aren't paused or unloaded
Chrome APIsStand-ins for the ones WebKit lacks, so startup doesn't throw
User agentSays Chrome inside the extension only

Limits to design around

Desktop-app connections
Unlocking with a password manager's desktop app needs that app to trust Browsa, and those helpers only trust a signed list of browsers. Extensions work on their own instead: 1Password signs in inside the extension.
Stand-ins, not features
notifications, downloads, side panels and identity.launchWebAuthFlow answer but do nothing yet. Each can become a real Browsa feature when an extension needs it.
No API wrapping
Wrapping chrome.* in a JS Proxy stops runtime messages reaching the background, so missing APIs are added as plain objects.
Not supported
chrome.debugger, blocking webRequest and DevTools panels. declarativeNetRequest works.

Where it lives in the code.

Browsa is MIT licensed. The full write-up is docs/extensibility.md.

PieceWhere
The Library and the verified listMac/Sources/Library/, docs/library.md
Store links, updates, CRX unpackingExtensibility/ExtensionSources.swift
The Chrome compatibility layerWebKitEngine/ExtensionCompat.swift
Manifest and the browsa blockExtensibility/ExtensionManifest.swift
Tools: panel, sidebar, commandsMac/Sources/Workbench/
MCP toolboxExtensibility/AgentToolbox.swift
Local endpoint and Agent spaceMac/Sources/Agents/
Page notesExtensibility/Margin.swift

jmaorr/BrowsaChrome extension notes