SkillAgentSearch skills...

agenticschema

Turn Schema.org, JSON-LD, RDFa and Microdata into tools for AI agents. Zero backend, browser-first, with MCP and WebMCP support

Install / Use

claude mcp add searchstefano -- npx -y github:searchstefano/agenticschema

If the server publishes to npm under a different name, use that package instead — check the repo README.

About this skill
🔌

MCP Server

Model Context Protocol server

Quality Score

72/100

Supported Platforms

Claude Code
Claude Desktop

Tags

AgenticSchema · GitHub license npm version PRs Welcome

Turn the Schema.org markup a page already has into MCP tools an AI agent can call.

Most pages already publish structured data. Agents still scrape them. This library closes that gap: it reads the JSON-LD, microdata and RDFa already in the page and emits Model Context Protocol tools. You write no new API and you run no backend.

              Website
                 │
                 │  the Schema.org markup it already publishes:
                 │  JSON-LD · microdata · RDFa
                 ▼
           AgenticSchema
                 │
                 │  one callable tool per thing the page describes
                 │
           ┌─────┴─────┐
           ▼           ▼
        WebMCP        MCP
       (browser)    (Node)
           └─────┬─────┘
                 ▼
               Agent

Here is what comes out of a page that exists today:

world.openfoodfacts.org/product/3017620422003

  read    get_web_site
  read    get_organization
  read    get_search_action
  action  search_web_site(search_term_string)

search_web_site is executable. An agent holding it queries Open Food Facts directly instead of guessing a URL or going through a search engine. Nobody published anything new to make that happen: the page has carried a SearchAction all along, and even the parameter name is the one the page itself declares in query-input.

Run that page yourself. No browser, and no transport to configure:

npx @agenticschema/server https://world.openfoodfacts.org/product/3017620422003

(get_search_action in that list is noise, a read tool over the action's own definition. It is a known rough edge, left visible rather than trimmed out of the example.)

On your own site it is one file. The script-tag build is a plain classic script with the WebMCP polyfill already inside, roughly 27 KB gzipped, so it goes wherever a <script> tag goes: a WordPress theme, a Shopify theme, a React, Next.js or Astro layout, or Google Tag Manager.

<script src="https://cdn.jsdelivr.net/npm/@agenticschema/browser@latest"></script>

You install no package and you configure no bundler. That tag reads the page and registers the tools, which is the whole of step one. Something still has to carry them to an agent: Chrome has run an origin trial for the native path since version 149, and there is a local relay for development. The next section is about that choice, and it is the part people get wrong.

Contents

The six sections worth reading before anything else. GitHub's own outline menu has the rest.


Read this first: registration is not transport

This is the single thing that trips people up, so it comes before everything else.

Getting a page's data to an agent takes two steps, and this library only does the first one:

   ①  REGISTRATION                        ②  TRANSPORT
   @agenticschema/browser                 a WebMCP-capable browser,
   reads the page's Schema.org            an extension, or a local relay
   markup and registers tools             carries those tools to the agent
   on document.modelContext
        │                                        │
        └──────────► document.modelContext ◄─────┘
                     (the meeting point)

@agenticschema/browser writes tools into document.modelContext. That is the whole job. It does not open a connection to anything, because a browser tab cannot listen on a port. See Three constraints.

So after adding the script tag you have a page whose tools are correctly registered and that no agent can reach yet. Nothing is broken; the second half is simply not there. You pick the transport separately, and the choice depends on who is meant to call the tools. See Choosing a transport.

The symptom of forgetting step ② is very specific and worth recognising: the tools show up in Chrome DevTools (Application panel) but your MCP client reports zero sources. DevTools reads document.modelContext in-process; your MCP client is a separate program that cannot. Everything is working, and nothing is connected.


Quick start

Two tags. The first registers the tools, the second carries them to a local MCP client such as Claude Desktop, Cursor or Claude Code.

<!-- ① registration: read this page's Schema.org markup, publish it as WebMCP tools -->
<script src="https://cdn.jsdelivr.net/npm/@agenticschema/browser@latest"></script>

<!-- ② transport (development only): bridge those tools to a local MCP relay -->
<script src="https://cdn.jsdelivr.net/npm/@mcp-b/webmcp-local-relay@4/dist/browser/embed.js"></script>

Then run the relay and point your MCP client at it:

{
  "mcpServers": {
    "webmcp-local-relay": {
      "command": "npx",
      "args": ["-y", "@mcp-b/webmcp-local-relay@latest"]
    }
  }
}

Open the page, and the tools appear in your client. Verify with webmcp_list_sources: your tab should be listed with a tool count above zero.

Four things worth knowing before you paste that in:

  • Order matters. The relay embed reads whatever is already registered and subscribes to changes, so put it after the registration tag.
  • Leave off type="module". The script-tag build is an IIFE, so it runs as an ordinary script, which is what makes it work through a tag manager. Adding type="module" still works, but it costs you document.currentScript and with it the simplest way to read options. See How the adapter finds its own tag.
  • Tag ② is for development. Shipping it to real visitors makes every one of their browsers probe 127.0.0.1. See Keep the relay out of production.
  • Pin your versions. Unversioned jsDelivr URLs are cached at the edge for days, long enough to keep serving a build you have already replaced. @latest and @4 above are pins.

If you only want the browser's own built-in agent to use the tools, you need tag ① alone.


Why this is worth doing at all

That first sentence is measurable rather than a pitch. Schema.org publishes the usage statistics from Google's crawl, counting how many domains use each term:

| Term | Domains | | ----------------- | ------- | | potentialAction | 10M+ | | SearchAction | 10M+ | | EntryPoint | 10M+ | | urlTemplate | 10M+ | | query-input | 10M+ |

Over ten million domains already declare how to search them, machine-readably, today. That is a capability this library hands to an agent as a callable tool, and nobody had to publish anything new for it to work. The vocabulary's tail is shorter than it looks, too: of 958 types, 16 appear on 10M+ domains, 50 on 1M+ and 95 on 100K+, so a hand-written profile registry can cover the part of the web that exists in practice.

One caveat worth stating plainly: those counts are what sites declare, not what is well formed enough to map. That is a different number, and the table does not claim it. Source: schemaorg/schemaorg data/public_stats/google, 2026-07.


Try it without committing to anything

Three ways in, in rising order of commitment.

1. In the browser, nothing installed

Open the playground →

Paste any JSON-LD and watch the tools appear. Try the hostile sample: it is the fastest way to see what the library refuses and why. Alongside it, a live page carrying the script tag for the WebMCP path end to end.

Both pages load the packages from jsDelivr at exact versions, so what you are trying is what you would ship, not a local build.

2. Read a real page from the terminal

No browser, and no transport question. The Node adapter fetches the page itself and speaks plain MCP over stdio:

npx @agenticschema/server https://en.wikipedia.org/wiki/Backpack

Wire it into Claude Desktop:

{
  "mcpServers": {
    "page": {
      "command": "npx",
      "args": ["-y", "@agenticschema/server", "https://en.wikipedia.org/wiki/Backpack"]
    }
  }
}

Every entity also becomes a readable MCP resource, which the browser adapter cannot do.

This is the shortest path to seeing real output, and the one with the fewest moving parts. If you are evaluating the library, start here.

3. On your own site

See Quick start above, then The script tag, in full.


The script tag, in full

Where the tag goes

The build is a single classic script with no bundler and no package install behind it, so anywhere you can paste a <script> tag will do:

| Platform | Where | | --- | --- | | WordPress | The theme's header template, or any plugin that inserts scripts into <head>. | | Shopify | theme.liquid, before the closing </head>. | | React, Next.js, Astro | The shared layout or document component, alongside your other third-party tags. | | Plain HTML | In <head>, or before </body>. | | Google Tag Manager, Cloudflare Zaraz | A Custom HTML tag. See Through a tag manager. |

Put it in the layout that every page shares rather than on one page, and as early as you can. In a single-page app the adapter follows history.pushState and route changes by itself, so a single tag covers every route with no extra wiring.

One thing can stop it outright: a Content-Security-Policy that does not allow the CDN. That is a one-line fix, or you can self-host the file. See Content-Security-Policy.

If you would rather import the package instead, it is on npm, and the JavaScript API takes the options the attributes cannot express.

How the adapter finds its own tag

To read its data-* options the adapter first has to find the tag it was loaded from. It tries three things, in order:

  1. document.currentScript, set while a classic script runs, including one a tag manager inserted, and null in a module script because the HTML specification says so,
  2. script[data-agenticschema], an explicit marker,
  3. script[src*="agenticschema"], the src of the standard snippet.

Since the build is an IIFE, the plain snippet takes rule 1 and everything works with no marker, whatever the file is called and however it got onto the page:

<script data-max-tools="8"
        src="https://cdn.jsdelivr.net/npm/@agenticschema/browser@latest"></script>

Adding type="module" gives up rule 1. The tag then has to be identifiable some other way: the URL above still matches rule 3, but a self-hosted copy under an unrelated filename matches nothing, and its options are ignored in silence.

<!-- module script, and nothing in the src says "agenticschema": rule 2 or nothing -->
<script type="module" data-agenticschema data-max-tools="8"
        src="/assets/webmcp-bundle.js"></script>

Using 0.1.2 or earlier? That build was ESM, so the tag needed type="module", rule 1 never applied and rul

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars3
CategoryCustomer
Updated14d ago
Forks0

Languages

TypeScript

Security Score

92/100

Audited on Aug 18, 2026

1 low