---
title: "WebMCP tools this site gives to AI agents — Matt Pyle"
description: "Six live WebMCP tools on a personal site: four that read, two that write. Runnable examples, a tool manifest, and what Chrome actually does."
canonical: https://www.mattpyle.com/webmcp/
source: https://www.mattpyle.com/webmcp/
---

# WebMCP

Six typed tools this page hands to an agent, and what they return.

The same tools, as data [webmcp/tools.json](/webmcp/tools.json) [webmcp/index.json](/webmcp/index.json)

## Where the standard stands today

Spec status

A [Draft Community Group Report](https://webmachinelearning.github.io/webmcp/) of the W3C Web Machine Learning Community Group.

Browser support

An [origin trial in Chrome](https://developer.chrome.com/docs/ai/webmcp) from version 149. No other engine implements it.

This site's token

Expires 17 Nov 2026. After that Chrome ignores it and the tools stop registering.

Discovery

Registration is the JavaScript API alone. The JSON endpoints here are this site's own convention.

## What WebMCP is

An agent driving an ordinary web page has to infer everything: which element is the search box, what a button does, whether a click worked. WebMCP replaces the inference with a contract. A page calls `registerTool` with a name, a description, and a JSON Schema for its inputs; the agent gets a function it can call and a return value it can parse. The same shape the Model Context Protocol gives a server-side tool, running in the page, with the page's own data and the visitor's own session.

Registration is feature-detected and additive. In a browser with no `modelContext` — which today is every browser except Chrome with the trial — the code returns immediately, writes no DOM and reads no layout. Remove the tools and the site is byte-for-byte what it was.

## The tool catalog

### describe\_site

read 1 of 6

Describe mattpyle.com: who the author is, what the site is, and which sections it has. Call this first for context about the site you're on.

Returns

An object with person, site, and sections.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'describe_site');
await document.modelContext.executeTool(tool, '{}');
```

No inputs. Call it as-is.

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

### get\_recent\_writing

read 2 of 6

List the most recent published articles on mattpyle.com, newest first, optionally filtered to a single tag.

| Input | Type | Required | Constraints |
| --- | --- | --- | --- |
| limit | integer | No | 1–20, default 5 |
| tag | string | No | — |

Returns

An object with posts: title, url, date, tags, description. If a tag matches nothing, posts is empty and the result adds a note, the unmatched tag, the unfiltered count, and the tags that do exist.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'get_recent_writing');
await document.modelContext.executeTool(tool, '{"limit":5}');
```

Both inputs are optional.

Limit (optional)

How many articles to return (1-20).

Tag (optional)

Only return articles carrying this tag (case-insensitive).

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

### search\_content

read 3 of 6

Search the titles, descriptions, and tags of every published article, project, and changelog entry on mattpyle.com.

| Input | Type | Required | Constraints |
| --- | --- | --- | --- |
| query | string | Yes | min length 1 |

Returns

An object with results: type, title, url, snippet. A query that matches nothing returns an empty results with a note naming the query and the corpus that was searched.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'search_content');
await document.modelContext.executeTool(tool, '{"query":"webmcp"}');
```

One required input.

Query

Text to search for (case-insensitive).

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

### set\_appearance

write 4 of 6

Switch mattpyle.com between its modern appearance and a retro, GeoCities-era skin. This changes only the calling browser's own view (stored in that browser's localStorage) — it never affects the site for other visitors. Pass mode: 'retro' or 'modern'.

| Input | Type | Required | Constraints |
| --- | --- | --- | --- |
| mode | string | Yes | modern | retro |

Returns

An object with mode and message: the mode actually applied.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'set_appearance');
await document.modelContext.executeTool(tool, '{"mode":"modern"}');
```

Writes to this browser only.

Mode

The appearance to switch to: 'modern' or 'retro'.

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

### sign\_guestbook

write 5 of 6

Sign the guest book on mattpyle.com with a name and a message. The entry is saved in the calling browser's own localStorage and nowhere else — no server receives it and no other visitor can see it. Entries signed through this tool are recorded and displayed as agent-written, which the form cannot claim and this tool cannot disclaim. Safe to retry: calling it again with the same name and message as the most recent entry returns that entry instead of writing a duplicate.

| Input | Type | Required | Constraints |
| --- | --- | --- | --- |
| name | string | Yes | min length 1 |
| message | string | Yes | min length 1 |

Returns

An object with ok, the entry that was written, and a confirmation message naming its number. A repeated identical call adds duplicate: true and returns the existing entry, unwritten.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'sign_guestbook');
await document.modelContext.executeTool(tool, '{"name":"an agent reading this page","message":"Called the tool from the catalog to see what the badge looks like."}');
```

Writes to this browser only.

Name

The signature on the entry (1-40 characters; longer names are trimmed).

Message

What the entry says (1-280 characters; longer messages are trimmed).

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

### list\_related\_sites

read 6 of 6

List the sites in the web ring on mattpyle.com: a hand-picked, one-directional ring of sites experimenting with the agentic web. Read-only. The ring is still being filled, so some entries are open slots with no URL.

Returns

An object with ring (name, description) and sites: name, url, description, status.

How an agent calls it

```
const tool = (await document.modelContext.getTools()).find(t => t.name === 'list_related_sites');
await document.modelContext.executeTool(tool, '{}');
```

No inputs. Call it as-is.

Disabled — needs JavaScript.

Output unavailable

// JavaScript is off, so nothing here can run.
// The definitions, inputs and snippets on this page
// are static text and still read fine without it.

## Declared on a page

These are not registered in script. They are forms this site already had, carrying `toolname` and `tooldescription` attributes, and Chrome builds the tool — input schema included — from the form's own controls when it parses the page. So the tool an agent calls and the form a visitor fills in are the same thing, and each one exists only while its page is open.

run\_audit [/audit/](/audit/)

The same form a visitor uses, running the same audit through the same rate limiter and the same 45-second budget as this site's /mcp and /a2a surfaces. It fetches the address given, about a dozen requests, obeying that site's robots.txt.

## How an agent uses them

Every page registers the tools on `document.modelContext` as it loads, including after a client-side navigation. An agent in the browser discovers them there and invokes one by name. Nothing is exposed over the network, there is no endpoint and no authentication — the tools live in the page you already have open.

Arguments go over as a **JSON string**, not an object; passing an object throws `Failed to parse input arguments`. Results come back as a JSON string too.

Chrome does not enforce the declared input schema, so every handler validates its own inputs: clamping `limit`, rejecting a blank query, falling back to modern for an unknown mode, trimming an over-long guest-book entry to the limits the visible form enforces.

## Testing them yourself

The run controls above work in any browser with no setup, because they call the handler directly. What they do not exercise is the protocol.

For that: Chrome 149 or later with the WebMCP origin trial, `chrome://flags/#enable-webmcp-testing`, and a client on the agent side — the Model Context Tool Inspector extension is the straightforward one.

All six tools have been driven that way on Chrome 150.0.7871.187, on 02 Aug 2026, against this production site. The read tools returned what the page renders, `set_appearance` flipped that browser's view, and `sign_guestbook` wrote an entry that appeared while the guest book was open, badged as agent-written.

To do the same on a site you own, read [how to implement WebMCP on a website](/writing/how-to-implement-webmcp-on-a-website/).

## Machine-readable resources

[/webmcp/tools.json The tool manifest: names, descriptions, input schemas, and an example call for each. Generated from the real tool objects at build time.](/webmcp/tools.json) [/webmcp/index.json The content index the tools themselves read: the author entity, the section map, and every published article, project, and changelog entry.](/webmcp/index.json) [/llms.txt The site index, written for machines and readable by anyone.](/llms.txt) [/llms-full.txt The full content export.](/llms-full.txt) [/agents.md How to read and cite this site, including what not to assume.](/agents.md)
