Skip to content
GitHub ↗

Quick start

The golden path: open → list → register → execute → close. Every step prints evidence.

export AGENT_WEBMCP_REGISTRY=$PWD/.agent-webmcp/registryH=$(agent-webmcp open --json https://example.com \  | python3 -c "import json,sys; print(json.load(sys.stdin)['handle'])")agent-webmcp list $Hagent-webmcp close $H --yes
bash

Open and list

open attaches the page and prints a handle plus a toolCount. That count is point-in-time: pages expose tools as they load, so 0 means list again, not give up.

agent-webmcp open --json https://example.com# {"handle":"s_…","url":"…","toolCount":0,#  "reapplied":[],"skipped":[]}agent-webmcp list $H# name — description rows on a TTY, JSON when piped
bash

Never call a tool you have not listed. An empty list means the page exposes nothing — say so instead of guessing. Fetch one tool's full record (schema included) before calling it:

agent-webmcp list $H <tool-name>
bash

Author your first tool

Pages that expose nothing still take authored tools. This one answers pong; the fixture proves it before anything persists:

agent-webmcp register $H \  '{"name":"ping","description":"Ping fixture tool",    "inputSchema":{"type":"object","properties":{}},    "fixtureInput":{}}' \  '(async () => { return { content: [    { type: "text", text: "pong" }] }; })' \  --yesagent-webmcp list $H   # ping reads authored:true
bash

Writes need --yes. Piped output is JSON. See Custom tools before authoring against a real page — authoring blind is forbidden.

Compose and close

search and execute live on MCP only. Over MCP the same session reads:

{ "handle": "s_…", "code": "return await tools.ping();" }
json

execute returns {value, spilled, toolCalls, perSession, origins, untrusted:true} — untrusted is always true. Then release what you opened:

agent-webmcp close $H --yesagent-webmcp open --json https://example.com# reapplied:["ping"] — memory works
bash

Close what you open. close kills only browsers the engine launched, never foreign ones.