Ferrite Docs

Agent-native Apps

Serve people and agents from one application contract with HTML, Markdown, robots, llms.txt, OpenAPI, MCP, and WebMCP.

View as Markdown

Agent-native apps make their content and operations discoverable without asking an agent to scrape navigation chrome or reverse-engineer a visual interface. Humans and agents still use the same domain handlers and authorization rules.

Cloudflare reports that automated agents and bots now drive more than half of web requests. Its CEO Matthew Prince describes the resulting shift plainly: “the majority of traffic on the Internet is non-human.” That is a reason to design an explicit agent surface, not to make every bot trusted.

Add agent readiness

ferrite add agent-ready

Declare one Surface containing the application identity, canonical origin, human pages, routes, operations, JSON schemas, and read-only or idempotent traits. Route the well-known handler before application-specific routing.

Publish the discovery set

PathPurpose
/robots.txtCrawler policy and discovery links
/sitemap.xmlCanonical human and machine URLs
/llms.txtConcise app orientation and core links
/llms-full.txtBroader Markdown context
/index.md or Accept: text/markdownClean page representation
/openapi.jsonHTTP operation schemas
/.well-known/agent-card.jsonApp identity, skills, and interfaces
/.well-known/agent-readinessMachine-readable readiness evidence
POST /mcpStateless MCP tools
HTML pageWebMCP tools in the signed-in browser

Generate each representation from the same authored pages and operation declarations. A second hand-written “agent API” will drift.

Keep one authorization boundary

Public catalogue tools may be anonymous. Orders, tickets, evidence, staff schedules, and administrative actions require the same subject, tenant, role, ownership, and step-up checks as the UI. Server MCP does not inherit a browser cookie. WebMCP must not export that cookie into agent context.

Map an API operation

Operation {
    name: "list_products".into(),
    method: "GET".into(),
    path: "/api/products".into(),
    description: "List products with current price and availability.".into(),
    input_schema: Some(json!({
        "type": "object",
        "properties": { "query": { "type": "string" } },
        "additionalProperties": false
    })),
    read_only: true,
    idempotent: true,
    ..Default::default()
}

The MCP tool, OpenAPI operation, and WebMCP registration call the same list_products domain function.

Verify the served behavior

node tools/agent-score/agent-score.mjs scan http://127.0.0.1:8787

The score checks real responses and a real MCP tool call. A metadata file that claims an endpoint exists cannot make a missing endpoint pass.

Continue with WebMCP & OpenAPI and the Apps that implement the complete pattern.