---
description: "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."
---

# `toModelContextTool`

Adapts a [`WebTool`](/docs/reference/core/define-web-tool) to the `ModelContextTool` shape that `document.modelContext.registerTool` takes: it validates input, runs the confirmation gate and turns failures into readable `isError` results. [`registerWebTool`](/docs/reference/core/register-web-tool) uses it; call it directly when you register with WebMCP yourself.

## Import

```ts
import { toModelContextTool } from "@webmcp-agents/core";
import type { ToModelContextToolOptions } from "@webmcp-agents/core";
```

## Example

A confirm-gated tool registered by hand.

```ts title="register-by-hand.ts"
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`](/docs/reference/core/define-web-tool). |
| `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`](/docs/reference/core/create-call-tracker). |

## 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)`:

1. Parses `args`; a JSON string (Chrome 150) is parsed first. Invalid input answers here, before anything moves.
2. Finds the call's control and the field each parameter is typed into (see [Where the cursor goes](/docs/reference/core/define-web-tool#where-the-cursor-goes)). Whatever the cursor did not type is written now, in one step.
3. Runs the tool's `fill`, if it has one.
4. With `requireConfirmation`, waits for the person's trusted click on the control.
5. Runs `tool.execute` with `{ signal, source: "webmcp", context: context?.() }`.
6. Normalises the return value with [`toToolResult`](/docs/reference/core/error-result).

## Notes

- Errors map to codes: `WebToolInputError` is `invalid_input`, `ConfirmationUnavailableError` is `unavailable`, a [`WebToolError`](/docs/reference/core/web-tool-error) keeps its code, anything else is `failed`. See [`toolError`](/docs/reference/core/tool-error).
- An `AbortError` is 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 with `ConfirmationUnavailableError`.
- Only a valid call asks the person to confirm: they never approve something that then fails validation.
- Without an `options.signal` from the caller, a signal that never aborts is used.
- The adapter calls `tool.parse` and `tool.execute`, not `tool.run`, so the development-only `outputSchema` check does not run here.

## See also

- [Confirm before it runs](/docs/build/confirm-before-it-runs), the gate this adapter runs
- [`defineWebTool`](/docs/reference/core/define-web-tool), the definition it adapts
- [`registerWebTool`](/docs/reference/core/register-web-tool), the adapter plus registration
- [`ConfirmationUnavailableError`](/docs/reference/core/confirmation-unavailable-error), when the gate has no control
- [WebMCP types](/docs/reference/core/webmcp-types), the `ModelContextTool` shape
