@schwaizer/ocp-webmcp
Declare a Surface's OCP tool catalogue to the browser's own agent, via WebMCP.
npm i @schwaizer/ocp-webmcp
import { registerSurfaceTools } from '@schwaizer/ocp-webmcp';
const registration = registerSurfaceTools(createTaxHostTools());
// registration.registered → ['find_municipality', 'income_tax', …]
// registration.skipped → [{ name: 'book_appointment', reason: 'write' }]
// registration.supported → false on a browser with no WebMCP
Why this exists in a repository that concedes the case
OCP answers a question about a page from a device that is not the one showing it. When the page and the microphone are the same machine, a browser-native agent is simpler, and the root README says so without hedging: no install, no pairing, no relay, no protocol, and the site pays for no inference either way.
That concession creates a maintenance trap. A site supporting both paths would otherwise keep two tool catalogues — one shaped for OCP, one for WebMCP — and they drift apart at the first schema change, with the stale one being whichever nobody is looking at. This package removes the second catalogue. One definition, re-declared.
Write tools are never registered
A tool declaring write: true is skipped, and this is the whole design rather
than a precaution.
Core §10 requires a human confirmation for a write, expiring to deny, and OCP
puts that ceremony on the Surface — the page the visitor is looking at —
precisely because the Companion cannot be trusted to have asked. WebMCP has no
equivalent. The agent decides when to call, and the page receives the arguments
with nothing between the model's intent and the effect. Registering
book_appointment here would mean a booking made because a model inferred one
was wanted, with no expire-to-deny and no record of who approved it.
There is deliberately no allowWrites option. A flag with that name gets
turned on by someone who has not read this section. A site that wants writes over
WebMCP needs its own consent step first, and that is a real thing to build rather
than a boolean to flip.
What it does not claim
- WebMCP is not a shipped standard. It is an origin trial and a W3C-track
proposal, and its surface has changed shape more than once. This adapter
feature-detects, supports both spellings it has been observed to take
(
provideContextandregisterTool), and no-ops where neither exists.supportedreports what actually happened rather than being inferred from a browser version. - It bridges one direction only. Nothing here lets a WebMCP agent reach an OCP Hub, a Companion or a paired phone. The two paths share a catalogue and nothing else.
- Registration is not disclosure, but the first call is. Declaring a tool hands the agent a name, a description and a JSON schema. The moment the agent calls one, the arguments it extracted go wherever that tool sends them — on the site's own origin, into the site's own logs. A deployment's privacy copy has to describe that actor, and "the assistant" usually means the paired phone.
API
| export | |
|---|---|
registerSurfaceTools(tools, options?) | Declares the catalogue; returns { registered, skipped, supported, unregister }. Never throws. |
isWebMcpAvailable(options?) | Whether this browser exposes a surface the adapter can use. |
SurfaceTool | { name, description, inputSchema, write?, run } — structurally identical to ocp-embed's HostTool, restated so this package needs no dependency to register a tool. |
options.modelContext overrides where WebMCP is found (default
navigator.modelContext), which is how the tests drive the real code rather than
a re-implementation of it.
License: Apache-2.0