Protocol
SDK, then remote MCP.
Unbrowse is an authenticated action surface. The SDK is the product. MCP is a thin remote harness over it. Agent Skills are the YAML those harnesses already are.
Canonical REST
All agent traffic goes through /api/v1. The authenticated principal determines the workspace — a caller-supplied workspace id is never authority.
POST /api/v1/runs
GET /api/v1/runs/:id
POST /api/v1/runs/:id/responses
POST /api/v1/runs/:id/cancel
GET /api/v1/runs/:id/events
POST /api/v1/capabilities/search
POST /api/v1/accounts/connections { origin, username, password } → vault:// ref
POST /api/v1/accounts/register { origin, username } → generated, vaulted
GET /api/v1/vault refs + audit, never secrets
POST /api/v1/learn { har | traces, goal?, title? } → learned.* capability
GET /api/v1/learned
GET /api/v1/learned/:id/harness.yaml
GET /api/v1/learned/:id/skill.md
GET /api/v1/usage verified calls this month, rendered, passthrough, quota
GET /api/v1/sites?q= compiled sites (public registry) with tool counts
GET /api/v1/sites/:host that site's tools
GET /api/v1/sites/:host/openapi.json OpenAPI 3.1: one operation per tool, typed input and output
POST /api/v1/sites/:host/call/:tool run a tool (same metered, verified run)
/api/v1/sites/:host/mcp the site as its own MCP server
Authorization: Bearer ub_live_…TypeScript SDK
import { Unbrowse } from "@unbrowse/sdk";
const ub = new Unbrowse({
apiKey: process.env.UNBROWSE_API_KEY!,
baseUrl: "https://v3.unbrowse.ai/api/v1",
});
const run = await ub.run({
task: "top stories on Hacker News",
interactionMode: "unattended",
idempotencyKey: "hn-1",
});
if (run.status === "input_required") {
await ub.resume(run.runId, run.stateRevision, [{
requirementId: run.requirements[0].id,
expectedRevision: run.requirements[0].revision,
action: "accept",
values: { export_format: "csv" },
}]);
}MCP tools
Remote MCP at /mcp is Streamable HTTP (Grok, Claude, Cursor). /api/mcp is the same adapter over JSON-RPC. authorization and run actor. When three or fewer skills match, dedicated unbrowse.skill.* tools are listed with slot schemas from the harness YAML.
unbrowse.run— start a durable run; first request is fulfilled while indexingunbrowse.inspect— status, requirements, verified resultunbrowse.resume— answer progressive fields on the same rununbrowse.cancel— stop new dispatches, return an effect receiptunbrowse.usage— this month's verified calls, rendered runs and their passthrough cost, and the quota left. Only verified successes billunbrowse.discover— private Jev space, then public registry. Each learned capability carrieshints: health from its run ledger,warm/rendered/cold, p50/p95 latency and the next stepunbrowse.browse.open,unbrowse.browse.snapshot,unbrowse.browse.act,unbrowse.browse.finish,unbrowse.browse.close— drive a recorded patchright cloud browser with@refsnapshots; the first task is fulfilled and finish (or close) compiles the site into alearned.*capability — an API call or a server-rendered results page, returned as{ title, text, links }. Logins fill from the vault (vault: "password"), never from the agentunbrowse.learn— compile HAR files or recorded traces into a one-calllearned.*capability; see the live demo
Sites as tools
Unbrowse is a browser engine and a compiler: it compiles a website's own requests into an API, and the API into tools. Every compiled site is an MCP server at /api/v1/sites/<host>/mcp and an OpenAPI 3.1 document at /api/v1/sites/<host>/openapi.json; each tool carries its input schema and an output schema learned from verified responses. The public registry holds pre-indexed sites (search and page reads, re-verified on a schedule); your own learned capabilities appear as my__… tools in the main server, and public ones you use stay in your tool list.
Statuses
- succeeded
- Declared business outcome independently verified. HTTP 200 is not enough.
- input_required
- Waiting on a versioned requirement. Not a capability failure. Resume the same run.
- outcome_unknown
- A mutation may have landed. Automatic mutating retries stay suspended until reconciliation.
- failed / cancelled
- Known effects are retained. Cancellation cannot unsend a delivered request.
Harness YAML
unbrowse/v1alpha1 packages declare slots, operations, bindings, guards and independent outcome checks. YAML is authoring; the runtime executes a typed IR. No eval, no shell, no website-provided expressions.