toModelContextTool
Adapts a WebTool to the ModelContextTool shape that document.modelContext.registerTool takes: it validates input, runs the confirmation gate and turns failures into readable isError results.
Adapts a WebTool to the ModelContextTool shape that document.modelContext.registerTool takes: it validates input, runs the confirmation gate and turns failures into readable isError results. registerWebTool uses it; call it directly when you register with WebMCP yourself.
Import
import { toModelContextTool } from "@webmcp-agents/core";
import type { ToModelContextToolOptions } from "@webmcp-agents/core";
Example
A confirm-gated tool registered by hand.
import { defineWebTool, toModelContextTool } from "@webmcp-agents/core";
const clearCart = defineWebTool({
id: "clear_cart",
title: "Clear cart",
description:
"Remove every item from the cart. The person confirms on " +
"the page. Use only when they asked to start over.",
destructive: true,
requireConfirmation: true,
execute: () => "The cart is empty.",
});
// What registerWebTool does for you, by hand: adapt, then register.
document.modelContext?.registerTool(toModelContextTool(clearCart));
Options
toModelContextTool(tool, options?). The first argument is the tool; the second is a ToModelContextToolOptions.
| Name | Type | Default | Description |
|---|---|---|---|
tool |
WebTool |
required | The tool to adapt, made with defineWebTool. |
context |
() => unknown |
not said | Read on every call and passed to execute as the earlier form’s ctx.context. New code closes over its own state instead. |
calls |
CallTracker |
not said | Counts each call (confirmation wait included), so unregistering can wait for it. See createCallTracker. |
Returns
A ModelContextTool with name (the tool’s id), title, description, inputSchema (the tool’s jsonSchema), annotations and execute. annotations are the tool’s annotations with each flag (readOnly, destructive, repeatable, external, safeResponse) written over them as its WebMCP hint. It also carries symbol-keyed properties the visual layer reads: a stage that validates a call and finds its control and fields before anything moves, and, for earlier code, the selector forms of the confirmation click and the cursor’s target.
Each call through execute(args, options):
- Parses
args; a JSON string (Chrome 150) is parsed first. Invalid input answers here, before anything moves. - Finds the call’s control and the field each parameter is typed into (see Where the cursor goes). Whatever the cursor did not type is written now, in one step.
- Runs the tool’s
fill, if it has one. - With
requireConfirmation, waits for the person’s trusted click on the control. - Runs
tool.executewith{ signal, source: "webmcp", context: context?.() }. - Normalises the return value with
toToolResult.
Notes
- Errors map to codes:
WebToolInputErrorisinvalid_input,ConfirmationUnavailableErrorisunavailable, aWebToolErrorkeeps its code, anything else isfailed. SeetoolError. - An
AbortErroris rethrown, never turned into a result. If the signal aborted while validating, the work doesn’t start. - With
requireConfirmation, a control found by name must be unique: with none, or several, the call fails withConfirmationUnavailableError. - Only a valid call asks the person to confirm: they never approve something that then fails validation.
- Without an
options.signalfrom the caller, a signal that never aborts is used. - The adapter calls
tool.parseandtool.execute, nottool.run, so the development-onlyoutputSchemacheck does not run here.
See also
- Confirm before it runs, the gate this adapter runs
defineWebTool, the definition it adaptsregisterWebTool, the adapter plus registrationConfirmationUnavailableError, when the gate has no control- WebMCP types, the
ModelContextToolshape