Essays

AI-agents are the new type of users

Creative sites look cool, but clear sites win. Heres why clarity always leads to better results in web design.

Ivan Kalkaev

For most of the web's history, digital product design had one obvious user in mind: a person in front of a screen. The screen had to work for eyes, fingers, attention span, memory, impatience, habit, fatigue, and all the little mistakes people make when software asks too much of them. That job hasn't gone away. Most products still have plenty of work left there.

The new thing is that another operator is now present in the chain. A person gives an AI agent a goal, and the agent starts doing the boring middle part: reading pages, opening files, clicking controls, filling forms, calling tools, comparing options, writing into tables, and stopping when it needs confirmation. The person still owns the intent and the responsibility. The agent is just the executor. Still, in the day-to-day mechanics of the product, it behaves like a user.


OpenAI describes ChatGPT agent as a system that can use a virtual computer, work with websites, and operate on connected data. Anthropic's computer use docs describe Claude interpreting screenshots, clicking coordinates, typing text, and pressing keys. Google's web.dev article on agent-friendly sites already treats delegated user tasks as a live web design problem. The category has left slideware and entered production. Agents are already operating ordinary products, often through interfaces that were never built to be read by anything except a human who can squint and infer the missing structure. That is the frame I find useful: design now has to cover the screen a person sees and the computable layer an agent can act on. Intent, actions, permissions, state, risk, result, and error all need to exist somewhere other than the pixels. The word "agentic" makes this sound more mystical than it is. Most of the work is inventory, semantics, contracts, and permissions. Dry stuff. Which is usually where the real product architecture lives.


1. The design problem has moved under the interface

The old interface question was whether a person could understand what to do next. That is still the first test. The second test is whether an agent can extract what exists on the page, what changed, which actions are available, what it is allowed to do, and what will happen after it acts. A polished card can still be useless to an agent. The product image looks good, the price is visually balanced, the buy button sits in the right place, the hover state is tasteful. Underneath, the name may have no clear link to the price, the variant may be implicit, the button may have no accessible name, and the availability state may be just a color. A person glues those pieces together with visual memory. An agent needs the relationship to be present in the structure.

This is where a lot of modern UI starts to look fragile. Custom controls, decorative wrappers, icon-only actions, hover-only explanations, motion-heavy state changes, modals stacked over modals, PDFs used as data containers, and tables rendered as images all make sense in a certain visual culture. They also ask the agent to reconstruct the product model from clues. Sometimes it will manage. Sometimes it will click the wrong thing with great confidence.




2. Agents read signals, not taste

An agent using a browser does not experience a website as a person does. Even visual browsing is mediated through screenshots, model inference, DOM structure, accessibility trees, text extraction, browser tools, APIs, and any direct actions the product exposes.

Google describes three main channels: screenshots, raw HTML, and the accessibility tree. Screenshots help with spatial cues, but they are slow, token-expensive, and brittle. HTML gives nesting and document structure. The accessibility tree gives roles, names, states, and relationships in a form originally built for assistive technology. Agents often combine these channels because each one fails differently. A button drawn in Figma still needs a button role in the page model. A product list needs real product objects rather than a row of cards separated by spacing. An error needs to be tied to the field it explains. Price, date, availability, risk, permission, and confirmation status need to be readable facts.

Accessibility matters here for the same reason it has always mattered, plus one more. WCAG 2.2, semantic HTML, accessible names, logical source order, focus handling, and properly connected form errors make products usable by more people. They also make products easier for agents to parse. I do not love that some teams need the agent argument to take accessibility seriously, but fine. Take the win and fix the markup.



3. Three layers of agent access

Agent access will live across three layers for a while: visual interpretation, structural interpretation, and direct capabilities. Old sites will be operated through screenshots and clicks. Better sites will be read through semantic HTML, accessibility trees, and structured data. Mature products will expose actions through APIs, MCP, WebMCP, commerce protocols, and capability catalogs.

The visual layer is the most human-like and the easiest to break. A screenshot gives the agent clues about hierarchy, grouping, and state, but layout shifts, overlays, animation, tiny hit areas, ambiguous icons, and delayed loading all raise the failure rate. Screenshot use is useful as a fallback. Treating it as the main integration path is expensive theater.

The structural layer is where HTML, DOM order, accessibility roles, accessible names, states, and relationships start doing useful work. Chrome DevTools already lets teams inspect the accessibility tree, computed accessibility properties, ARIA attributes, and source order. That tooling was not made for AI, and it does not need to be. It already exposes the same kind of structure agents need.

The direct capability layer is cleaner. The agent stops imitating a person with a mouse and calls a product action: search flights, create a support ticket, fetch orders, prepare a payment, reserve a slot, draft a message, cancel a subscription. MCP gives model applications a standard way to connect to tools and data. WebMCP, in Chrome's early browser work, lets a site expose page-level actions to a browser agent. The standards will move around. The direction will not: agents need callable actions with constraints, not nicer guesswork.



4. Where ordinary interfaces break

Icon-only controls are the easy example. A person can read a cart icon, a pencil, a trash can, a heart, or a three-dot menu from context. An agent may see an unnamed button, an unlabeled graphic, or nothing useful. The damage gets worse when the action deletes, pays, publishes, sends, or cancels.

Custom controls create the same mess with better CSS. MDN's advice is plain: use the correct HTML element for the job. Native buttons already carry keyboard behavior, focus behavior, and activation semantics. A clickable div can look fine while failing as a button in the accessibility tree. If a team adds role="button", it has promised to implement the rest of button behavior. Plenty of interfaces make that promise and then quietly break it. Visual grouping without structural grouping causes subtler errors. A product card may look like one object, while the title, price, rating, discount, image, variant, and buy button are scattered across unrelated nodes. The person reads proximity. The agent needs an object relationship: this price belongs to this product, this discount expires at this time, this button applies to this variant, this shipping estimate depends on this address.

State often disappears into styling. Loading, selected, unavailable, failed, saved, pending confirmation, completed. If those states live only as color, icon swaps, spinners, or nearby text, the agent can double-click, proceed too early, miss a failed save, or act on stale data. The same weakness annoys people too, which is the funny part. Agent readiness often exposes ordinary bad UI hygiene. Hover text, tooltips, carousels, image text, PDFs, and modal layers can carry supporting detail. They are bad places for conditions that change the decision. Shipping fees, medical disclaimers, cancellation terms, compatibility notes, legal consequences, and last-step payment warnings need a more stable home than a tooltip. Security is the sharper edge. OWASP treats prompt injection as one of the main LLM application risks. OpenAI has written about malicious instructions hidden in web pages, emails, comments, and documents. An agent that reads external content and can act on private data is exposed to instructions the user never gave. The more power the agent has, the less acceptable it is to let arbitrary page text steer the workflow.



5. Semantics before visual improvisation

The goal is to keep the visual richness while moving meaning out of pixels alone. Human interface design works with visual hierarchy: size, color, rhythm, grouping, contrast, state, and motion. Agent-ready design adds another hierarchy underneath it: role, name, relationship, permission, risk, consequence, reversibility, result.

Every visual fact that affects a decision should have a machine-readable twin. If the screen shows $29.99, the system should know amount, currency, tax status, availability, and validity window. If the UI says "last ticket", limited availability should exist as state. If a button pays for an order, the action model should know the order, amount, payee, permissions, confirmation requirements, and side effects.

A good interface can still look like a normal human product. It just has a computable skeleton. That skeleton is where the agent understands what the screen means.



6. What teams can fix now

Use native HTML before inventing clever controls. Buttons, links, fields, headings, lists, and tables should be exactly those things unless there is a real reason to do otherwise. ARIA is for describing complex widgets when native elements are insufficient; it is a bad cover story for broken markup. The WAI-ARIA Authoring Practices Guide is useful because it treats role, state, property, and keyboard behavior as one contract.

Accessible names should be written like product copy. A visible label can be short, but the accessible name needs enough context where ambiguity exists: add Bialetti Moka Express 3 cup to cart, remove shipping address, open delivery settings, compare this plan, cancel booking. That helps screen reader users, keyboard users, agents, and regular people who are tired of guessing which identical "More" link belongs to which row.

Forms need labels, hints, validation rules, and errors tied together. Required, invalid, disabled, saved, loading, and success states should exist in the structure rather than in color alone. Source order should follow the product logic, not the visual hack that made the grid easier to style. If a card looks coherent but the source order mixes content across several objects, the agent has to rebuild the model using layout residue. Do not hide critical information in hover states, modals, image text, or PDFs. If a fee, rule, deadline, risk, or restriction changes the action, make it structured. Each meaningful action should also return a clear result: what happened, when, which object or reference ID was created, what can be undone, and what still needs a person.

CAPTCHA is a blunt instrument. It blocks malicious automation and useful delegated agents at the same time. Products that want agent access need narrower controls: agent identity, scoped permissions, rate limits, human confirmation, and visible audit logs. Google's Web Bot Auth experiment points in that direction with cryptographic request signing for bots, so sites can distinguish declared identity from spoofed headers and IP addresses.



7. Structured data is no longer just SEO plumbing

Schema.org used to sit in the SEO bucket for most teams. Add product schema, maybe get a rich result, move on. That framing is too small now. It gives a shared vocabulary for Product, Offer, Event, Organization, Place, FAQ, HowTo, Review, AggregateRating, BreadcrumbList, Action, potentialAction, and EntryPoint. Google Search Central describes structured data as a standardized format that helps classify page content. Agents need the same kind of clarity, only with more pressure on correctness because they may act on the result. A product should be represented as a product. A date should carry timezone when that matters. A price should include amount and currency. An address should be structured. An action should include consequences. If JSON-LD says an item is in stock while the visible interface says it is unavailable, the agent may prepare the wrong order. For a person, that is annoying. For an agent, it is a system error with a polite tone.



8. Documentation is part of the interface now

The llms.txt proposal is a useful signpost even if it is still a practical initiative rather than an RFC-grade standard. It puts a Markdown file at the site root with a curated map of useful pages, instructions, and links to cleaner machine-readable resources. The underlying idea is right: agents need compact, current, high-signal entry points.

Cloudflare's 2026 Agent Readiness work looks at discovery, content, controls, and capabilities, including Markdown content negotiation, API Catalog, MCP Server Cards, OAuth discovery, and WebMCP. Their scan found robots.txt almost everywhere and newer agent-facing standards barely anywhere. That matches the state of the web. Most sites still assume a browser and a search crawler are the only serious readers. Agent-facing documentation should describe product capabilities, core entities, current canonical links, restrictions, usage rules, common workflows, versions, deprecated features, migrations, and available machine interfaces such as OpenAPI specs, MCP servers, and API catalogs. Stale documentation needs dates and context. A person may notice a deprecation warning. A model may copy the old integration path and run it with full confidence, which is how a small docs problem becomes a support ticket with teeth.



9. Actions need their own architecture

Button labels are the surface. The product's action model is the thing underneath. Every meaningful action should have a name, inputs, preconditions, permissions, side effects, reversibility, confirmation rules, result shape, audit behavior, and machine-readable errors. "Buy product" is a chain: select product, select variant, check inventory, calculate delivery, apply discount, show final price, get confirmation, create order, initiate payment, return receipt, expose cancellation or support.

Human UI often hides that chain behind screens. Agentic systems need enough of the chain exposed to delegate safely. Otherwise the agent clicks like a person while lacking the human's contextual intuition and without a stable way to know whether it understood the consequence. Mature products will keep a human representation and an agent representation of the same model. The human one is visual, explanatory, emotional, decision-oriented. The agent one is structured, scoped, auditable, and fit for direct action. If the two disagree, the product has a bug. The user may not see it immediately, but the agent will eventually find it.




10. API, OpenAPI, API Catalog, MCP, WebMCP

Direct APIs are the cleanest path when precision matters, but an API sitting in a developer portal is not enough. The agent has to discover it, understand it, authenticate to it, select the right operation, pass valid arguments, and recover from errors. OpenAPI 3.2 gives teams a language-agnostic description of HTTP APIs that humans and computers can read without source code or traffic inspection. RFC 9727 api-catalog adds a predictable discovery point at /.well-known/api-catalog, where a publisher can expose API descriptions, purposes, policies, and endpoints. MCP gives model applications a standard way to call tools and read data. WebMCP helps a web page expose page-specific capabilities to a browser agent during a visit.

The practical artifact is a capability catalog. The agent does not really want POST /orders; it wants to create an order draft for the current user, get an estimated total, cancel an order that has not shipped, prepare payment, or request human confirmation before execution. Those are different product actions with different risk levels.



11. Delegation without handing over the account

The worst version of agent access is simple and terrible: the agent logs in as the user, keeps the session, and clicks whatever the account can click. This will happen during the transition because it is easy. It is also a weak architecture. Permissions are too broad, delegation is vague, revocation is messy, audit trails are thin, and the system cannot reliably separate "the human did this" from "the agent did this because the human asked." OAuth 2.0 is closer to the right model because it grants limited access to a service on behalf of a resource owner through approval. Agent access needs short-lived tokens, narrow scopes, separate read and write permissions, human confirmation for irreversible actions, simple revocation, visible logs, and a clean split between preparation and execution.

The agent should get a mandate, not the whole account. Read the calendar. Draft an email. Find options. Prepare an order. Compare plans. Stage a cancellation. Payments, publications, deletions, legal actions, medical decisions, and high-impact transfers need a stronger boundary.




12. Security without pretending every bot is the same

Agent-friendly access means separating useful delegation from scraping, abuse, impersonation, and uncontrolled automation. Robots.txt remains a preference signal, and RFC 9309 is explicit that it is not authorization. Cloudflare says the same thing operationally: robots.txt expresses preferences but does not technically prevent access.

Cloudflare's Content Signals Policy adds newer machine-readable preferences for search, ai-input, and ai-train. Useful signal, not a wall. Real control starts with action risk.

Low-risk actions include public reading, search, comparison, and navigation. Medium-risk actions include reading personal data, creating drafts, or changing reversible settings. High-risk actions include sending messages, publishing, placing orders, cancelling services, or sharing personal data with a third party. Critical actions include payment, money transfer, medical instruction, legal action, trading, and data deletion.

High and critical actions need confirmation, constraints, auditability, and clear responsibility boundaries. OpenAI's ChatGPT agent documentation talks about explicit confirmation before real-world actions and active supervision for critical tasks. That is not safety theater. That is the minimum sane shape of delegated software.




13. Commerce will feel this first

Shopping delegates well. People already ask assistants to find a gift under a budget, compare vacuum cleaners, reorder pet food, find flights with tolerable layovers, assemble a cart, and wait for approval.

OpenAI and Stripe have pushed Agentic Commerce Protocol and Instant Checkout for purchases through AI agents. Google has announced Agent Payments Protocol, AP2, with attention to user control, authorization, and verifiable transaction trails. The exact winner matters less than the direction: commerce is moving toward structured catalogs, scoped payment authority, user confirmation, and clean merchant handoff.

For merchants, the catalog becomes infrastructure. Product, variant, price, currency, inventory, delivery, returns, seller, warranty, compatibility, restrictions, package contents. All of it needs to be accurate and structured. Checkout should separate order preparation from payment execution. Payment authority should be scoped by amount, merchant, expiry, and confirmation, with only the order data passed along. A weak agentic store makes the agent scrape product cards, guess variants, read shipping rules from a modal, and click a generic buy button. A strong one gives the agent a structured catalog, lets it calculate an order, prepares the purchase, and shows the person a confirmation before money moves. That difference looks technical until it starts deciding where the order goes.



14. Some products should keep friction

Agent access is not equally good for every product. If attention is the economic unit, an agent can weaken the model. Media, ad-supported sites, entertainment catalogs, recommendation platforms, and some marketing funnels monetize visits and engagement, not pure task completion. Some friction protects the user. Finance, healthcare, legal workflows, access management, personal data, and account recovery need carefully designed confirmation and audit paths. A slower step can be better design. Data-heavy products will also restrict machine access when extraction threatens the business. Flights, hotels, rentals, marketplaces, live pricing, closed databases, and professional directories will keep balancing user-agent usefulness against large-scale scraping. Products that want to become agent layers themselves may treat outside agents as competitors. Refusing all agent access has a cost. If two banks, marketplaces, travel products, or delivery services are equally good for humans, while one lets agents safely read, compare, stage actions, and request confirmation, that product becomes easier to delegate to. Agents will become a selection layer because some products will be less structurally dishonest than others.



15. A practical migration path

Start with an inventory of delegatable scenarios. Pick five to ten tasks a user would actually hand to an agent: find information, compare options, fill a draft, prepare an order, book a slot, check status, cancel a service, assemble a report. The question is what is useful and acceptable to delegate, not what an agent can technically stumble through.

Then map the actions. For each action, define risk, inputs, permissions, confirmation, reversibility, result, and audit behavior. This step tends to make teams uncomfortable because many products do not have a real action model. They have screens. Audit the accessibility tree across those scenarios. Use Chrome DevTools, Lighthouse, keyboard testing, and real browser agents. Inspect roles, names, states, source order, focus behavior, labels, errors, and ARIA. This is accessibility work, and it is also a machine-legibility diagnostic. Fix the cheap semantic damage first. Replace fake buttons with buttons, add field labels, connect errors to fields, repair headings, remove ambiguous links, expose states, and stabilize cards and forms. Then add structured content: Schema.org where appropriate, sitemaps, canonical pages, Markdown versions of important docs, llms.txt if it helps, and clear status and error pages.

Only after that does the programmatic layer make sense: OpenAPI descriptions, API Catalog, OAuth delegation, direct actions for the main scenarios, WebMCP where the page context matters, MCP where external tools and data matter. Building tools before fixing semantics usually just gives agents a more formal way to misunderstand the product. Access policy comes last in this pass, but it should not be vague. Separate search crawlers, AI training crawlers, agents acting on behalf of users, partner integrations, and malicious automated traffic. One bot policy for all of them is either too hostile or too naive.



16. New design artifacts

Agentic products need a few extra artifacts in the design process. A delegation map shows which user goals can be delegated, which are blocked, which require confirmation, and which require active supervision. An action registry lists meaningful operations across reading, creating, updating, deleting, publishing, paying, and sharing data, with permissions, risk, reversibility, interfaces, and audit behavior attached.

A semantic screen spec describes roles, accessible names, states, relationships, errors, and structured data alongside layout. An agent error map separates missing permission, invalid format, state conflict, stale data, required confirmation, unavailable action, and rate limit. A confirmation policy defines where an agent may act alone, where it may prepare a draft, where it must ask a person, and where execution is forbidden. The final artifact is an agent action log. The user should be able to see what the agent did, when it did it, on whose behalf, with which data, and what can be undone. Without that log, agent access is just account magic with better branding.



17. Product notes by category

E-commerce should start with catalog quality. Product, variant, price, inventory, delivery, returns, seller, rating, warranty, compatibility, and restrictions need structure. A visual dropdown barely connected to the SKU is a bad place for size selection. Keep order preparation separate from payment. SaaS should expose work actions as APIs and tools. An agent should be able to list projects, create tasks, prepare reports, find documents, and stage changes, while staying inside the user's role and delegated scope. Logs matter more here than animation polish. Media and content products need a strategy. They may want agents to find, quote, summarize, and route people back. They may want to restrict AI use, license access, or expose paid partner feeds. Robots.txt, Content Signals, clean excerpts, canonical pages, and commercial access paths all belong in that decision.

Financial services can use agents for reading, analysis, comparison, draft preparation, and explanation. Transaction execution needs limits, confirmations, and audit. In finance, lower friction is not automatically better design.

Healthcare products can use agents for scheduling, forms, information search, and question preparation. Records, medical instructions, and data transfer need explicit consent, data minimization, and clear identity of the acting party.

Travel and events products should make dates, time zones, cancellation rules, fees, availability, and restrictions machine-readable. These products often hide the expensive conditions behind tidy cards, which is exactly where agents make expensive mistakes.

Government and education services may benefit the most because the tasks are bureaucratic and multi-step. They also tend to have the worst machine readability: PDFs, old portals, unclear forms, and errors with no explanation. In that area, accessibility and agent readiness are almost the same project.



18. Metrics worth tracking

Track the share of main scenarios an agent completes without intervention, the share where it asks for confirmation correctly, the number of steps to completion, the number of ambiguous elements per scenario, and the failures caused by misread state. Track interactive elements with correct roles and names, forms with labels and connected errors, structured data coverage, current OpenAPI descriptions, API Catalog or discovery support, and fallback moves into screenshot-based interpretation. That last one is a tax. If the agent keeps falling back to vision, the product is probably hiding its own model.

Risk metrics matter too: dangerous actions stopped by confirmation policy, token and time cost per scenario, stale content appearing in agent answers, and cases where the agent can prepare but not execute. These belong in design QA next to accessibility, performance, and search hygiene.



19. Technical appendix: descriptors, arguments, contracts

Agent-ready products need machine-readable contracts. A contract should describe the entity, available actions, required arguments, risk level, result, errors, and execution policy. A descriptor is a formal description of an object or action that the agent can read without guessing. The human interface shows a product card, a price, and a buy button. The agent-facing model says this is product sku_123; it has price, currency, availability, and variants; it exposes cart.add; the action accepts skuId, quantity, and variantId; it modifies the cart; payment needs human confirmation. The screen should be a visual representation of that stricter model. It can still be expressive, but it should not be the only place where product meaning exists.



Interface semantics

<article aria-labelledby="product-title-123">
  <h2 id="product-title-123">Bialetti Moka Express 3 cup</h2>

  <p>
    <span>Price:</span>
    <data value="29.99" data-currency="USD">$29.99</data>
  </p>

  <p id="stock-123">In stock: 4 units</p>

  <button
    type="button"
    aria-describedby="stock-123"
    data-action="cart.add"
    data-entity-type="product"
    data-entity-id="sku_123">
    Add to cart
  </button>
</article>
<article aria-labelledby="product-title-123">
  <h2 id="product-title-123">Bialetti Moka Express 3 cup</h2>

  <p>
    <span>Price:</span>
    <data value="29.99" data-currency="USD">$29.99</data>
  </p>

  <p id="stock-123">In stock: 4 units</p>

  <button
    type="button"
    aria-describedby="stock-123"
    data-action="cart.add"
    data-entity-type="product"
    data-entity-id="sku_123">
    Add to cart
  </button>
</article>
<article aria-labelledby="product-title-123">
  <h2 id="product-title-123">Bialetti Moka Express 3 cup</h2>

  <p>
    <span>Price:</span>
    <data value="29.99" data-currency="USD">$29.99</data>
  </p>

  <p id="stock-123">In stock: 4 units</p>

  <button
    type="button"
    aria-describedby="stock-123"
    data-action="cart.add"
    data-entity-type="product"
    data-entity-id="sku_123">
    Add to cart
  </button>
</article>

data-action and data-entity-id are internal conventions rather than browser standards. They still help because designers, frontend engineers, QA, analytics, and agent-layer developers get a shared language. The agent needs to know which object the action belongs to.



Entity descriptor

{
  "type": "Product",
  "id": "sku_123",
  "name": "Bialetti Moka Express 3 cup",
  "price": {
    "amount": 29.99,
    "currency": "USD",
    "includesTax": true
  },
  "availability": "in_stock",
  "inventoryCount": 4,
  "variants": [
    {
      "id": "variant_3cup",
      "label": "3 cup",
      "available": true
    }
  ],
  "primaryActions": ["cart.add", "product.compare", "favorite.add"]
}
{
  "type": "Product",
  "id": "sku_123",
  "name": "Bialetti Moka Express 3 cup",
  "price": {
    "amount": 29.99,
    "currency": "USD",
    "includesTax": true
  },
  "availability": "in_stock",
  "inventoryCount": 4,
  "variants": [
    {
      "id": "variant_3cup",
      "label": "3 cup",
      "available": true
    }
  ],
  "primaryActions": ["cart.add", "product.compare", "favorite.add"]
}
{
  "type": "Product",
  "id": "sku_123",
  "name": "Bialetti Moka Express 3 cup",
  "price": {
    "amount": 29.99,
    "currency": "USD",
    "includesTax": true
  },
  "availability": "in_stock",
  "inventoryCount": 4,
  "variants": [
    {
      "id": "variant_3cup",
      "label": "3 cup",
      "available": true
    }
  ],
  "primaryActions": ["cart.add", "product.compare", "favorite.add"]
}

This can live in application state, an API response, JSON-LD, a catalog, or a separate agent layer. Public pages can use Schema.org where it fits. Private product logic will usually need a richer model.



Action descriptor

{
  "name": "cart.add",
  "title": "Add item to cart",
  "description": "Adds the selected product variant to the current user's cart.",
  "entityTypes": ["Product"],
  "riskLevel": "low",
  "sideEffects": ["cart_modified"],
  "requiresHumanConfirmation": false,
  "idempotency": "not_idempotent",
  "inputSchema": {
    "type": "object",
    "required": ["skuId", "quantity"],
    "properties": {
      "skuId": {
        "type": "string",
        "description": "Product identifier"
      },
      "variantId": {
        "type": "string",
        "description": "Variant identifier"
      },
      "quantity": {
        "type": "integer",
        "minimum": 1,
        "maximum": 10
      }
    }
  },
  "outputSchema": {
    "type": "object",
    "required": ["cartId", "lineItemId", "subtotal"],
    "properties": {
      "cartId": { "type": "string" },
      "lineItemId": { "type": "string" },
      "subtotal": {
        "type": "object",
        "properties": {
          "amount": { "type": "number" },
          "currency": { "type": "string" }
        }
      }
    }
  },
  "errors": [
    {
      "code": "OUT_OF_STOCK",
      "meaning": "The item is out of stock or the requested quantity is unavailable"
    },
    {
      "code": "VARIANT_REQUIRED",
      "meaning": "A product variant must be selected"
    }
  ]
}
{
  "name": "cart.add",
  "title": "Add item to cart",
  "description": "Adds the selected product variant to the current user's cart.",
  "entityTypes": ["Product"],
  "riskLevel": "low",
  "sideEffects": ["cart_modified"],
  "requiresHumanConfirmation": false,
  "idempotency": "not_idempotent",
  "inputSchema": {
    "type": "object",
    "required": ["skuId", "quantity"],
    "properties": {
      "skuId": {
        "type": "string",
        "description": "Product identifier"
      },
      "variantId": {
        "type": "string",
        "description": "Variant identifier"
      },
      "quantity": {
        "type": "integer",
        "minimum": 1,
        "maximum": 10
      }
    }
  },
  "outputSchema": {
    "type": "object",
    "required": ["cartId", "lineItemId", "subtotal"],
    "properties": {
      "cartId": { "type": "string" },
      "lineItemId": { "type": "string" },
      "subtotal": {
        "type": "object",
        "properties": {
          "amount": { "type": "number" },
          "currency": { "type": "string" }
        }
      }
    }
  },
  "errors": [
    {
      "code": "OUT_OF_STOCK",
      "meaning": "The item is out of stock or the requested quantity is unavailable"
    },
    {
      "code": "VARIANT_REQUIRED",
      "meaning": "A product variant must be selected"
    }
  ]
}
{
  "name": "cart.add",
  "title": "Add item to cart",
  "description": "Adds the selected product variant to the current user's cart.",
  "entityTypes": ["Product"],
  "riskLevel": "low",
  "sideEffects": ["cart_modified"],
  "requiresHumanConfirmation": false,
  "idempotency": "not_idempotent",
  "inputSchema": {
    "type": "object",
    "required": ["skuId", "quantity"],
    "properties": {
      "skuId": {
        "type": "string",
        "description": "Product identifier"
      },
      "variantId": {
        "type": "string",
        "description": "Variant identifier"
      },
      "quantity": {
        "type": "integer",
        "minimum": 1,
        "maximum": 10
      }
    }
  },
  "outputSchema": {
    "type": "object",
    "required": ["cartId", "lineItemId", "subtotal"],
    "properties": {
      "cartId": { "type": "string" },
      "lineItemId": { "type": "string" },
      "subtotal": {
        "type": "object",
        "properties": {
          "amount": { "type": "number" },
          "currency": { "type": "string" }
        }
      }
    }
  },
  "errors": [
    {
      "code": "OUT_OF_STOCK",
      "meaning": "The item is out of stock or the requested quantity is unavailable"
    },
    {
      "code": "VARIANT_REQUIRED",
      "meaning": "A product variant must be selected"
    }
  ]
}

The contract matters more than the button because the contract is what lets the agent act without guessing. It is also where the product states the cost of being wrong.



Policy descriptor

{
  "action": "order.place",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "allowedForAgents": true,
  "agentMayPrepareOnly": true,
  "agentMayExecute": false,
  "auth": {
    "required": true,
    "scopes": ["cart.read", "cart.write", "order.prepare"]
  },
  "rateLimit": {
    "maxCalls": 20,
    "window": "1h"
  },
  "audit": {
    "logInput": true,
    "logOutput": true,
    "visibleToUser": true
  }
}
{
  "action": "order.place",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "allowedForAgents": true,
  "agentMayPrepareOnly": true,
  "agentMayExecute": false,
  "auth": {
    "required": true,
    "scopes": ["cart.read", "cart.write", "order.prepare"]
  },
  "rateLimit": {
    "maxCalls": 20,
    "window": "1h"
  },
  "audit": {
    "logInput": true,
    "logOutput": true,
    "visibleToUser": true
  }
}
{
  "action": "order.place",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "allowedForAgents": true,
  "agentMayPrepareOnly": true,
  "agentMayExecute": false,
  "auth": {
    "required": true,
    "scopes": ["cart.read", "cart.write", "order.prepare"]
  },
  "rateLimit": {
    "maxCalls": 20,
    "window": "1h"
  },
  "audit": {
    "logInput": true,
    "logOutput": true,
    "visibleToUser": true
  }
}

This layer matters most for payments, messages, deletion, bookings, financial operations, medical flows, and legal consequences. Low-risk actions can often be delegated. High-risk actions should be prepared by the agent and confirmed by the human.



20. Action schemas

Every agent-accessible action needs a consistent vocabulary: name, title, description, entityTypes, inputSchema, outputSchema, riskLevel, sideEffects, isReversible, requiresHumanConfirmation, authScopes, rateLimit, preconditions, postconditions, errors, auditPolicy, and uiFallback. Treat this vocabulary as product infrastructure, because it is where design intent becomes executable behavior.

Bad schemas look flexible to the developer and vague to everyone else. The agent receives a bucket and has to guess what belongs inside:

{
  "inputSchema": {
    "type": "object",
    "properties": {
      "data": {
        "type": "string"
      }
    }
  }
}
{
  "inputSchema": {
    "type": "object",
    "properties": {
      "data": {
        "type": "string"
      }
    }
  }
}
{
  "inputSchema": {
    "type": "object",
    "properties": {
      "data": {
        "type": "string"
      }
    }
  }
}


Better schemas reduce wrong choices. They force the caller to name the decision it is making:

{
  "inputSchema": {
    "type": "object",
    "required": ["startDate", "endDate", "guests"],
    "properties": {
      "startDate": {
        "type": "string",
        "format": "date",
        "description": "Check-in date"
      },
      "endDate": {
        "type": "string",
        "format": "date",
        "description": "Check-out date"
      },
      "guests": {
        "type": "integer",
        "minimum": 1,
        "maximum": 8
      },
      "roomType": {
        "type": "string",
        "enum": ["standard", "deluxe", "suite"]
      }
    }
  }
}
{
  "inputSchema": {
    "type": "object",
    "required": ["startDate", "endDate", "guests"],
    "properties": {
      "startDate": {
        "type": "string",
        "format": "date",
        "description": "Check-in date"
      },
      "endDate": {
        "type": "string",
        "format": "date",
        "description": "Check-out date"
      },
      "guests": {
        "type": "integer",
        "minimum": 1,
        "maximum": 8
      },
      "roomType": {
        "type": "string",
        "enum": ["standard", "deluxe", "suite"]
      }
    }
  }
}
{
  "inputSchema": {
    "type": "object",
    "required": ["startDate", "endDate", "guests"],
    "properties": {
      "startDate": {
        "type": "string",
        "format": "date",
        "description": "Check-in date"
      },
      "endDate": {
        "type": "string",
        "format": "date",
        "description": "Check-out date"
      },
      "guests": {
        "type": "integer",
        "minimum": 1,
        "maximum": 8
      },
      "roomType": {
        "type": "string",
        "enum": ["standard", "deluxe", "suite"]
      }
    }
  }
}

Avoid abstract fields such as data, payload, and value when concrete names are available. Use enums where the choice set is known. Include units such as currency, timezone, grams, meters, or percentage. Separate object IDs and display names. Return { "amount": 3490, "currency": "RUB" } rather than forcing the agent to parse "3,490 RUB". Keep search and action separate. Name dangerous operations by consequence. placePaidOrder says more than submit.


21. OpenAPI, MCP, and WebMCP examples

OpenAPI is a good base for HTTP API descriptions, especially when agent policy lives near the operation through extensions. The point is to keep the operational contract close to the code path that enforces it.

paths:
  /cart/items:
    post:
      operationId: addCartItem
      summary: Add item to cart
      description: Adds a product or product variant to the current user's cart.
      x-agent-risk-level: low
      x-agent-confirmation-required: false
      x-agent-side-effects:
        - cart_modified
      security:
        - OAuth2:
            - cart.write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - skuId
                - quantity
              properties:
                skuId:
                  type: string
                variantId:
                  type: string
                quantity:
                  type: integer
                  minimum: 1
                  maximum: 10
      responses:
        "200":
          description: Item added
          content:
            application/json:
              schema:
                type: object
                required:
                  - cartId
                  - lineItemId
                properties:
                  cartId:
                    type: string
                  lineItemId:
                    type: string
        "409":
          description: Item unavailable
paths:
  /cart/items:
    post:
      operationId: addCartItem
      summary: Add item to cart
      description: Adds a product or product variant to the current user's cart.
      x-agent-risk-level: low
      x-agent-confirmation-required: false
      x-agent-side-effects:
        - cart_modified
      security:
        - OAuth2:
            - cart.write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - skuId
                - quantity
              properties:
                skuId:
                  type: string
                variantId:
                  type: string
                quantity:
                  type: integer
                  minimum: 1
                  maximum: 10
      responses:
        "200":
          description: Item added
          content:
            application/json:
              schema:
                type: object
                required:
                  - cartId
                  - lineItemId
                properties:
                  cartId:
                    type: string
                  lineItemId:
                    type: string
        "409":
          description: Item unavailable
paths:
  /cart/items:
    post:
      operationId: addCartItem
      summary: Add item to cart
      description: Adds a product or product variant to the current user's cart.
      x-agent-risk-level: low
      x-agent-confirmation-required: false
      x-agent-side-effects:
        - cart_modified
      security:
        - OAuth2:
            - cart.write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - skuId
                - quantity
              properties:
                skuId:
                  type: string
                variantId:
                  type: string
                quantity:
                  type: integer
                  minimum: 1
                  maximum: 10
      responses:
        "200":
          description: Item added
          content:
            application/json:
              schema:
                type: object
                required:
                  - cartId
                  - lineItemId
                properties:
                  cartId:
                    type: string
                  lineItemId:
                    type: string
        "409":
          description: Item unavailable

The x-agent-* fields are custom OpenAPI extensions. They keep the agent policy beside the operation instead of in a stale slide deck.

In MCP, an action usually looks like a tool. The same naming discipline applies there:

server.registerTool("cart_add", {
  title: "Add item to cart",
  description: "Adds the selected product or product variant to the current user's cart.",
  inputSchema: {
    skuId: z.string(),
    variantId: z.string().optional(),
    quantity: z.number().int().min(1).max(10)
  },
  outputSchema: {
    cartId: z.string(),
    lineItemId: z.string(),
    subtotal: z.object({
      amount: z.number(),
      currency: z.literal("USD")
    })
  },
  annotations: {
    readOnlyHint: false,
    destructiveHint: false,
    idempotentHint: false,
    openWorldHint: false
  },
  _agentPolicy: {
    riskLevel: "low",
    sideEffects: ["cart_modified"],
    requiresHumanConfirmation: false
  }
}, async ({ skuId, variantId, quantity }) => {
  const result = await cartService.addItem({ skuId, variantId, quantity });

  return {
    structuredContent: result,
    content: [
      {
        type: "text",
        text: JSON.stringify(result)
      }
    ]
  };
});
server.registerTool("cart_add", {
  title: "Add item to cart",
  description: "Adds the selected product or product variant to the current user's cart.",
  inputSchema: {
    skuId: z.string(),
    variantId: z.string().optional(),
    quantity: z.number().int().min(1).max(10)
  },
  outputSchema: {
    cartId: z.string(),
    lineItemId: z.string(),
    subtotal: z.object({
      amount: z.number(),
      currency: z.literal("USD")
    })
  },
  annotations: {
    readOnlyHint: false,
    destructiveHint: false,
    idempotentHint: false,
    openWorldHint: false
  },
  _agentPolicy: {
    riskLevel: "low",
    sideEffects: ["cart_modified"],
    requiresHumanConfirmation: false
  }
}, async ({ skuId, variantId, quantity }) => {
  const result = await cartService.addItem({ skuId, variantId, quantity });

  return {
    structuredContent: result,
    content: [
      {
        type: "text",
        text: JSON.stringify(result)
      }
    ]
  };
});
server.registerTool("cart_add", {
  title: "Add item to cart",
  description: "Adds the selected product or product variant to the current user's cart.",
  inputSchema: {
    skuId: z.string(),
    variantId: z.string().optional(),
    quantity: z.number().int().min(1).max(10)
  },
  outputSchema: {
    cartId: z.string(),
    lineItemId: z.string(),
    subtotal: z.object({
      amount: z.number(),
      currency: z.literal("USD")
    })
  },
  annotations: {
    readOnlyHint: false,
    destructiveHint: false,
    idempotentHint: false,
    openWorldHint: false
  },
  _agentPolicy: {
    riskLevel: "low",
    sideEffects: ["cart_modified"],
    requiresHumanConfirmation: false
  }
}, async ({ skuId, variantId, quantity }) => {
  const result = await cartService.addItem({ skuId, variantId, quantity });

  return {
    structuredContent: result,
    content: [
      {
        type: "text",
        text: JSON.stringify(result)
      }
    ]
  };
});

update is a weak tool name. updateDeliveryAddress, cancelBooking, prepareOrder, and confirmPayment tell the agent what the action does and how much risk it may carry.

WebMCP-style page registration helps a website expose page-specific capabilities to a browser agent that is already visiting the page. The page gets to state its own affordances:

window.webmcp?.registerTool({
  name: "support.createTicket",
  title: "Create support ticket",
  description: "Creates a support ticket for the current user's order.",
  inputSchema: {
    type: "object",
    required: ["orderId", "message"],
    properties: {
      orderId: { type: "string" },
      message: { type: "string", maxLength: 2000 },
      category: {
        type: "string",
        enum: ["delivery", "payment", "return", "other"]
      }
    }
  },
  riskLevel: "medium",
  requiresHumanConfirmation: true
});
window.webmcp?.registerTool({
  name: "support.createTicket",
  title: "Create support ticket",
  description: "Creates a support ticket for the current user's order.",
  inputSchema: {
    type: "object",
    required: ["orderId", "message"],
    properties: {
      orderId: { type: "string" },
      message: { type: "string", maxLength: 2000 },
      category: {
        type: "string",
        enum: ["delivery", "payment", "return", "other"]
      }
    }
  },
  riskLevel: "medium",
  requiresHumanConfirmation: true
});
window.webmcp?.registerTool({
  name: "support.createTicket",
  title: "Create support ticket",
  description: "Creates a support ticket for the current user's order.",
  inputSchema: {
    type: "object",
    required: ["orderId", "message"],
    properties: {
      orderId: { type: "string" },
      message: { type: "string", maxLength: 2000 },
      category: {
        type: "string",
        enum: ["delivery", "payment", "return", "other"]
      }
    }
  },
  riskLevel: "medium",
  requiresHumanConfirmation: true
});

The page can expose safe actions instead of waiting for an agent to reverse-engineer them from pixels. Cheaper. Less weird.



22. Components as action carriers

The useful chain is visual element to entity to action to argument schema to policy to result. A button labeled "Add to cart" maps to Product sku_123, action cart.add, input { skuId, variantId, quantity }, low risk, no confirmation requirement, and a result containing cartId, lineItemId, and subtotal.

Design systems can encode this directly. The component spec starts carrying product semantics:

Component: ProductCard
entityType: Product
entityId: string
primaryAction: cart.add
secondaryActions:
  - favorite.add
  - product.compare
requiredEntityFields:
  - name
  - price
  - availability
Component: ProductCard
entityType: Product
entityId: string
primaryAction: cart.add
secondaryActions:
  - favorite.add
  - product.compare
requiredEntityFields:
  - name
  - price
  - availability
Component: ProductCard
entityType: Product
entityId: string
primaryAction: cart.add
secondaryActions:
  - favorite.add
  - product.compare
requiredEntityFields:
  - name
  - price
  - availability

A component then carries action semantics as well as visual layout. That is where design and frontend architecture meet properly, instead of exchanging screenshots and hope.



23. Errors and confirmations

Generic errors block agents because they do not describe the next move. They turn a recoverable branch into a dead end.

{
  "error": "Something went wrong"
}
{
  "error": "Something went wrong"
}
{
  "error": "Something went wrong"
}

A useful error gives the agent a branch. It names the failure and points toward a possible recovery:

{
  "error": {
    "code": "OUT_OF_STOCK",
    "message": "The item is out of stock",
    "recoverable": true,
    "suggestedNextActions": [
      {
        "action": "product.findAlternatives",
        "arguments": {
          "skuId": "sku_123"
        }
      }
    ]
  }
}
{
  "error": {
    "code": "OUT_OF_STOCK",
    "message": "The item is out of stock",
    "recoverable": true,
    "suggestedNextActions": [
      {
        "action": "product.findAlternatives",
        "arguments": {
          "skuId": "sku_123"
        }
      }
    ]
  }
}
{
  "error": {
    "code": "OUT_OF_STOCK",
    "message": "The item is out of stock",
    "recoverable": true,
    "suggestedNextActions": [
      {
        "action": "product.findAlternatives",
        "arguments": {
          "skuId": "sku_123"
        }
      }
    ]
  }
}

If the item is out of stock, the agent can find alternatives. If permissions are missing, it can ask the user to sign in. If confirmation is required, it can present a summary. If data is stale, it can refresh the estimate. «‎Something went wrong»‎ mostly says the implementation got tired.

Confirmation should use the same discipline. Split dangerous operations into preparation and execution, then make that split visible in the contract:

{
  "name": "order.prepare",
  "riskLevel": "medium",
  "requiresHumanConfirmation": false,
  "output": {
    "orderDraftId": "draft_789",
    "total": {
      "amount": 54.90,
      "currency": "USD"
    },
    "requiresConfirmationFor": "order.confirm"
  }
}
{
  "name": "order.prepare",
  "riskLevel": "medium",
  "requiresHumanConfirmation": false,
  "output": {
    "orderDraftId": "draft_789",
    "total": {
      "amount": 54.90,
      "currency": "USD"
    },
    "requiresConfirmationFor": "order.confirm"
  }
}
{
  "name": "order.prepare",
  "riskLevel": "medium",
  "requiresHumanConfirmation": false,
  "output": {
    "orderDraftId": "draft_789",
    "total": {
      "amount": 54.90,
      "currency": "USD"
    },
    "requiresConfirmationFor": "order.confirm"
  }
}

The confirmation action carries the high-risk boundary. The name alone should make the cost of execution obvious:

{
  "name": "order.confirm",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "inputSchema": {
    "type": "object",
    "required": ["orderDraftId", "confirmationToken"],
    "properties": {
      "orderDraftId": { "type": "string" },
      "confirmationToken": { "type": "string" }
    }
  }
}
{
  "name": "order.confirm",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "inputSchema": {
    "type": "object",
    "required": ["orderDraftId", "confirmationToken"],
    "properties": {
      "orderDraftId": { "type": "string" },
      "confirmationToken": { "type": "string" }
    }
  }
}
{
  "name": "order.confirm",
  "riskLevel": "high",
  "requiresHumanConfirmation": true,
  "inputSchema": {
    "type": "object",
    "required": ["orderDraftId", "confirmationToken"],
    "properties": {
      "orderDraftId": { "type": "string" },
      "confirmationToken": { "type": "string" }
    }
  }
}

The agent prepares, the human confirms, and the system records what happened. That pattern will age better than a generic checkout call that quietly does six different things.




24. Design systems for agentic products

Design systems should add semantic passports to important components. A button spec should include visibleLabel, accessibleName, actionName, entityId, riskLevel, disabledReason, loadingState, successState, and errorState. It should forbid icon-only actions without names, "Next" without context, critical information hidden only in tooltips, and destructive actions without a confirmation state.

A product card spec should include productId, name, price.amount, price.currency, availability, primaryImage.alt, primaryAction, and variantState. Its agent requirements should say that the action button is linked to the product ID, price is not text-only, availability is not color-only, and variants have stable IDs.

That is the design-system version of agent readiness. Less glamorous than a new component library. More useful.



25. Readiness checklist

Before claiming agent readiness, a team should answer the boring questions. Each main action should have a machine name, input schema, output schema, risk level, and confirmation policy. Dangerous actions should have a separate confirmation step. The system should distinguish preparation from execution.

Errors should have codes. Buttons should be connected to entities. Product cards, events, bookings, documents, and orders should have stable identifiers. Price, date, currency, timezone, availability, and permissions should be structural data rather than strings the agent has to parse. Forms should have labels, descriptions, and connected errors. Custom components should fulfill their ARIA roles. Agent actions should be logged. Some scenarios should explicitly forbid agent execution. Human fallback should exist when the agent cannot finish the task.

If half the answers amount to "it's obvious in the interface", the product is not ready. It may be obvious to a person who already knows the pattern. That is not the same as being computable.



26. The useful formula

The old component model was visual block plus state plus behavior. The agentic component model adds entity, action, schema, and policy. That is the useful shift for designers and frontend engineers.

Teams do not need websites for robots. They need products where important actions have readable contracts. The person gets a normal interface. The agent gets a map of capabilities.

AI agents do not replace human interfaces. People still need screens for understanding, choice, trust, control, and meaning. The screen remains where the person decides and feels the product. It just cannot be the product's only interface anymore.

The new baseline is a product that speaks honestly to humans, browsers, assistive technologies, search systems, API clients, and agents. Different languages, same product reality.

Design the screen for the human. Design the structure for the machine. Design actions for delegation. Design permissions for safety. Design logs for trust. The agentic web will not start because one standard wins. It will start because products with clear semantics, accessibility, structured data, exposed actions, and sane authorization become easier to delegate to. The rest will remain beautiful pages that agents click through half-blind.




Sources

MORE INSIGHTS

LETS WORK TOGETHER

Have a project in mind? Wed love to hear about it. Lets create something great together!

LETS WORK TOGETHER

Have a project in mind? Wed love to hear about it. Lets create something great together!

LETS WORK TOGETHER

Have a project in mind? Wed love to hear about it. Lets create something great together!