◎ @agenttool/browser · local-first · 0.2.0

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 · exact registry mirror
npm install --save-exact @agenttool/browser@0.2.0
LOVE · exact immutable HTTPS artifact
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.

check this machine
agenttool-browser doctor

Nine tools, one legible loop

TypeScriptJSONL / MCPJob
capabilities()browser_capabilitiesReport immutable launch-time authority and implemented or unsupported powers without touching a page or probing the network.
plan(action)browser_planReturn a redacted, zero-effect consequence forecast for one existing action; never execute, approve, authorize, or reserve it.
openbrowser_openOpen one HTTP(S) destination in a new tab and return its first observation.
observebrowser_observeRead a bounded accessibility snapshot, text, and snapshot-scoped references.
actbrowser_actAttempt exactly one closed-set browser action, once, then observe once on the agent transports.
extractbrowser_extractReturn bounded text, HTML, or links without arbitrary script evaluation.
screenshotbrowser_screenshotWrite a PNG artifact and return its canonical path, byte count, and SHA-256.
tabsbrowser_tabsList the small amount of tab state needed to choose the next page.
closebrowser_closeClose 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

TypeScript · trusted local application code
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.

shell · start the JSONL process
agenttool-browser jsonl
JSONL · one complete request line
{"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.

MCP host · sovereign destination authority
{
  "mcpServers": {
    "agenttool-browser": {
      "command": "/absolute/path/to/project/node_modules/.bin/agenttool-browser",
      "args": ["mcp", "--authority", "sovereign"]
    }
  }
}
MCP host · public-only compatibility boundary
{
  "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

ProfileDestinationsBrowser features
public · defaultPublic HTTP(S); local/private and reserved destinations deniedWebSockets and service workers blocked
localPublic plus local/private; reserved destinations deniedWebSockets classified against the same boundary; service workers blocked
sovereignValid HTTP(S) passed to the browser without AgentTool destination classification, including local/private/reservedWebSockets 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.

TypeScript · broad destination pass-through
const browser = await AgentBrowser.launch({
  authority: "sovereign"
});

console.log(browser.capabilities());
console.log(browser.plan({
  kind: "navigate",
  url: "https://example.com/search?q=private"
}));
CLI · authority fixed for this process
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.

StageUseBoundary
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.
TypeScript · caller-owned orchestration
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.

Observation.response · exact shape
{
  "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 surfacedNever inferred
Link and Content-Location as bounded discovery hintsA linked endpoint is safe, authenticated, compatible, or approved to invoke
X-Agent-Surface, Substrate-Disposition, and X-Substrate-DispositionThe publisher controls the agent, host, policy, or next action
X-KingdomIdentity, relationship, recognition, trust, or membership
X-Token-Cost, X-Byte-Count, and X-Joy-IndexBilling 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:

😂

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

Local Browser is not hosted /v1/browse

SurfaceRuns whereWhat it requires
@agenttool/browser@0.2.0Your local TypeScript, JSONL, or stdio MCP process, using your installed Chrome-family browserNo AgentTool bearer, credits, Redis, or remote worker. Local actions are attempted once; profiles and artifacts remain on the operator’s machine.
POST /v1/browseA separate AgentTool API and BullMQ worker implementationBearer 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.

shell · one JSONL round trip
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