Skip to content
kernelPublic

About

webmcp++

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

xwebmcp

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.

Repository layout

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)

Specification

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_patterns default to https://<domain>/* and https://*.<domain>/*;
  • every tool has a registry entry with a status of draft, qa_passed, verified, or broken, and the evidence the spec requires for that status;
  • tools never assume which page the browser is on when they are invoked.

Tool file contract

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.

Loading a tool

With the xwebmcp CLI

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.

In your own Chrome (extension)

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 first

This 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's GET /browsers/{id}/webmcp/tools. xwebmcp invoke <tool_ref> (refs come from xwebmcp tools) invokes that registration, like POST /browsers/{id}/webmcp/invoke. A tool_ref becomes invalid when its document navigates; list again afterwards. To use a site's tools, open a tab there first.
  • chrome.debugger attaches 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; attach combines installed files per namespace, so those stacks point to the namespace batch instead.

Connect an agent: xwebmcp mcp

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             # Codex

or 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.

Develop tools locally

This is the loop for iterating on a tool file against your normal, logged-in Chrome with a coding agent driving it.

  1. Install the extension and register the native host (once): xwebmcp setup chrome, then Load unpacked → ~/.xwebmcp/extension in chrome://extensions. xwebmcp status should report bridge: connected.

  2. Point the MCP server at your checkout:

    claude mcp add xwebmcp -- xwebmcp mcp --dev ~/src/xwebmcp

    --dev accepts a repository root (uses its registry.json), a domains/<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 with xwebmcp add keep 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 and xwebmcp mcp reconnect 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.)

  3. Grant the site once in the extension popup (Grant access) when the server reports pending origins.

  4. Loop: the agent opens a tab on the site (open_tab), calls list_tools and then invoke_tool with the tool's tool_ref, reads the failure's diagnostics.error.stack (lines map to the tool file), edits the file, waits for the reinstall (xwebmcp mcp logs installed <namespace>/<tool> on stderr), lists again (the reinstalled tool has a new tool_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 --autoConnect

Both 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.

On a Kernel browser

# 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.

By hand

# 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.

Contributing

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 tests

CI 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.

About

webmcp++

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages