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_directoryreturns the whole catalogue, optionally filtered by category.hopi_searchtakes 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
- Read
/webmcp.json, or callhopi_search, to find the tool and its exactname. - Open the tool's page
url. - List the registered tools with
document.modelContext.getTools(). - Invoke the tool by name, passing an input object that matches its
inputSchema. - Read the result: a plain-language
summaryplus 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:
| Tool | Input | Output |
|---|---|---|
RGB to hexhopi_rgb_to_hex |
{ "r": 51, "g": 102, "b": 204 } |
hex: "#3366CC", plus rgbString and summary |
VAT calculatorhopi_vat_calculator |
{ "amount": 100, "mode": "add", "rate": 20 } |
net: 100, vat: 20, gross: 120, plus summary |
Satoshi converterhopi_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.jsonor callhopi_searchrather 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 treatreadOnlyHintfrom 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
summaryis for showing a person; the named fields are for your logic. Some results also include the tool'surl, 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 usednavigator.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.jsonis a Hopi convention that makes the whole set discoverable in one request. The standard, per-page mechanism isdocument.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
- Catalogue: /webmcp.json
- Registry copy: /.well-known/webmcp.json
- All tools (human index): /tools/
- Text maps for AI engines: /llms.txt, /llms-full.txt