Make your site understandable to every AI.
Describe your website once with a capability manifest at /ai2w. AI2Web exposes it across MCP, ACP, REST, GraphQL and more, with discovery, validation and a safe action model built in. This is the practical guide; normative detail lives in the specification.
Three steps to AI-ready
- Publish a manifest at
/ai2w- by hand, via an SDK, or the WordPress plugin. - Add the discovery anchor at
/.well-known/ai2wpointing to it. - Validate + connect - score it, then add your
/ai2w/mcpendpoint to any assistant.
Validate any site from the command line, or in the browser with the validator:
npx -p @ai2web/validator ai2web validate https://your-site.comPick your language
Six SDKs plus a WordPress plugin, all producing the same manifest and scoring identically.
npm install @ai2web/core # types, builder, validation, discovery
npm install @ai2web/server # /ai2w route handler (Node + Cloudflare)
npm install @ai2web/mcp-bridge # expose your capabilities as an MCP servernpm install @ai2web/core # ships as ESM with bundled types, no build step
npm install @ai2web/server # /ai2w route handler (Node + Cloudflare)
# Or drop the build-free <ai2w-badge> web component into any page, no bundler.npm install @ai2web/core @ai2web/react
# hooks: useDiscover / useValidate / useNegotiate + <Ai2wBadge/>pip install ai2webcomposer require ai2web/ai2webgo get github.com/ai2web-foundation/ai2web-godotnet add package Ai2Webgem install ai2web# Install the "AI2Web" plugin from the WordPress admin, then enable it.
# It serves /ai2w automatically from your posts, products and pages.Describe your site once
Capabilities are a map of module → boolean | object. A boolean declares presence; an object adds detail such as an endpoint. Commerce is one module among equals - the model is not commerce-centric.
import { ai2web, validateManifest } from "@ai2web/core";
const manifest = ai2web({ name: "Northwind", url: "https://northwind.com", type: "ecommerce" })
.capability("content")
.capability("commerce", { endpoint: "/ai2w/products", checkout: true })
.capability("search", { endpoint: "/ai2w/search" })
.transports({ mcp: { enabled: true, endpoint: "/ai2w/mcp" }, rest: { enabled: true } })
.auth({ methods: ["none", "oauth2"], oauth2: { pkce: true, scopes: ["checkout"] } })
.consent({ requires_user_approval_for: ["purchase"] })
.contact({ support: "[email protected]" })
.build();
const { score, tier } = validateManifest(manifest); // 100, "Enterprise"import { ai2web, validateManifest } from "@ai2web/core";
// The packages ship as standard ESM, so plain JavaScript works unchanged.
const manifest = ai2web({ name: "Northwind", url: "https://northwind.com", type: "ecommerce" })
.capability("content")
.capability("commerce", { endpoint: "/ai2w/products", checkout: true })
.transports({ mcp: { enabled: true, endpoint: "/ai2w/mcp" }, rest: { enabled: true } })
.contact({ support: "[email protected]" })
.build();
console.log(validateManifest(manifest).score); // 90+import { Ai2wBadge, useValidate } from "@ai2web/react";
export function Readiness() {
// Fetches /ai2w for the URL and scores it, live.
const { result, loading } = useValidate("https://northwind.com");
if (loading) return <span>Scanning...</span>;
return (
<div>
<Ai2wBadge url="https://northwind.com" />
<p>Score: {result?.score}/100 - {result?.tier}</p>
</div>
);
}from ai2web import Manifest, validate_manifest
manifest = (
Manifest.for_site("Northwind", "https://northwind.com", "ecommerce")
.capability("content")
.capability("commerce", {"endpoint": "/ai2w/products", "checkout": True})
.capability("search", {"endpoint": "/ai2w/search"})
.transports({"mcp": {"enabled": True, "endpoint": "/ai2w/mcp"}, "rest": {"enabled": True}})
.auth({"methods": ["none", "oauth2"], "oauth2": {"pkce": True, "scopes": ["checkout"]}})
.consent({"requires_user_approval_for": ["purchase"]})
.contact({"support": "[email protected]"})
.build()
)
result = validate_manifest(manifest) # {"score": 100, "tier": "Enterprise", ...}use Ai2Web\Manifest;
use Ai2Web\Validator;
$manifest = Manifest::forSite('Northwind', 'https://northwind.com', 'ecommerce')
->capability('content')
->capability('commerce', ['endpoint' => '/ai2w/products', 'checkout' => true])
->capability('search', ['endpoint' => '/ai2w/search'])
->transports(['mcp' => ['enabled' => true, 'endpoint' => '/ai2w/mcp'], 'rest' => ['enabled' => true]])
->auth(['methods' => ['none', 'oauth2'], 'oauth2' => ['pkce' => true, 'scopes' => ['checkout']]])
->consent(['requires_user_approval_for' => ['purchase']])
->contact(['support' => '[email protected]'])
->build();
$result = Validator::validate($manifest); // ['score' => 100, 'tier' => 'Enterprise']import "github.com/ai2web-foundation/ai2web-go"
manifest := ai2web.ForSite("Northwind", "https://northwind.com", "ecommerce").
Capability("content", true).
Capability("commerce", map[string]any{"endpoint": "/ai2w/products", "checkout": true}).
Capability("search", map[string]any{"endpoint": "/ai2w/search"}).
Transports(map[string]any{
"mcp": map[string]any{"enabled": true, "endpoint": "/ai2w/mcp"},
"rest": map[string]any{"enabled": true},
}).
Auth(map[string]any{"methods": []string{"none", "oauth2"}, "oauth2": map[string]any{"pkce": true}}).
Consent(map[string]any{"requires_user_approval_for": []string{"purchase"}}).
Contact(map[string]any{"support": "[email protected]"}).
Build()
result := ai2web.Validate(manifest) // score 100, tier "Enterprise"using Ai2Web;
var manifest = Manifest.ForSite("Northwind", "https://northwind.com", "ecommerce")
.Capability("content")
.Capability("commerce", new() { ["endpoint"] = "/ai2w/products", ["checkout"] = true })
.Capability("search", new() { ["endpoint"] = "/ai2w/search" })
.Transports(new() {
["mcp"] = new Dictionary<string, object?> { ["enabled"] = true, ["endpoint"] = "/ai2w/mcp" },
["rest"] = new Dictionary<string, object?> { ["enabled"] = true },
})
.Auth(new() { ["methods"] = new[] { "none", "oauth2" } })
.Consent(new() { ["requires_user_approval_for"] = new[] { "purchase" } })
.Contact(new() { ["support"] = "[email protected]" })
.Build();
var result = Validator.Validate(manifest); // Score 100, Tier "Enterprise"require "ai2web"
manifest = Ai2Web.ai2web(name: "Northwind", url: "https://northwind.com", type: "ecommerce")
.capability("content")
.capability("commerce", endpoint: "/ai2w/products", checkout: true)
.capability("search", endpoint: "/ai2w/search")
.transports(mcp: { enabled: true, endpoint: "/ai2w/mcp" }, rest: { enabled: true })
.auth(methods: %w[none oauth2], oauth2: { pkce: true, scopes: ["checkout"] })
.consent(requires_user_approval_for: ["purchase"])
.contact(support: "[email protected]")
.build
result = Ai2Web.validate(manifest) # { score: 100, tier: "Enterprise", ... }The AI2Web plugin generates your manifest automatically. No code required:
1. Install and activate the "AI2Web" plugin.
2. It maps your content, WooCommerce products and pages to capabilities.
3. /ai2w and /.well-known/ai2w go live instantly, scored 100/100.
4. Optional: enable Agent checkout and OAuth2 + PKCE in Settings -> AI2Web.
Works with WordPress 7.0 native AI (Abilities API + AI Client).One handler, every route
The SDK server handlers emit /ai2w, the well-known anchor, actions, and the /llms.txt + /.well-known/agent.json projections from a single manifest.
import { cloudflareHandler } from "@ai2web/server";
// One handler serves /ai2w, /.well-known/ai2w, actions, and the
// /llms.txt + /.well-known/agent.json projections from a single manifest.
export default cloudflareHandler({ manifest });import { createServer } from "node:http";
import { nodeListener } from "@ai2web/server";
// Serves /ai2w, the well-known anchor, actions and the llms.txt +
// agent.json projections from one manifest.
createServer(nodeListener({ manifest })).listen(3000);from ai2web import handle
# Framework-agnostic: hand it the request method + path, get status/headers/body.
res = handle({"manifest": manifest}, request.method, request.path)
return Response(res["body"], status=res["status"], headers=res["headers"])use Ai2Web\Server;
$res = Server::handle(['manifest' => $manifest],
$_SERVER['REQUEST_METHOD'],
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
http_response_code($res['status']);
foreach ($res['headers'] as $k => $v) { header("$k: $v"); }
echo json_encode($res['body']);res := ai2web.Handle(
ai2web.ServerOptions{Manifest: manifest},
r.Method, r.URL.Path, nil, "",
)
for k, v := range res.Headers {
w.Header().Set(k, v)
}
w.WriteHeader(res.Status)
json.NewEncoder(w).Encode(res.Body)using Ai2Web;
var res = Server.Handle(manifest, ctx.Request.Method, ctx.Request.Path);
ctx.Response.StatusCode = res.Status;
foreach (var (k, v) in res.Headers) ctx.Response.Headers[k] = v;
await ctx.Response.WriteAsJsonAsync(res.Body);Endpoints
GET /.well-known/ai2w discovery anchor (required) - carries or points to the manifest
GET /ai2w the capability manifest (recommended canonical endpoint)
GET /ai2w/mcp MCP transport for actions and tools
GET /llms.txt projection for llms.txt-aware agents
GET /.well-known/agent.json projection for agent.json-aware agents
POST /ai2w/negotiate capability + transport negotiationDiscovery is read-only and safe: fetching a manifest never changes state. The /llms.txt and agent.json routes are projections of the same manifest, so agents that speak those formats work without parsing ai2w first, while /ai2w stays authoritative for execution.
The vocabulary
| Module | What it declares | Spec |
|---|---|---|
content | Readable, structured content: articles, pages, docs, FAQs | RFC-0008 |
search | A query interface over content and catalog | RFC-0008 |
commerce | Products, cart, checkout, returns, inventory | RFC-0005 |
support | Order tracking, returns, refunds, cancellations, issues | RFC-0007 |
actions | Declared operations an agent can call | RFC-0002 |
events | Subscribable events (order shipped, price drop) | RFC-0002 |
agent | The site's own AI agent (agent-to-agent) | RFC-0004 |
identity | Legal name, policies, support and security contacts | Spec 4.3 |
extensions | Namespaced x-<vendor> extensions | RFC-0010 |
Safe by construction
High-risk actions never auto-run. The flow is: discover → prepare → the site returns a preview → the user approves → execute → confirmation with an audit reference. In the reference adapters this is enforced in one shared executor, and a high-risk action previews even if a manifest tries to declare approval: false. Credentials are never sent cross-origin, and outbound targets are checked against an SSRF guard before every request.
One model, many protocols
The executing adapters - @ai2web/mcp-bridge, acp-adapter, graphql-adapter, openapi-adapter - all route through one guarded executor, so the approval, same-origin credential and SSRF rules hold uniformly across every transport.
Works with connectors today
An AI2Web MCP endpoint works with existing assistant connectors - no vendor needs to adopt ai2w first.
- Claude: add your
/ai2w/mcpURL as a custom connector, orclaude mcp add --transport httpin Claude Code. - ChatGPT: add the same endpoint via developer mode; Streamable HTTP is supported.
- Grok: point it at the same
/ai2w/mcpendpoint; xAI's API supports MCP servers. - Any MCP client: point it at
/ai2w/mcp. The network connector fronts many sites at once.
Ready to check your site?
Get an AI Readiness score in seconds.
Get listed, verified
The Discovery Network (directory.ai2web.dev) lets agents find your site without knowing its URL in advance. Every listing is verified server-side: the directory re-fetches your live /.well-known/ai2w, validates it, and requires the manifest's origin to match. Submitted data is never trusted, so the directory cannot be spammed with sites that are not really AI-ready. A periodic health check re-verifies each listing.
Three ways to get listed:
Scan your site at ai2web.dev/validator, then click "Add to directory".
The backend verifies your live manifest before listing - one click, no account.import { createAi2wHandler } from "@ai2web/server";
// Announces this origin to the directory once, on its first discovery serve.
export default createAi2wHandler({ manifest, announce: true });curl -X POST https://directory.ai2web.dev/register \
-H 'content-type: application/json' \
-d '{"url": "https://your-site.com"}'One endpoint, every site
You do not have to add each site's MCP endpoint one by one. Add the single network connector -connector.ai2web.dev/mcp - to your assistant and discover and use every AI-ready site through it. It exposes find_sites (search the directory), describe_site (read a site's capabilities and actions), and call_site_action (invoke an action, approval-gated for high-risk).
# Add the network connector once - then find + use any AI-ready site.
claude mcp add ai2web --transport http https://connector.ai2web.dev/mcp
# Or add https://connector.ai2web.dev/mcp as a custom connector in the Claude app.Point your MCP client at: https://connector.ai2web.dev/mcp
Tools:
find_sites(capability?, type?, q?) discover AI-ready sites
describe_site(url) read its capabilities + actions
call_site_action(url, action, input, confirm?) invoke an action