patrick@fribourg:~$ cat webmcp.md
My directory talks to agents
This is my discovery of the day, and I'm still a little giddy about it. I spent an evening teaching a web page to say what it can do. Now, when you ask ChatGPT for German cybersecurity feeds, it no longer opens my HTML and guesses: it calls a tool and gets back the answer the page computed. It's called WebMCP, it's brand new, and I haven't had this much fun with a browser API in years.
Start from this RSS directory: https://ok-ia.ch/fornews/annuaire.html — find the Cybersecurity category, and add CyberVeille and CERT-FR to my fornews.ai account. French only, and don't duplicate anything that's already there.
→ calls theme_bundle { theme: "cybersecurite", exclude_ai_optout: true }
{ "theme": "Cybersecurity", "feeds": 33, "ai_optout_excluded": true, "import_fornews": "https://ok-ia.ch/fornews/annuaire/ia-theme-cybersecurite.fornewssource", "page": "https://ok-ia.ch/fornews/annuaire.html#cybersecurite" }
Both feeds added, set to French, active. Neither was already in your account.
A real session, 8 October. The prompt is shortened; the JSON is what the page actually returned. No scraping, no screen control — the page answered.
cat the-problem.md
Until now, an agent that wanted something from your site had one method: load the page, read the HTML, guess what the buttons do, and click. It sort of works. It breaks the day you rename a CSS class. And above all, it asks a machine to do archaeology where answering would do.
WebMCP turns that around: the page declares its tools, and the agent calls them. It's a W3C draft from a community group, already implemented in the ChatGPT desktop app and in origin trial in Chrome and Edge. Ten lines are enough:
await document.modelContext.registerTool({ name: "search_feeds", description: "Search 1,716 verified news feeds by keyword, language and theme.", annotations: { readOnlyHint: true }, inputSchema: { type: "object", additionalProperties: false, /* … */ }, async execute({ query, language, theme }) { // … answer from what the page already holds in memory } });
The best part: it cost nothing. My directory already keeps 1,716 sources in memory to build its cards — the tools only declare what was already there. No extra request, not one byte for an ordinary visitor.
diff expected.txt actual.txt
Then I tried it. And nothing happened. I asked ChatGPT which tools it could see on the page, and it proudly answered:
"Chercher une source", "Toutes les langues", "Français · 432", "Masquer les 743 fermés aux plates-formes d'IA", "Ajouter", "Copier le flux", "Zapping"…
Those are my buttons. It was reading the DOM, exactly like before. A little later it even asked permission to drive my screen — the very thing WebMCP exists to avoid. My tools were there, alive in the browser, and completely invisible.
cat the-four-details.md
The answer was in OpenAI's documentation, which I had made the mistake of not reading before writing code. Four details. None of them is cosmetic: these are the signs by which an agent recognises a tool it may call without asking. Here they are — they cost me an evening, have them for free.
A module, not an IIFE
Tools must be registered in a top-level page module, during
initialisation. My <script defer> wrapped in an anonymous
function didn't count.
snake_case
I had written search-feeds. It wants
search_feeds. Yes — a hyphen against an underscore.
additionalProperties: false
The input schema has to close the door on properties it hasn't declared. Without it, the agent doesn't know what it is allowed to send.
readOnlyHint
The promise that the tool changes nothing. It makes all the difference: a tool that touches nothing can be called without interrupting the user.
And a fifth, which was my fault rather than the documentation's: I had put the tools only on the page that holds the data. Agents navigate. It landed on the directory, followed a theme link, and lost everything on the way. All 38 theme pages carry them now, with the data fetched on demand.
cat it-works.md
After the fixes, the same question. This time
it names search_feeds, list_themes,
theme_bundle. And when I ask it to call the third one:
{ "theme": "Cybersecurity", "feeds": 33, "ai_optout_excluded": true, "import_fornews": "https://ok-ia.ch/fornews/annuaire/ia-theme-cybersecurite.fornewssource" }
That URL exists nowhere: not in the HTML, not even in my JavaScript file. It is assembled at call time. You cannot get it by reading the page — which is the proof the tool actually ran. I grinned at my screen like an idiot.
cat what-really-matters.md
Here is the detail I'm proudest of, and it isn't technical. Of the 1,716
publishers in that directory, 743 refuse AI crawlers in their
robots.txt. I know because I've been collecting their
robots.txt for months.
These tools are called by an AI. Staying quiet about that refusal would be a way of working around it.
So every answer says so, source by source, and one flag leaves them out — the bundle it returns then skips those publishers entirely. I don't filter by default: the refusal targets training and crawling, not a human's RSS reader. But saying it is the least one can do.
One last principle I care about: all my tools are read-only. The site answers, it never acts. It hands back the address of an import file; the app, on your machine, decides whether to add the source. The public site gets no write powers at all — and never needed any.
cat try-it.md
You'll need the ChatGPT desktop app, updated, its built-in browser, and a model that supports this — the docs name GPT-5.6 Sol or GPT-6 Sol, and say WebMCP is disabled on Luna. Open the directory and ask something like:
Find me French cybersecurity feeds I can add to my RSS reader, skipping publishers that refuse AI crawlers.
In Chrome, enable about:flags#enable-webmcp-testing
first. And to check in five seconds that the API is there at all, open the console:
typeof document.modelContext.
play openai-walkthrough.mp4
OpenAI's own walkthrough, five minutes, and the clearest explanation of the idea I've found. Nothing is loaded from YouTube until you press play — the thumbnail is served from here, and the player only appears when you ask for it.
“Build custom site annotations with ChatGPT” — OpenAI
The spec is public, the code is readable, and OpenAI's video shows the idea in a few minutes. If you run a site that knows something, try it: it's an afternoon's work, and there is a strange pleasure in watching a machine ask politely instead of rummaging.
see the directory →Further reading: the WebMCP specification · OpenAI's documentation · their video.