Agent-Readable Web Standard (ARWS)
Agents that interact with web applications should not need to scrape HTML, click buttons, or reverse-engineer APIs. Web applications should be self-describing — exposing their capabilities in a machine-readable format that agents can discover, comprehend, and execute against without touching the GUI.
ARWS defines a four-layer pipeline for this. Each layer builds on the previous one. Together they turn any web application into a headless API that agents can use natively.
The Four Layers
graph TD
L1["<b>Layer 1: Discovery</b><br/>/.well-known/llms.txt<br/><i>Where are the tools?</i>"] --> L2
L2["<b>Layer 2: Schema</b><br/>OpenAPI 3.1+ / JSON Schema<br/><i>What do the tools accept?</i>"] --> L3
L3["<b>Layer 3: Transport</b><br/>MCP Server<br/><i>How do I call them?</i>"] --> L4
L4["<b>Layer 4: Safety</b><br/>OAuth scopes + HITL<br/><i>What am I allowed to do?</i>"]
Layer 1: Discovery
Agents need a deterministic entry point to index a site’s capabilities — the same way search crawlers use robots.txt.
Endpoint: Web servers host a manifest at /.well-known/llms.txt or /.well-known/agent-manifest.json.
Payload:
{
"name": "Acme Store",
"description": "E-commerce platform for electronics and accessories.",
"auth": {
"type": "oauth2",
"token_url": "https://acme.com/oauth/token",
"scopes": ["agent:read", "agent:draft_only"]
},
"tools": "https://acme.com/.well-known/openapi.json"
}
The manifest contains three things: a semantic summary of what the application does, authentication requirements, and a URI pointing to the tool definitions.
Layer 2: Tool Definition
Agents rely on strict typing and semantic context to generate valid payloads. Tool definitions use industry-standard schemas — not custom formats.
Specification: OpenAPI 3.1+ or native JSON Schema.
Semantic verbosity matters. LLMs need more context than human developers:
| Anti-pattern | Standard |
|---|---|
param: q | param: search_query — “The exact name or category of the product the user is looking for.” |
param: type with no constraints | param: product_type with enum: ["electronics", "accessories", "clothing"] and descriptions per value |
response: data | response: search_results with per-field descriptions |
Explicit enums with descriptions prevent the agent from hallucinating invalid parameter values. Every field that accepts a constrained set of values should declare them.
Layer 3: Transport
Without a standard transport, agents must learn a bespoke REST architecture for every website. MCP eliminates this.
sequenceDiagram
participant A as Agent
participant M as MCP Server
participant App as Application Backend
A->>M: tools/list
M-->>A: [{name: "search_products", inputSchema: {...}}, ...]
A->>M: tools/call {name: "search_products", arguments: {query: "USB-C cable"}}
M->>App: Internal API call
App-->>M: Product results
M-->>A: {content: [{type: "text", text: "Found 12 results..."}]}
The MCP server wraps the application’s existing API. The agent never calls the backend directly — it calls the MCP server, which handles authentication, validation, error formatting, and response serialization. The agent gets a consistent interface regardless of the backend’s architecture.
What MCP standardizes:
- How the agent discovers available tools (
tools/list) - How the agent passes execution payloads (
tools/call) - How the server returns results or actionable errors (so the agent can self-correct)
Layer 4: Safety
Autonomous agents operate probabilistically. The API must strictly separate read operations from irreversible actions.
Tool Classification
| Class | Access Level | Auth Required | Examples |
|---|---|---|---|
| Safe (read-only) | Data gathering | Minimal or none | search_products, get_weather, list_orders |
| State-changing (write) | Mutations | Agent-scoped OAuth token | create_order, update_profile |
| Destructive (irreversible) | Financial or deletion | Agent-scoped OAuth + HITL approval | transfer_funds, delete_account |
Idempotency
All tools must be idempotent. If an agent fires the same request twice, the system state must not corrupt — no double charges, no duplicate records. Use idempotency keys for any state-changing operation.
Human-in-the-Loop
For high-risk state changes, the API responds with 202 Accepted and triggers an asynchronous approval flow:
sequenceDiagram
participant A as Agent
participant API as Application
participant U as User (Phone/Email)
A->>API: tools/call {name: "transfer_funds", arguments: {amount: 500, to: "vendor"}}
API-->>A: 202 Accepted — "Pending human approval"
API->>U: "Your agent requests: Transfer $500 to Vendor. Approve / Deny?"
U-->>API: Approved
API->>API: Execute transfer
API-->>A: {status: "completed", transaction_id: "tx_892"}
The agent receives a pending status, not a rejection. It can continue other work while waiting for approval. This keeps the agent productive without granting unsupervised access to irreversible operations.
Implementation Stack
Discovery and Routing
| Tool | Function |
|---|---|
| Next.js / Nuxt / Astro | Auto-generate /.well-known/llms.txt at build time by scraping API routes |
| Express / FastAPI middleware | Detect agent User-Agent headers and route directly to the MCP server, bypassing HTML |
Schema Generation
| Tool | Function |
|---|---|
| Pydantic (Python) | .model_json_schema() converts typed models directly to JSON Schema |
| Zod (TypeScript) | .openapi() generates OpenAPI-compatible schemas from runtime validators |
| TypeSpec (Microsoft) | Readable API definition language that compiles to OpenAPI with semantic verbosity built in |
Protocol Integration
| Tool | Function |
|---|---|
| MCP SDK (TypeScript, Python) | Wrap existing APIs into an MCP-compliant server. Handles tool discovery, payload validation, and bidirectional communication |
Authentication and Approval
| Tool | Function |
|---|---|
| OAuth 2.0 with agent scopes | Auth providers (Auth0, Clerk, WorkOS) issue agent-specific tokens with restricted scopes (agent:read, agent:draft_only) |
| Approval webhooks | Services like Svix standardize outbound webhooks to prompt users: “Your agent requests [Action]. Approve / Deny?” |
ARWS and MCP
ARWS and MCP are complementary, not competing:
- MCP defines how agents communicate with tool servers — the wire format, tool discovery, and invocation protocol.
- ARWS defines how web applications expose themselves as MCP-compatible tool servers — the discovery endpoint, schema requirements, security classification, and approval flows.
MCP answers “how do agents call tools?” ARWS answers “how do websites become tools?”
A web application that implements all four ARWS layers is automatically compatible with any MCP client — Claude Code, OpenAI Codex, Gemini CLI, or any custom agent. The application becomes a tool that any agent can discover and use without custom integration.
Adoption Path
| Step | What to do | Effort |
|---|---|---|
| 1 | Add /.well-known/llms.txt with app description and API pointer | Minutes |
| 2 | Ensure API endpoints have OpenAPI 3.1 specs with semantic descriptions | Hours (if specs exist), days (if not) |
| 3 | Wrap the API in an MCP server using the SDK | Hours |
| 4 | Add agent-scoped OAuth tokens and classify tools by risk level | Days |
| 5 | Implement HITL approval webhooks for destructive operations | Days |
Steps 1-3 make the application agent-accessible. Steps 4-5 make it agent-safe. Start with discovery and schema — those unlock value immediately.