WebMCP: Hopi tools for AI agents

Hopi exposes 185 of its tools to AI agents, not only to a person clicking around. Any calculator, converter or generator that runs from typed values can be discovered and called directly. This page explains how that works: what WebMCP is, how an agent finds the tools, what to send them, what comes back, and the handful of rules worth following. It is written for anyone building or using browser-based AI agents, and you do not need to be an expert to follow it.

What WebMCP is

WebMCP, short for Web Model Context Protocol, is an emerging web standard that lets an AI agent running in your browser discover and call a page's tools directly, through a small interface called document.modelContext. Instead of reading the page, guessing at the layout and filling in form fields, the agent asks the page what tools it offers, sends typed arguments, and gets a clean, structured answer back.

Hopi exposes 185 of its tools this way. Each one is a plain calculation: work out a percentage, add or remove VAT, convert kilograms to stones, turn RGB values into a hex colour, decode a JWT, and so on. They are all read-only. Nothing is saved, nothing has a side effect, and, with a single exception (the currency converter, which fetches live exchange rates), everything runs in your own browser. That is what makes them safe for an agent to call freely.

What you can use

The full set is the same tools a person sees at all tools. Any tool that can run from typed values is agent-callable. The ones that cannot are the few that need a file upload, a live timer or a canvas, since those have nothing to send as plain arguments.

The machine-readable list of everything on offer is the catalogue at /webmcp.json (with an identical copy at /.well-known/webmcp.json for catalogue and registry conventions that use the well-known location).

How an agent finds a tool (discovery)

There are two ways in, and they work together.

The catalogue

Reading /webmcp.json gives an agent the whole set in one request, so it can choose a tool before opening any page. Each entry looks like this:

{
  "name": "hopi_rgb_to_hex",
  "title": "RGB to hex converter",
  "url": "https://hopi.co.uk/rgb-to-hex/",
  "category": "colour",
  "description": "Convert red, green and blue values (0 to 255) to a hex colour code.",
  "annotations": { "readOnlyHint": true },
  "inputSchema": { "type": "object", "properties": { ... }, "required": [ ... ] }
}

The name is how you call the tool, the url is the page it lives on, the inputSchema tells you exactly what arguments it accepts, and annotations flags it as read-only. Because the catalogue is generated from the same definitions the pages register, what you read here is what the page actually offers.

The two site-wide helper tools

On every Hopi page, two tools are always available so an agent that lands anywhere can orient itself:

  • hopi_directory returns the whole catalogue, optionally filtered by category.
  • hopi_search takes a keyword query, for example "kg to stone" or "take home pay", and returns the best-matching tools with their URLs.

When the agent opens a specific tool's page, that tool registers itself through document.modelContext, and the agent lists it with getTools().

How a call works, step by step

  1. Read /webmcp.json, or call hopi_search, to find the tool and its exact name.
  2. Open the tool's page url.
  3. List the registered tools with document.modelContext.getTools().
  4. Invoke the tool by name, passing an input object that matches its inputSchema.
  5. Read the result: a plain-language summary plus the structured fields.

Conceptually, on the tool's page, that is:

const tools   = await document.modelContext.getTools();
const rgb2hex = tools.find(t => t.name === "hopi_rgb_to_hex");

const raw = await document.modelContext.executeTool(
  rgb2hex,
  JSON.stringify({ r: 51, g: 102, b: 204 })
);

const result = JSON.parse(raw);
// result.hex === "#3366CC"

A few details of the current API are worth knowing. getTools() is asynchronous, so await it. You invoke a tool through document.modelContext.executeTool() rather than calling the tool object directly; the arguments go in as a JSON string, and the result comes back as a string to parse. And while the catalogue at /webmcp.json carries each inputSchema as an object, the schema exposed on a tool from getTools() is currently a stringified version of the same thing.

The interface is feature-detected. On a browser or agent that does not support WebMCP yet, the same page just behaves as an ordinary tool you use by hand, so nothing breaks.

Inputs

Every tool ships a JSON Schema describing its arguments, so you can build and check a call before making it. A schema states the property names, their types (number, integer, string, boolean), which are required, the allowed values for a fixed set of options (enum), numeric ranges (minimum and maximum), sensible defaults, and formats such as a date written as YYYY-MM-DD. Some tools have a mode selector, where the fields you must supply depend on the mode you pick.

The RGB to hex schema, for example, is strict about ranges:

{
  "type": "object",
  "properties": {
    "r": { "type": "integer", "minimum": 0, "maximum": 255 },
    "g": { "type": "integer", "minimum": 0, "maximum": 255 },
    "b": { "type": "integer", "minimum": 0, "maximum": 255 }
  },
  "required": ["r", "g", "b"]
}

Because the constraints are in the schema, not just in the description, an agent can validate its arguments and correct them before ever making a call.

Outputs

A successful call returns a structured object with two kinds of information:

  • A summary: a short, plain-language sentence, ideal for reading out to a person or feeding to a language model.
  • The machine fields: the actual values, named clearly, so your code can use them without parsing the sentence.

Where a value needs exact precision, such as a Bitcoin amount, it is returned as a canonical decimal string alongside a separate display version, so nothing is lost to rounding. If a call is wrong, you get a structured error of the shape { "error": "..." } rather than a crash, so a bad call is always recoverable. Here is the RGB to hex output:

{
  "hex": "#3366CC",
  "rgbString": "rgb(51, 102, 204)",
  "summary": "rgb(51, 102, 204) is #3366CC"
}

Worked examples

Three real tools, with an input you can send and the output you get back:

ToolInputOutput
RGB to hex
hopi_rgb_to_hex
{ "r": 51, "g": 102, "b": 204 } hex: "#3366CC", plus rgbString and summary
VAT calculator
hopi_vat_calculator
{ "amount": 100, "mode": "add", "rate": 20 } net: 100, vat: 20, gross: 120, plus summary
Satoshi converter
hopi_satoshi_converter
{ "mode": "sats_to_btc", "value": "150000000" } sats: "150000000", btc: "1.5", a display object, plus summary

One tool behaves slightly differently on purpose: the currency converter (hopi_currency_converter) fetches live, hourly exchange rates from Hopi's own server, so its numbers depend on the day. Everything else runs entirely in the browser with no network call.

Best practice

  • Start from the catalogue or search. Read /webmcp.json or call hopi_search rather than guessing a tool name.
  • Match the schema. Use the exact property names, respect the required fields and enums, and keep numbers inside the stated ranges.
  • Hopi's tools are read-only. Every tool is implemented without side effects and advertises that with readOnlyHint: true, so Hopi's calls are safe to run and safe to retry. Use the annotation to decide whether a confirmation step is needed, but treat readOnlyHint from arbitrary sites as a hint rather than a security guarantee.
  • Handle the error shape. On bad input you get { "error": "..." }. Read it, fix the arguments, and call again.
  • Use the right part of the result. The summary is for showing a person; the named fields are for your logic. Some results also include the tool's url, which makes a tidy citation link.
  • Never send a private key or seed phrase to the crypto tools. The validators exist to check public addresses; none of them ever needs a secret.

Tested as an agent interface

Hopi's WebMCP catalogue and its runtime registrations are tested together, so /webmcp.json is not hand-maintained metadata that can drift from what the pages actually do. Automated release gates check that the catalogue and the runtime registrations match, that every tool has curated valid and invalid input cases, that outputs and structured errors keep their shape, that reversible calculations round-trip, that pathological inputs stay within an execution-time budget, and that every tool registers and runs in a real browser.

Good to know

  • WebMCP is an emerging standard, currently in browser origin trials, so the exact interface may still shift. Hopi feature-detects it in one place, so a change is a small update rather than a rewrite. The getter is document.modelContext (earlier builds used navigator.modelContext).
  • Tool registration only happens in a secure context, so the pages must be served over HTTPS, which Hopi always is.
  • The catalogue at /webmcp.json is a Hopi convention that makes the whole set discoverable in one request. The standard, per-page mechanism is document.modelContext.getTools(); the catalogue simply saves an agent from opening 185 pages to see what exists.
  • For AI answer engines that read plain text rather than call tools, Hopi also publishes /llms.txt and /llms-full.txt, a categorised map of every tool with its description and FAQs.

Links