# How to make a website agent-ready

> The three layers that make a site usable by AI agents — llms.txt for crawler context, WebMCP for in-browser tools, and A2A for agent-to-agent calls — with the working code we shipped on this validator.

"Agent-ready" means three different things to three different kinds of AI agent — and they stack. Here is exactly how we made this validator agent-ready, with the code we shipped, so you can copy the pattern.

There are three independent layers. Adopt any one on its own; together they cover a crawler reading your site, an agent driving your UI in the browser, and another agent calling you over the network.

## Layer 1 — llms.txt: context for crawlers

The baseline. A plain-text [llms.txt](/guide/what-is-llms-txt) at your root tells an AI crawler what your site is and which pages matter, in priority order. It's read at fetch time — no JavaScript, no protocol. Start here: [how to create one](/guide/how-to-create-llms-txt).

## Layer 2 — WebMCP: tools for in-browser agents

WebMCP lets a page expose callable *tools* to an AI agent running inside the browser, so the agent invokes a typed action instead of guessing at your form. There are two ways, and we ship both.

**Declarative** — annotate a real form and the browser synthesizes a tool from it:

```
<form toolname="open_llms_txt_report"
      tooldescription="Validate a site's llms.txt and open its report.">
  <input name="url" toolparamdescription="Domain or URL to validate">
</form>
```

**Imperative** — register a tool in JavaScript that returns structured data directly:

```
navigator.modelContext?.registerTool({
  name: "validate_llms_txt",
  description: "Validate a website's llms.txt; returns a 0-100 score and findings.",
  inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"] },
  execute: (input) => fetch("/api/validate?url=" + encodeURIComponent(input.url))
    .then(r => r.json())
    .then(data => ({ content: [{ type: "text", text: JSON.stringify(data) }] }))
});
```

Guard the imperative call behind `if (navigator.modelContext)` so it's a no-op where WebMCP is absent. It's a Chrome origin trial today, so real reach is small — treat it as a forward signal, not traffic.

## Layer 3 — A2A: a callable agent for other agents

The Agent2Agent (A2A) protocol lets other agents *discover and call* your service. You publish an agent card and back it with a JSON-RPC endpoint:

```
// GET /.well-known/agent-card.json
{
  "name": "llms.txt Validator",
  "url": "https://llms-txt-validator.dev/a2a",
  "skills": [{ "id": "validate_llms_txt", "name": "Validate llms.txt" }]
}
```

The `url` must point at a working endpoint that speaks A2A — we implement the synchronous `message/send` method, wrapping the same validation logic as a skill. The card is a contract, not a meta tag.

## The one rule: don't publish a signal you can't back

It's tempting to drop an empty agent card or fake form annotations to turn an audit green. Don't. An agent that fetches your card and calls a dead endpoint — or invokes a tool that does nothing — trusts you less afterward. Every signal here should resolve to something real. That's why this [validator](/) reports a WebMCP or A2A signal as "present" only when a working implementation backs it.

## How to verify each layer

  - **llms.txt** — paste your domain into the [validator](/); aim for 100/100 with no broken links.

  - **WebMCP** — in Chrome 149+, enable `chrome://flags/#enable-webmcp-testing` and run Lighthouse's Agentic Browsing audit; your tools should be listed.

  - **A2A** — fetch your `/.well-known/agent-card.json`, then POST a `message/send` call to its `url` and confirm you get a completed task back.
