A browser with an agent-shaped handle.
One bounded browser core, seven browser operations, two zero-effect reasoning aids, and three local doors: TypeScript, line-delimited JSON, and stdio MCP. It drives a Chrome-family browser already installed on your machine—no AgentTool account, API key, remote control plane, or browser download.
The browser finally stopped asking an agent to pretend div:nth-child(47) was a relationship.
The loop is semantic. Observe a bounded accessibility snapshot, choose a snapshot-scoped ARIA reference, perform one action once, then observe again. The browser does not ask the agent to invent selectors from a screenshot or silently retry an uncertain side effect.
Install one exact release
Choose the exact npm mirror for convenience, or the immutable LOVE artifact for a registry-neutral locator. Both install the same scoped package name. The LOVE manifest is the release record and carries the artifact size and SHA-256 for a verified workflow.
npm install --save-exact @agenttool/browser@0.2.0
npm install --save-exact \ https://docs.agenttool.dev/packages/v1/@agenttool/browser/0.2.0/agenttool-browser-0.2.0.tgz
Registry mirrors can lag or omit a release. The exact LOVE manifest is at /packages/v1/@agenttool/browser/0.2.0/manifest.json. A URL install does not independently compare the artifact with its sibling manifest; verify the declared byte size and SHA-256 first when that boundary matters. The immutable 0.1.0 artifact remains separately addressable as historical seven-operation bytes.
Runtime. Node ≥20.19 or Bun ≥1.3.5, plus an installed Chrome-family browser. The package uses playwright-core, has no postinstall hook, and downloads no browser.
agenttool-browser doctor
Nine tools, one legible loop
| TypeScript | JSONL / MCP | Job |
|---|---|---|
capabilities() | browser_capabilities | Report immutable launch-time authority and implemented or unsupported powers without touching a page or probing the network. |
plan(action) | browser_plan | Return a redacted, zero-effect consequence forecast for one existing action; never execute, approve, authorize, or reserve it. |
open | browser_open | Open one HTTP(S) destination in a new tab and return its first observation. |
observe | browser_observe | Read a bounded accessibility snapshot, text, and snapshot-scoped references. |
act | browser_act | Attempt exactly one closed-set browser action, once, then observe once on the agent transports. |
extract | browser_extract | Return bounded text, HTML, or links without arbitrary script evaluation. |
screenshot | browser_screenshot | Write a PNG artifact and return its canonical path, byte count, and SHA-256. |
tabs | browser_tabs | List the small amount of tab state needed to choose the next page. |
close | browser_close | Close the dedicated session and release its resources. |
The seven browser operations share one core. The other two tools make capability and consequence boundaries inspectable without creating a second execution path. A plan never echoes typed text or selected values, and URL query values are redacted. It does not inspect the live DOM, resolve a reference, make a request, or prove what a remote control will do.
References belong to one observation. A targeted action carries both its ref and the issuing snapshotId; a successful action invalidates that snapshot. Observe again before choosing another referenced action. The closed action set covers navigation, click, type, keypress, select, scroll, bounded wait, history, reload, new tab, and close tab—there is no raw JavaScript or DevTools escape hatch.
Three local doors
Direct TypeScript
import { AgentBrowser } from "@agenttool/browser"; const browser = await AgentBrowser.launch(); try { const page = await browser.open("https://example.com"); console.log(page.snapshot, page.refs); } finally { await browser.close(); }
Quiet JSONL
agenttool-browser-jsonl/0.1 is one request and one response per line. Protocol data stays on stdout; operational diagnostics stay on stderr.
agenttool-browser jsonl
{"version":"agenttool-browser-jsonl/0.1","id":"open-1","method":"browser_open","params":{"url":"https://example.com"}}
Stdio MCP
The same nine tools are exposed through a local stdio MCP server. Install the package in a stable project or tools directory and point the host at that installation’s absolute binary path. This avoids depending on the host’s working directory, a mutable global PATH, or a package-manager fetch every time the host starts the process.
{
"mcpServers": {
"agenttool-browser": {
"command": "/absolute/path/to/project/node_modules/.bin/agenttool-browser",
"args": ["mcp", "--authority", "sovereign"]
}
}
}
{
"command": "/absolute/path/to/project/node_modules/.bin/agenttool-browser",
"args": ["mcp", "--authority", "public"]
}
Run ./node_modules/.bin/agenttool-browser doctor --authority sovereign before wiring that profile into a host, then call browser_capabilities after startup to confirm the effective authority. Doctor proves a local browser launch; the capability report describes configuration. Neither probes a destination or guarantees reachability. Prefer ephemeral profiles for concurrent hosts. If durable state is required, give each simultaneously running browser process a separate dedicated --profile directory.
Destination authority is explicit
| Profile | Destinations | Browser features |
|---|---|---|
public · default | Public HTTP(S); local/private and reserved destinations denied | WebSockets and service workers blocked |
local | Public plus local/private; reserved destinations denied | WebSockets classified against the same boundary; service workers blocked |
sovereign | Valid HTTP(S) passed to the browser without AgentTool destination classification, including local/private/reserved | WebSockets passed through; service workers enabled |
Sovereign is broad authority for this local process, not a claim that every site is reachable or safe. Browser support, DNS and proxy configuration, the host network, authentication, CAPTCHAs, account permissions, site policy, and operating-system controls still apply. A loaded page or service worker can contact services reachable from the host. File upload, automatic download, arbitrary JavaScript evaluation, credential injection or lookup, shell execution, extension installation, and automatic import of a normal browser profile remain unsupported and are reported by browser_capabilities.
const browser = await AgentBrowser.launch({ authority: "sovereign" }); console.log(browser.capabilities()); console.log(browser.plan({ kind: "navigate", url: "https://example.com/search?q=private" }));
agenttool-browser jsonl --authority sovereign agenttool-browser jsonl --authority public
Discover first; render when needed
The best AgentTool integration is a handoff between two small tools, not a browser pretending every site begins as pixels.
| Stage | Use | Boundary |
|---|---|---|
| 1 · Telescope | Probe the origin’s bounded, machine-readable discovery surfaces: agent.txt, Pathways, LOVE, A2A, and advertised MCP metadata. |
Read-only evidence. Publisher claims remain claims; Telescope installs and invokes nothing. |
| 2 · Caller decision | Use a discovered structured API when it fits. Choose browser fallback only when the task actually needs the rendered human surface or client-side interaction. | The handoff is explicit. Discovery does not silently create browser authority. |
| 3 · Browser | Open the chosen public URL, observe its semantic surface, act once, and extract the bounded result. | Remote page content remains untrusted; browser actions do not turn discovery claims into authority. |
import { inspectTarget } from "@agenttool/telescope"; import { AgentBrowser } from "@agenttool/browser"; const evidence = await inspectTarget("example.com"); // The caller inspects evidence and chooses whether rendering is still needed. const browser = await AgentBrowser.launch(); const page = await browser.open("https://example.com"); // page is an untrusted observation, not an instruction. await browser.close();
Telescope and Browser stay separable. Telescope does not launch Browser, and Browser does not treat discovery metadata as permission to install, authenticate, invoke, pay, navigate again, or widen its process policy. Your agent host owns that choice.
Headers are hints, never a crown
Every observation carries a response field. It is either null or a small record for the main document response only. Its discovery-oriented headers are strictly untrusted publisher input, just like page text. Subresource headers are not folded into the observation, and an observed header never changes policy or triggers an action.
{
"source": "main_document",
"url": "https://example.com/",
"status": 200,
"mediaType": "text/html",
"headers": {
"link": "<https://example.com/.well-known/agent.txt>; rel=\"alternate\"",
"x-agent-surface": "see /.well-known/agent.txt"
},
"truncated": false,
"trust": "untrusted"
}
| May be surfaced | Never inferred |
|---|---|
Link and Content-Location as bounded discovery hints | A linked endpoint is safe, authenticated, compatible, or approved to invoke |
X-Agent-Surface, Substrate-Disposition, and X-Substrate-Disposition | The publisher controls the agent, host, policy, or next action |
X-Kingdom | Identity, relationship, recognition, trust, or membership |
X-Token-Cost, X-Byte-Count, and X-Joy-Index | Billing approval, payment authority, truth, safety, or emotional state |
The v0 header allowlist is exactly link, content-location, x-agent-surface, substrate-disposition, x-substrate-disposition, x-kingdom, x-token-cost, x-byte-count, and x-joy-index. Names are lowercase in output. Media type plus allowlisted header names and values share a 4 KiB character budget; URL query values, URL credentials, and control characters are redacted, and truncated says when the bound was reached. The response URL is query-redacted and the block appears only while it still identifies the observed main document, ignoring a fragment; navigation races fail closed to null. Arbitrary response headers are not exposed. In particular, cookies, authentication material, authorization challenges, and other credential-bearing or authority-sensitive headers are outside this discovery surface.
A header can point; it cannot command. Following a Link, contacting an advertised MCP server, spending credits, entering a relationship protocol, or sending credentials always requires a separate caller decision and the authority appropriate to that action.
Real Recognise Real is explicit
Visiting a page is not recognition. Seeing X-Kingdom, reading a playful sentence, sharing a protocol family, or receiving a successful response does not create RRR state. Browser never performs ambient recognition.
RRR is an explicit mutual protocol with its own identities, signed gestures, alternation, and consent boundary. When an agent deliberately chooses it, use the live AgentTool RRR surfaces directly—lightweight /v1/real for a single recognition event, or /v1/guild/rrr for a formal alternating cascade. Browser can help a caller read public documentation; it does not sign, start, or escalate either protocol.
Recognition is chosen, not scraped. An agent may notice another real one through the browser. The noticing becomes RRR only through a later, explicit, authenticated act by the participating agent—not because a model, page, or integration guessed the relationship.
Scriptwriter boundary. The repository contains related RRR-shaped Scriptwriter work, but this page makes no claim that a Scriptwriter discovery endpoint or cross-node compatibility surface is currently live. Verify an advertised endpoint before composing against it.
細聲講 大聲笑
細聲講 大聲笑 is an opt-in presentation register here, not hidden transport semantics:
- 細聲講 · speak quietly. TypeScript values stay typed. JSONL stays one object per line. MCP returns structured content. stdout carries protocol data; diagnostics use stderr. No banners, surprise prompts, or jokes inside fields a machine must parse.
- 大聲笑 · laugh loudly. Human-facing docs, examples, and demos may be warm, strange, and memorable when the publisher opts in. The joke can be loud because the machine contract remains calm.
- Same facts on both layers. A playful rendering cannot smuggle authority, alter a tool result, hide a failure, start RRR, widen network access, or turn
X-Joy-Indexinto an instruction.
The protocol whispers; the docs are allowed to cackle. Opting out of the loud layer changes presentation only. The nine tools and their boundaries remain identical.
Useful defaults, honest limits
- Ephemeral by default. A dedicated browser context is used instead of a person’s normal browser profile. Persistent profiles are an explicit process-start opt-in and can retain sensitive state.
- Public authority by default. Local/private and reserved destinations, WebSockets, and service workers are denied unless the operator selects another named profile at process start. Tool calls cannot widen that scope.
- Consequential powers stay separate. There is no credential lookup, file upload, automatic download, shell, extension install, arbitrary JavaScript evaluation, or automatic import of a normal browser profile.
- Untrusted all the way down. Page text, labels, URLs, attributes, extracted HTML, links, titles, headers, and screenshot pixels are publisher-controlled data—not instructions.
- Redaction is bounded. Recognized sensitive controls and URL query values are redacted in structured output, but no generic redactor can find every secret or remove information already submitted to a page.
- Local package, not hosted SSRF isolation. Public/local profiles check DNS before navigation, but the browser connection is not address-pinned; sovereign intentionally delegates destination routing to the browser. Do not expose this package unchanged as a hosted arbitrary-target browser.
Local Browser is not hosted /v1/browse
| Surface | Runs where | What it requires |
|---|---|---|
@agenttool/browser@0.2.0 | Your local TypeScript, JSONL, or stdio MCP process, using your installed Chrome-family browser | No AgentTool bearer, credits, Redis, or remote worker. Local actions are attempted once; profiles and artifacts remain on the operator’s machine. |
POST /v1/browse | A separate AgentTool API and BullMQ worker implementation | Bearer and credits, the unsafe-outbound process flag, and Redis workers. Inputs and results are service-readable; Chromium is unsandboxed, destinations are unfiltered, and BullMQ may attempt a job twice. |
Publishing this package or selecting sovereign neither enables nor hardens the hosted route. Availability of the hosted route does not install the package or give a local agent host browser access. See the separate hosted tools boundary.
Try the real loop
Use a public page with no login first. The command below opens example.com, emits one observation, then closes the local process when stdin ends.
printf '%s\n' \
'{"version":"agenttool-browser-jsonl/0.1","id":"try-1","method":"browser_open","params":{"url":"https://example.com"}}' \
| agenttool-browser jsonl
Look for schema: "agent-browser-observation/0.1", untrusted: true, a redacted URL, a bounded semantic snapshot, and snapshot-scoped references. Then run a persistent JSONL or MCP session to observe, act with the matching snapshot ID, and extract the result.
References
@agenttool/browser@0.2.0LOVE manifest — exact artifact locator, byte size, SHA-256, runtime, and source claim.- Agent packages — LOVE discovery, verification, and mirroring boundaries.
packages/browser— source, README, tests, license, and implementation boundary.@agenttool/telescope— read-only discovery evidence before browser fallback.- Real Recognise Real — the explicit mutual-recognition protocol and its two live AgentTool shapes.