← Browser AI From First Principles

Expose the First WebMCP Tool

Expose a typed read-only book operation through WebMCP, validate its arguments, bound its results and inspect the complete call lifecycle.

Chapter 17 built the contract. This chapter registers it with the browser.

Our first WebMCP tool should not send a message, edit a document or buy anything. It should read from the book we control. We will expose search_book: a bounded operation that returns relevant chapters and passages.

The modesty is deliberate. Starting with a bounded read-only tool removes state-changing consequences while we test registration, discovery, schema handling, execution and tracing. It does not remove disclosure risk — a read tool can still leak — so the corpus and the result still need explicit bounds, which is most of what section 6 is about.


1. Why the first rung is read-only

There is a capability ladder, and search_book is its lowest rung:

read a controlled corpus          ← we are here
read across origins
prepare a draft
reversible local write
external effect (send, publish)

Each higher rung adds an authority question that the rung below did not have to answer. Starting at the bottom lets us get the plumbing right — does the browser see the tool, does the schema round-trip, does the trace capture every stage — before any mistake can leave the page.


2. The complete search_book contract

The reader should be able to state every dimension of this tool:

DimensionValue
Identityprogrammer.ie + search_book + registration instance
Namesearch_book
Description“Search this book for passages related to a query.”
Input schemaquery (string, 2–300 chars), limit (integer, 1–10, default 5), additionalProperties: false
Effect classread, scope registered-book-index, network: forbidden
AnnotationsreadOnlyHint: true, consequentialHint: false, untrustedContentHint: false
Result shapearray of { chapter, title, url, excerpt } plus truncated
Result boundsat most 10 matches; excerpts capped at 180 characters; corpus limited to this book
Deterministic validationschema, then domain (query length, limit range), then result-shape check
ErrorsTypeError for query, RangeError for limit, empty array for no match
Authority classadmitted automatically within declared scope; no approval surface required

Every one of those rows is enforced by application code. The schema is a hint to the agent about argument shape; it is not the enforcement boundary.


3. The WebMCP registration surface

WebMCP is an origin-trial API. The syntax below was rechecked against the Chrome for Developers WebMCP documentation on September 6, 2026; it is available behind the origin trial from Chrome 149 and behind chrome://flags/#enable-webmcp-testing for local development.

const controller = new AbortController();

await document.modelContext.registerTool(
  {
    name: "search_book",
    description: "Search this book for passages related to a query.",
    inputSchema: {
      type: "object",
      properties: {
        query: { type: "string", minLength: 2, maxLength: 300 },
        limit: { type: "integer", minimum: 1, maximum: 10 }
      },
      required: ["query"],
      additionalProperties: false
    },
    annotations: {
      readOnlyHint: true,
      consequentialHint: false,
      untrustedContentHint: false
    },
    execute: async ({ query, limit = 5 }, { signal }) => {
      return searchBook({ query, limit, signal });
    }
  },
  { signal: controller.signal }
);

Three details matter and have changed as the API evolved:

  • Registration is on document.modelContext.registerTool, not navigator.modelContext. Earlier drafts used the navigator path; the manuscript and the Chapter 18 experiment metadata were corrected to document in the September 2026 pass.
  • Removal is driven by an AbortController. You pass { signal } to registerTool and later call controller.abort() to withdraw the registration. As of Chrome 153, aborting does not interrupt an execution already in flight — a detail the Observatory should record rather than assume.
  • The execute handler receives the input object and an options object carrying a signal. Whatever the platform does or does not enforce against the schema, the handler re-validates (section 5) and passes the signal into any cancellable work.

The invariant to hold onto is:

platform may constrain  +  application validates  =  the actual enforcement boundary

The durable architecture is the part that will outlast the syntax: a name, a description, a typed input schema, safety annotations, a lifecycle and an implementation.


4. What the safety annotations do and do not do

readOnlyHint, consequentialHint and untrustedContentHint are communication, not enforcement.

AnnotationWhat it tells an agentWhat it does not guarantee
readOnlyHint: trueThe author states this call changes no stateThat the implementation is actually side-effect-free
consequentialHint: trueThe call has a significant or hard-to-reverse effect, so the consuming agent or browser can require user confirmation before it runsThat a false value is safe to run unattended, or that the annotation is itself the authorization mechanism
untrustedContentHint: trueThe result may contain external or user-generated text needing sanitizationThat a false value is free of injected instructions

The hints let a well-behaved agent and the browser make better default decisions. They are supplied by the page, so the Observatory stores them as browserAttested: false. The real constraints on search_book — no network, no writes, bounded disclosure — live in the implementation and in the deterministic policy layer, which assigns effects from its own registry regardless of what the annotations claim.


5. Validate twice, then check the result

The schema constrains the shape the model produces. The application enforces the domain.

function searchBook({ query, limit = 5, signal }) {
  if (typeof query !== "string" || query.trim().length < 2) {
    throw new TypeError("query must contain at least two characters");
  }
  if (!Number.isInteger(limit) || limit < 1 || limit > 10) {
    throw new RangeError("limit must be between 1 and 10");
  }
  const matches = index.search(query.trim(), limit, { signal });
  return boundResult(matches); // caps count, excerpt length and corpus
}

There are five gates, not two:

schema validation      → shape is well-formed
domain validation      → query and limit satisfy the tool's own rules
policy validation      → the effect class is admissible in this context
resource limits        → count, excerpt length, corpus scope
result validation      → the returned value matches the result contract

A JSON argument that passes the schema is not a valid operation, and a handler that resolves is not a valid result. The last gate matters: a resolved promise carrying twenty matches or a 4,000-character excerpt has violated the contract even though nothing threw.


6. “Read-only” is not “safe to disclose everything”

A read tool has no side effect. It can still leak.

The disclosure boundary for search_book is set by four limits:

  • Corpus scope — search runs only over this book’s registered index, not arbitrary DOM text, files or another origin.
  • Excerpt size — 180 characters per match, enough to judge relevance, not enough to reconstruct a chapter.
  • Result count — at most 10 references, which also protects the agent’s context window from being flooded by one call.
  • Public content only — the index contains published chapter summaries; private or draft material is never registered.

“Read-only” describes the effect on state. The disclosure boundary is a separate decision, and for a tool that reads private content it is the more important one.


7. Tool identity is contextual

Two sites can register a tool called search_book, and one site can register, withdraw and re-register it during a single visit. The tool name alone is not an identity.

WebMCP itself keys a tool by origin and name. For tracing we need to tell two registrations of the same name apart, so the Observatory assigns each registration its own ID and treats the effective trace identity as:

origin + tool name + Observatory registration ID

An agent’s tool list is time-dependent page state: registrations can appear after load, be replaced with a new definition, or be withdrawn when a controller aborts. The Observatory snapshots, for each registration:

  • page origin;
  • the full tool definition and a schema hash;
  • registration time;
  • the annotations;
  • whether the source was declarative (HTML form) or imperative (registerTool);
  • replacement or removal, with the reason.

A behavioral change between two runs is only interpretable if we know the tool list was identical — or exactly how it differed.


8. Test the tool before a model touches it

This is the methodological core of the chapter. Before asking an agent to discover search_book, prove the deterministic contract with fixtures that use no model at all:

FixtureExpected outcomeStage exercised
Valid query, limit: 3Up to 3 bounded matches from this bookexecution + result bounds
Query with no corpus matchEmpty array, truncated: false — never fabricated textgrounding
301-character queryRejectedschema
limit: 11Rejectedschema
Extra destination fieldRejectedschema (additionalProperties: false)
Malformed JSON stringRejectedparse
Query engineered to return long textExcerpts still capped at 180 charsresult validation
Abort mid-executionHandler observes signal; partial work discardedcancellation

Only when every row passes deterministically do we move to evaluating whether an agent discovers and selects the tool. Running these in the other order lets a tool bug masquerade as a reasoning failure — the single most common way agent debugging goes wrong.


9. The book becomes addressable software

Once the site exposes a family of operations —

search_book
get_chapter
find_concept
compare_chapters
run_experiment
inspect_evidence

— the book is no longer only text rendered in a browser. It has a semantic interface: an agent can retrieve a specific chapter, compare two, or locate a concept under a bounded contract, without being handed control of the page.

This is a smaller claim than “the book is an agent.” The book exposes capabilities. What discovers and uses them is a separate system, evaluated separately, starting in the next chapter.


10. Run the WebMCP search-book lab

Select Run with Browser AI from Chapter 18 to open:

/tools/ai/browser-ai-from-first-principles/18-chapter/

The lab has two separate paths. The live path calls document.modelContext.registerTool, inspects whether the surface is present, and records the exact failure when it is not — origin trial inactive, flag unset — rather than collapsing every case into “unavailable”. The simulator path exercises the same contract in-page; its events carry surface: in-page-simulator and are never counted as browser registrations.

Either way you can run the deterministic fixtures from section 8, inspect the identity snapshot (origin, contract version, registration ID, schema hash, definition hash, surface), and step through the lifecycle events: inspection, registration, proposal, argument validation, policy admission, execution, result validation, replacement, removal.

What this establishes: the search_book contract is registered through the current WebMCP surface where available, and its deterministic behavior is fully tested. What remains specified rather than observed: policy.modelSelectionClaim is not-observed — this chapter does not claim that a browser agent discovered or chose the registration. The recorded trace still shows only the Prompt API lifecycle. Behavioral evaluation of discovery and selection begins in Chapter 19.


Conclusion

A useful WebMCP tool combines a semantic name, typed inputs, safety annotations that inform rather than enforce, bounded results, application-side validation at every gate, and a complete identity and lifecycle trace.

search_book gives us a safe first mechanism. The next problem is behavioral: when several plausible tools exist, will an agent discover and choose the right one, with the right arguments, and actually use what it returns?


Sources and further reading

  1. Chrome for Developers, WebMCP.
  2. Chrome for Developers, WebMCP imperative API.
  3. Chrome for Developers, WebMCP tool security.
  4. Model Context Protocol, Specification.
  5. JSON Schema, Understanding JSON Schema.