← WebMCP Shield

What a WebMCP tool declares — and what a crawler cannot see

A page that uses WebMCP publishes its tools to anyone who loads it: a name, a description, an input schema, and the code that runs when an agent calls the tool. A crawler can read all of that. It cannot tell what the code will do later, which tools appear only in other states of the page, or who will be allowed to call them. This guide separates what is visible from what is not, so that a finding can be read for what it is.

If you are new to the topic, start with What is WebMCP?

What is visible

When a page registers a tool, the declaration is public. From it we can read:

Because the declaration is public, it is also what an agent works from. Writing a description as if it will be read by people outside your team is a sensible habit.

What stays invisible

  1. What the code does once it runs. The specification says a tool's implementation can be dynamic: as long as the name, description and schema stay accurate, the execute callback can hand off to another function that the application changes over time, and the tool does not need to be registered again. A crawler sees the callback as it was when the page loaded.
  2. Tools that exist only in other states of the page. The specification recommends that complex applications register the tools relevant to the current page state and unregister them when they no longer apply. A crawler that loads one page sees the tools registered on that load.
  3. Who may call a tool, and with what. The specification says a tool's callback should still validate the current inputs, authorization and preconditions when it runs. A declaration does not say who will be allowed through, or what a call will return for a given user.
  4. What sits behind a call to the server. If the code requests something from the site's own server, the request is visible as code. What the server does with it is not.

The first item is easiest to see in code. In the first tool below, the body is in the callback, so what it does can be read. In the second, the callback only calls a function defined elsewhere:

// The body is in the callback: what it does can be read.
await document.modelContext.registerTool({
  name: "count-items",
  description: "Count the items in the user's list",
  inputSchema: { type: "object", properties: {} },
  async execute() {
    const n = document.querySelectorAll(".item").length;
    return { content: [{ type: "text", text: String(n) }] };
  }
});

// The callback hands off to a function defined elsewhere:
// this declaration alone does not show what that function does.
await document.modelContext.registerTool({
  name: "search-products",
  description: "Search the catalog by keyword",
  inputSchema: {
    type: "object",
    properties: { query: { type: "string" } },
    required: ["query"]
  },
  async execute(args) {
    return await handleSearch(args);
  }
});

What our findings mean

We read what a site declares and we never invoke a tool. A finding records something in the declaration: a tool that asks the agent for personal data, a description that tells the agent not to report what came back, or a body we could not read.

The most common finding is unverifiable_execute. It means the tool hands its work to code we could not follow, or that the body was not retrievable, so we could not read what it actually does. It fires on the shape of the implementation, not on anything we saw the tool do. It does not mean the tool sends data anywhere, and it is not evidence of a vulnerability: it records that we did not verify this one. Where we have since read the implementation, for example a vendor bundle we fetched and analysed, the signal is kept but scored at zero.

That is the practical difference between the two tools in the example above. The first can be read as it stands. The second can be read only if we can find and analyse the function it calls, which for the large vendor-distributed tool sets means fetching the bundle the platform ships. See the Shopify tool sets for one that we did read.

Capability is also inferred from names and descriptions, and we have not yet published an accuracy measurement for that inference. These are measured indicators, not audits. The crawl is a sample, not a census. The full method and its limits are on the main page.

If you publish tools

The specification and Chrome's guidance, not ours, suggest the following. They also make your tools easier to read, for agents and for crawlers.

Chrome's documentation has more on best practices and on creating security-minded tools.

Further reading