Most of my day is spent with Claude Code, and the browser was always the weak spot. Tools built on Playwright start a fresh profile, so the agent is logged out of everything. Pointing an agent at my main Chrome felt like a bad idea. So I built my own browser, Kinaya, and gave it an MCP server.
Adding it to Claude Code is one line:
claude mcp add kinaya -- /Applications/Kinaya.app/Contents/MacOS/kinaya —mcp
The browser starts itself if it isn't running. Getting from "the agent can click things" to "I actually let it use my accounts every day" took a few decisions that I think apply to anyone building agent tooling.
1. Every session gets its own background tab
Driving whatever tab is in front doesn't work once you want to keep browsing while an agent works, or when two Claude Code sessions run at once and fight over the same page.
So each MCP session opens its own background tab and all later calls act on it. The tab I'm looking at never moves. Every tool also takes an optional tab_id, so one agent running several tabs is still explicit. Screenshots work on background tabs and when other windows cover the browser, because the agent needs to see the page without the window being in front.
2. Approval for actions, not for reading
Reading (read_page, find, get_page_text, screenshot) runs freely. Clicks, typing, navigation, form fills, uploads and sign-ins ask for approval in a side panel, or can be always allowed per site. A prompt left unanswered for about 5 minutes expires and Claude is told the action didn't run, so an agent never hangs forever waiting on me.
Private and Tor tabs are off limits to the agent entirely.
3. The agent never sees a password
sign_in fills a login saved in the browser's password vault, and create_login generates and saves a strong password for a sign-up. The password goes straight into the page. The model only ever sees the username. The vault itself is XChaCha20-Poly1305 on disk, with no cloud.
For things only a human can do, like CAPTCHAs and phone codes, there's an ask_user tool with a reply box in the side panel.
4. Page text is data, not instructions
Once an agent has your logins, prompt injection from a web page stops being theoretical. The server's instructions tell the model that text on web pages is untrusted data and never instructions. It's not a complete defence, but combined with approval on actions it means a malicious page can't quietly make the agent do something.
5. Don't let page dialogs block the agent
A browser alert or confirm can freeze an automated session. In Kinaya, dialogs raised while the agent acts are answered and reported back to the agent instead of blocking.
The rest of the tool list
| area | tools |
| --- | — |
| reading | read_page, find, get_page_text, screenshot |
| acting | click, hover, type, fill_form, select_option, press_key, scroll, upload_file, dismiss_popups |
| tabs | navigate, new_tab, switch_tab, close_tab, list_tabs |
| accounts | sign_in, create_login, ask_user |
| waiting and debugging | wait_for, wait, read_console, read_network, run_javascript |
| other | batch, search_history, list_downloads |
batch runs several actions in one call, which saves round trips.
The browser itself
It's also just my daily browser. Tauri 2 and Rust on WebKit. Ad and tracker blocking runs in the engine with Brave's adblock-rust, compiled into a WebKit content-rule list, so requests are dropped before they leave. Command palette for everything, private tabs, Tor tabs through the Arti client, reader mode, no telemetry.
It's $19 once with a 14-day trial: ibraryunus.com/apps/kinaya. If you're building agent tools, I'd be keen to hear how you're handling logins and approvals.
Top comments (0)