A library of custom WebMCP tools for Kernel browsers, organized by website domain.
Custom WebMCP tools are registered browser-side with webmcp.addCustomTools() (via the
Kernel Browser REPL or the Kernel API) and can be page-backed (kind: "page") or
CDP-backed (kind: "cdp"). This repo collects tool implementations anyone can load into
their own Kernel browser session.
domains/
<registrable-domain>/ # one directory per site, e.g. delta.com, aa.com
README.md # "# <domain>" plus a | tool | description | table
<tool-name>.js # one file per tool, named after the tool
tests/ # node --test "tests/*.test.mjs"
registry.json # machine-readable index of every tool
spec/ # the rules this repo follows, and JSON Schemas
scripts/validate-registry.mjs # checks registry.json and domains/ against the spec
bench/ # A/B harness: an agent with vs without a domain's tools (bench/README.md)
The rules for layout, naming, registry.json, URL patterns, status values, and
per-domain READMEs are in spec/repository.md. It is the
normative source; this README is a summary. The tool source format, the cdp and
page runtimes, and the invocation lifecycle are in
spec/cdp-tools.md.
In short:
- one directory per registrable domain, one tool per file, namespace = domain;
url_patternsdefault tohttps://<domain>/*andhttps://*.<domain>/*;- every tool has a registry entry with a
statusofdraft,qa_passed,verified, orbroken, and the evidence the spec requires for that status; - tools never assume which page the browser is on when they are invoked.
Every *.js tool file is a self-contained JavaScript expression that evaluates to an
array containing one tool definition, exactly the string you can hand to
webmcp.addCustomTools({ namespace, source }):
[{
kind: "cdp", // "cdp" survives page navigation
match: { url_patterns: ["https://delta.com/*", "https://*.delta.com/*"] },
tool: {
name: "search_flights",
description: "…",
inputSchema: { /* JSON Schema */ },
outputSchema: { /* JSON Schema — required for every tool */ },
annotations: { readOnlyHint: true }
},
execute: async (input) => { /* … */ }
}]No imports, no require(), no top-level statements. Failures are explicit output
(status: "error", no_results, …), never a successful empty result. Tools must not
purchase, submit payments, or enter personal data unless the tool description says so.
The xwebmcp CLI installs tool files from GitHub, pins them to a commit, and pushes
them into a browser: your own Google Chrome through the bundled extension, or a Kernel
browser through the API.
go install github.com/kernel/xwebmcp/cmd/xwebmcp@latest
# install every tool in this repo (or owner/repo/path@ref, or a github.com URL)
xwebmcp add kernel/xwebmcp
xwebmcp add kernel/xwebmcp/domains/southwest.com@main
xwebmcp list # what is installed and at which commit
xwebmcp add kernel/xwebmcp # update to the current commit; prints a diff of changed code
xwebmcp remove southwest.com # drop a namespace (or owner/repo, or namespace/tool)Tools live under ~/.xwebmcp/ (override with XWEBMCP_HOME). lock.json records the
source, resolved commit, and SHA-256 of every file. When a repository has a
registry.json, its entries decide which files are tools and which namespace they
belong to; otherwise every .js file under the given path is a tool and its parent
directory is the namespace.
The extension/ directory is a Chrome MV3 extension that runs the tool files unchanged
in your normal Chrome profile, with your real logins. kind: "cdp" bodies run in the
extension (a sandboxed page drives the tab over chrome.debugger with the same helper
API the Kernel browser REPL provides: cdp, waitForEvent, gotoUrl, js, click,
fillInput, waitForElement, …), so they keep running while the page navigates.
One-time setup (macOS and Linux; Chrome, Chromium, Beta, Dev, or Canary):
xwebmcp setup chrome # quit Chrome firstThis backs up Chrome's Local State, enables the enable-webmcp-testing flag
(document.modelContext), registers xwebmcp as a native messaging host, writes the
unpacked extension to ~/.xwebmcp/extension, and opens chrome://extensions. There,
turn on Developer mode and Load unpacked → ~/.xwebmcp/extension. The command
is idempotent; re-run it after upgrading xwebmcp to refresh the extension files.
Then:
xwebmcp attach # push the installed tools into the extension
xwebmcp status # flag, native host, bridge, and per-site access state
xwebmcp tools # every WebMCP tool open tabs expose (site-native and custom), with tool_refs
xwebmcp invoke <tool_ref> \
--input '{"origin":"DAL","destination":"HOU","departureDate":"2026-10-20","tripType":"oneway"}'The extension asks for no site access up front. Open its popup and click Grant access
to all sites to approve HTTP/HTTPS access once, including sites installed later. This
lets the extension discover tools across those sites; Chrome still requires your consent.
Alternatively, after attach, click Grant access for only the installed tools' sites,
or grant the current site individually. The popup also lists the tools on the current
tab and whether each installed tool is registered there.
How invocation works and what to expect:
- Every tool, site-native or custom (page or cdp), is a live registration in the tab whose
URL it matches, identified by an opaque
tool_ref, exactly as in the Kernel API'sGET /browsers/{id}/webmcp/tools.xwebmcp invoke <tool_ref>(refs come fromxwebmcp tools) invokes that registration, likePOST /browsers/{id}/webmcp/invoke. Atool_refbecomes invalid when its document navigates; list again afterwards. To use a site's tools, open a tab there first. chrome.debuggerattaches while a cdp tool runs, or briefly when registering a page tool on a site whose CSP blocks dynamic compilation. Chrome may show its debugging banner during either operation.- A tool invoked from the page (
document.modelContext.executeTool) keeps running in the extension if its document navigates away, but that document can no longer receive the result; the invocation log (listInvocations) records it as undelivered. - Tool inputs are never logged or stored. Failed invocations keep the thrown error,
console output, and timing, readable through
getInvocation. A local file installed individually retains its filename in mapped stacks;attachcombines installed files per namespace, so those stacks point to the namespace batch instead.
xwebmcp mcp is a stdio MCP server that exposes the browser's tools to Claude Code,
Codex, Cursor, or any MCP client:
claude mcp add xwebmcp -- xwebmcp mcp # Claude Code
codex mcp add xwebmcp -- xwebmcp mcp # Codexor in an mcpServers JSON config: {"command": "xwebmcp", "args": ["mcp"]}.
The server's tools mirror the Kernel API (list_tools / invoke_tool are
GET /webmcp/tools / POST /webmcp/invoke), plus tab and install management:
| tool | does |
|---|---|
list_tools |
{exclude_custom?} → WebMCP tools live in open tabs, site-native and custom (page and cdp), in the Kernel API's WebMCPTool shape (tool_ref, tool, source) |
invoke_tool |
{tool_ref, input, timeout_sec?} → runs that exact registration; returns WebMCPInvocationResult plus diagnostics on failure (error with stack mapped to the tool file, console output, timing) |
get_invocation |
{invocation_id} → the same diagnostics again for a past invocation, for debugging |
list_custom_tools, install_tool, remove_tool |
installed tools; install_tool takes a local file path |
open_tab, navigate_tab, close_tab, list_tabs |
extension-owned tabs; with --kernel, only list_tabs is available and lists tabs that expose tools |
By default the server only lists and invokes tools in tabs it opened itself (an
xwebmcp tab group); start it with --all-tabs to include the user's tabs. Tool inputs are never logged or stored by the server or the
extension. --kernel <browser> points the same server at a Kernel browser, but it
cannot map a numeric tab_id to a REPL target for open_tab, navigate_tab, or
close_tab, so those operations return an error rather than acting on the wrong tab.
This is the loop for iterating on a tool file against your normal, logged-in Chrome with a coding agent driving it.
-
Install the extension and register the native host (once):
xwebmcp setup chrome, then Load unpacked →~/.xwebmcp/extensioninchrome://extensions.xwebmcp statusshould reportbridge: connected. -
Point the MCP server at your checkout:
claude mcp add xwebmcp -- xwebmcp mcp --dev ~/src/xwebmcp--devaccepts a repository root (uses itsregistry.json), adomains/<namespace>directory, or a single tool file, and can be repeated. Every tool file under it is installed when the server starts and reinstalled whenever its content changes; tools installed withxwebmcp addkeep working next to them.xwebmcp dev <path>runs the same watcher without an MCP server. Open tabs pick up reinstalled tools without a reload, and the watcher andxwebmcp mcpreconnect on their own when the extension is reloaded or Chrome restarts its native host; a reinstall that fails while the extension is away is retried on the next scan. Reloading the extension clears and re-registers the custom tools in already-open tabs, leaving the sites' own tools alone. (Tabs that were open before upgrading from an older xwebmcp need one reload.) -
Grant the site once in the extension popup (Grant access) when the server reports pending origins.
-
Loop: the agent opens a tab on the site (
open_tab), callslist_toolsand theninvoke_toolwith the tool'stool_ref, reads the failure'sdiagnostics.error.stack(lines map to the tool file), edits the file, waits for the reinstall (xwebmcp mcplogsinstalled <namespace>/<tool>on stderr), lists again (the reinstalled tool has a newtool_ref), and invokes again.
A prompt that works with Claude Code:
The tool file domains/probe.test/page_title.js is watched by the xwebmcp MCP server.
Open https://probe.test/ with open_tab, find page_title in list_tools, and invoke it with
input {} via invoke_tool. If it fails, use the diagnostics to fix the file, list tools
again, and invoke the new tool_ref until it completes.
Page inspection. xwebmcp mcp deliberately has no screenshot, DOM-snapshot, or
network tools. Pair it with
chrome-devtools-mcp, which
can attach to the same running Chrome: enable remote debugging once at
chrome://inspect/#remote-debugging (Chrome 144+) and add it with --autoConnect:
claude mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest --autoConnectBoth servers then see the same tabs; use chrome-devtools-mcp's take_screenshot,
take_snapshot, list_network_requests, and list_console_messages on the tab an
xwebmcp invocation left behind. chrome.debugger (used while a cdp tool runs) and a
DevTools client can be attached to one tab at the same time; only the DevTools UI panel
open on that tab blocks the extension from attaching.
# register every installed tool on a Kernel browser (KERNEL_API_KEY must be set)
xwebmcp attach --kernel <browser id or name>
xwebmcp tools --kernel <browser id or name>attach sends all installed files in each namespace as one batch with
force_overwrite_namespace, so a failed file does not leave that namespace partly
updated. Re-running it after an update is safe for both targets.
# 1. create a browser
kernel browsers create --profile personal-stuff --proxy cincy
# 2. in browser_repl (or via POST /browsers/{id}/webmcp/custom-tools):
const source = (await import("node:fs")).readFileSync("domains/delta.com/search_flights.js", "utf8");
await webmcp.addCustomTools({ namespace: "delta.com", tools: eval(source) });
const live = (await webmcp.listTools()).find(t => t.tool.name === "search_flights");
const result = await webmcp.invokeTool(live.tool_ref, { /* input */ }, { timeoutSec: 120 });Never invoke a tool whose metadata you don't trust: check its status, notes, and
url_patterns in registry.json first.
Before opening a pull request:
node scripts/validate-registry.mjs # registry.json, domains/, and README tables against the spec
node --test "tests/*.test.mjs" # tool testsCI runs both on every pull request. Adding a tool means adding the tool file, a test, a
registry.json entry, and a row in the per-domain README table.