Skip to content
WebMCP Kit
Esc
navigateopen⌘Jpreview
On this page

@agentlane/webmcp

The small browser SDK that generated WebMCP tools are built on: defineTool, registerTools, and the rules that keep tools stable.

The generated tools are built on a small SDK rather than the raw browser API. The reason is simple: WebMCP is a draft standard that still changes month to month, and its surface moves (document.modelContext today, navigator.modelContext in older Chrome). The SDK pins one spec version, resolves whichever surface the browser exposes, and quietly does nothing in browsers with neither — so your site never breaks for regular visitors.

The two-module shape

Generated code always splits into two kinds of files:

  • Tool modules define tools and export them. No side effects — importing one touches nothing.
  • One entry module per scope calls registerTools and owns the tools’ lifetime.

This split is what makes “connect later” cheap: adding an API key or telemetry config later touches the entry module only. Tool code never changes.

// webmcp/cart.ts — a tool module
import { defineTool } from "@agentlane/webmcp";

export const addToCart = defineTool({
  stableKey: "cart.add",            // durable identity — authored once, never changed
  name: "add_to_cart",              // wire name agents see (defaults to stableKey)
  title: "Add to cart",
  description: "Add a product to the shopping cart by SKU.",
  inputSchema: {
    type: "object",
    properties: {
      sku: { type: "string", description: "Product SKU" },
      quantity: { type: "integer", minimum: 1, default: 1 },
    },
    required: ["sku"],
    additionalProperties: false,
  },
  annotations: { readOnlyHint: false },
  async execute({ sku, quantity }: { sku: string; quantity?: number }) {
    const res = await fetch("/cart/add", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ sku, quantity: quantity ?? 1 }),
    });
    if (!res.ok) throw new Error(`add to cart failed: HTTP ${res.status}`);
    return await res.json();
  },
});
// webmcp/register.ts — the entry module
import { registerTools } from "@agentlane/webmcp";
import { addToCart } from "./cart";

export const registration = registerTools([addToCart]);

In a React app, tie the lifetime to the component instead:

useEffect(() => {
  const reg = registerTools([addToCart]);
  return () => reg.unregister();
}, []);

defineTool

Validates eagerly, freezes, and returns the tool. No side effects — an invalid definition throws a TypeError at module load, not at call time.

Field Required What it is
stableKey yes Durable identity, dot-namespaced domain.action (e.g. cart.add). Written once, survives renames and re-runs. Never sent to the browser.
description yes What the tool does, when to use it, what it returns. This is what agents read — it’s the product.
name no Wire name agents call, 1–128 chars of [A-Za-z0-9_.-]. Defaults to stableKey. Safe to change anytime.
title no Human-readable display name.
inputSchema no Plain JSON Schema object. Set additionalProperties: false.
annotations no Hints like { readOnlyHint: true }.
execute yes The page-owned logic. Throw on failure — never succeed silently on missing data. Return any JSON value; the SDK normalizes it.

registerTools

const reg = registerTools(tools, {
  signal,             // optional AbortSignal — abort means unregister
  telemetry: false,   // optional — see Telemetry below
  // tracking: {...}  // optional, opt-in per-call analytics
});

Returns { ready, unregister, signal }. ready resolves to one result per tool and never rejects:

State Meaning
registered Live on the page’s WebMCP surface.
unsupported Browser has no WebMCP surface — graceful no-op, site unaffected.
aborted The signal aborted before registration.
failed Registration failed — e.g. a duplicate name on the page.

Two rules to know:

  • One batch per scope. A duplicate name or stableKey within a batch throws and fails the whole call.
  • Unregistration happens only via unregister() or the abort signal — never spontaneously.

Telemetry

The SDK emits limited usage telemetry by default — SDK adoption, browser support mix, and tool-call reliability. It does not use a persistent visitor identifier or include raw tool inputs and results. Opting out is one flag: registerTools(tools, { telemetry: false }); for a guaranteed page-wide opt-out, set globalThis.__WEBMCP_TELEMETRY__ = false before the SDK loads. It also stays silent automatically when the browser sends Global Privacy Control, and during SSR. The separate tracking option can include tool inputs and results but is entirely opt-in and off unless configured. See the Privacy Policy for the collected data and controls.

Was this page helpful?