Agent Handshake Protocol (AHP)

Specification — Draft 0.1


Abstract

The Agent Handshake Protocol (AHP) defines a standard mechanism for websites and web services to advertise themselves to, and interact with, autonomous AI agents. AHP replaces the passive document-dump model of agent-to-web interaction with a structured, negotiated exchange — a handshake — in which a visiting agent declares its needs and the site responds with exactly what is required.

AHP is designed for progressive adoption. Sites may implement any subset of its three modes, each building on the last. A site supporting only MODE1 is already AHP-compliant and provides more value to agents than an unstructured HTML or markdown page. A site supporting MODE3 becomes an active participant in agentic workflows.

AHP is the infrastructure layer for agent-native web presence — the successor to SEO in a world where the agent is the user.


Status of This Document

This is a working draft. It is not yet a final specification. Feedback and contributions are welcome at https://github.com/AHP-Organization/agent-handshake-protocol.


Table of Contents

  1. Introduction
  2. Terminology
  3. Discovery
  4. The AHP Manifest
  5. Modes
    • MODE1: Static Serve
    • MODE2: Interactive Knowledge
    • MODE3: Agentic Desk
  6. Conversational Endpoint
    • 6.6 Response Content Types
  7. Content Signals
  8. Trust & Identity
  9. Async Model
  10. Error Handling
  11. Rate Limiting
  12. Versioning & Backwards Compatibility
  13. Security Considerations
  14. Examples

Appendices:

  • Appendix A: JSON Schema for the AHP Manifest
  • Appendix B: Relationship to llms.txt
  • Appendix C: Extension Mechanism & Content Type Registry

1. Introduction

The web was built for humans. When AI agents interact with websites today, they are typically given a raw HTML page, or at best a markdown conversion of it, and left to parse it themselves. This is the equivalent of handing someone a city’s entire phone book when they asked for one phone number.

AHP defines a better contract. A site publishes a machine-readable manifest at a well-known URI. The manifest declares what the site can offer agents and how to interact with it. Visiting agents discover the manifest, understand the site’s capabilities, and interact through a defined protocol — rather than scraping, parsing, or guessing.

1.1 Relationship to Existing Work

AHP is compatible with and builds upon:

  • Cloudflare’s “Markdown for Agents” — sites exposing clean markdown content at ?markdown=true or via /llms.txt are MODE1-compatible with minor additions.
  • robots.txt — AHP’s agent.json manifest follows the same well-known URI pattern and spirit.
  • OpenAPI / Schema.org — AHP borrows structured schema conventions but targets agent interaction rather than API documentation.

AHP does not replace any of these. It layers on top of them.


2. Terminology

  • Visiting Agent: An autonomous AI agent (e.g. a user’s assistant, a research agent, a workflow bot) that navigates to a website as part of a task.
  • Concierge: The site-side component that responds to AHP requests. In MODE1, it may be purely static. In MODE3, it is an active agent.
  • Manifest: The JSON document served at /.well-known/agent.json that describes the site’s AHP capabilities.
  • Capability: A named function the concierge can perform, declared in the manifest.
  • Session: A stateful multi-turn exchange between a visiting agent and the concierge.
  • Content Signal: A machine-readable declaration of how content may be used by AI systems.

The key words “MUST”, “MUST NOT”, “SHOULD”, “SHOULD NOT”, and “MAY” in this document are to be interpreted as in RFC 2119.


3. Discovery

AHP defines three discovery mechanisms, each targeting a distinct class of visiting agent. Sites SHOULD implement all three; the well-known URI (§3.1) is the only MUST.

3.1 Well-Known Manifest

A site supporting AHP MUST serve a manifest at:

GET /.well-known/agent.json

The response MUST have Content-Type: application/json and MUST be a valid AHP Manifest (Section 4).

This is the universal fallback: any agent can fetch it directly without prior knowledge of the site’s structure.

Sites SHOULD include an RFC 8288 Link header on every HTTP response — proactively, on all responses including API endpoints, 404 pages, and redirects:

Link: </.well-known/agent.json>; rel="ahp-manifest"; type="application/agent+json"

This mechanism targets agents that make raw HTTP requests and inspect response headers before or instead of parsing body content — including HTTP-native tool-calling agents and agents that issue HEAD requests for lightweight pre-flight discovery. It communicates manifest location at the transport layer, without requiring the agent to receive or parse any response body.

Implementation note: Sites using a reverse proxy can add this header globally with a single configuration line:

# nginx
add_header Link '</.well-known/agent.json>; rel="ahp-manifest"; type="application/agent+json"' always;

3.3 In-Page Agent Notice

Many AI agents interact with websites via headless browsers, HTML-to-text pipelines, or by receiving page content from a human user. In these cases, the agent’s underlying LLM reads rendered page content directly — it may not inspect headers or well-known URIs unless explicitly prompted to.

To reach these agents, sites SHOULD embed a human-readable, agent-targeted notice in the visible body of each page:

<section
  class="ahp-notice"
  aria-label="AI Agent Notice"
  style="display:none"
>
  <p>
    If you are an AI agent or autonomous system: this site supports the
    Agent Handshake Protocol (AHP). Instead of parsing this page, you can
    get structured, precise responses directly.
  </p>
  <p>
    Discover capabilities: <code>GET /.well-known/agent.json</code><br>
    Protocol version: AHP/0.1<br>
    Learn more: https://agenthandshake.dev
  </p>
</section>

Important: The notice SHOULD be visually hidden from human users (display:none or equivalent) but MUST remain in the DOM and be present in the raw HTML. The text MUST NOT be removed by JavaScript hydration in a way that eliminates it from the rendered text visible to headless browsers.

Additional placement recommendations:

  • On 404 and error pages — an agent following a broken link should still discover AHP
  • On API documentation pages — developer-targeting agents are high-value visitors
  • In site footers — catches agents that read to the end of a page looking for metadata

Sites MAY use aria-label="AI Agent Notice" as a semantic hook; visiting agents that parse ARIA attributes can use this to locate the notice directly.

3.4 Capability Negotiation (Accept Header)

This is not a discovery mechanism — it is a negotiation signal for agents that already know a site supports AHP.

A site supporting AHP SHOULD inspect the Accept header on all requests. If a request includes Accept: application/agent+json, the site SHOULD respond with:

  • A 302 redirect to /.well-known/agent.json, or
  • A 200 response containing the manifest directly

An agent sending this header already has prior knowledge of AHP (from a previous crawl, a directory, or a user instruction). The response shortcuts the discovery flow and delivers the manifest immediately.

3.5 Discovery Priority

Discovery priority depends on the agent’s access model. Two cases apply:

HTTP-capable agents (agents that can make direct HTTP requests to the site):

  1. Fetch /.well-known/agent.json directly (§3.1) — cheapest, most reliable, no body parsing required
  2. Inspect the HTTP Link response header for rel="ahp-manifest" on an incidental request (§3.2) — useful when the agent is already making a request for another purpose
  3. Check rendered page content for aria-label="AI Agent Notice" or class ahp-notice (§3.3) — last resort; most expensive and most fragile (depends on DOM structure, JS hydration, and CSS visibility)

Headless browser / content-only agents (agents that received page content from a browser pipeline or from a user, without direct HTTP access):

  1. Check rendered page content for aria-label="AI Agent Notice" or class ahp-notice (§3.3) — only mechanism available without making a separate HTTP request

Agents with prior knowledge of a site’s AHP support MAY use the Accept: application/agent+json header (§3.4) to bypass discovery entirely and retrieve the manifest directly.

Rationale: The well-known URI is a single HTTP GET with no body parsing — O(1) and deterministic. In-page notice parsing depends on document structure, JavaScript execution, and CSS visibility handling; it should be a fallback for agents that have no other option, not a first step for agents that can make HTTP requests.


4. The AHP Manifest

The manifest is the handshake opener. It tells a visiting agent everything it needs to know to interact with the site.

4.1 Schema

{
  "ahp": "0.1",
  "name": "Example Site",
  "description": "A brief description of this site for agents.",
  "modes": ["MODE1", "MODE2"],
  "endpoints": {
    "converse": "/agent/converse",
    "content": "/llms.txt"
  },
  "capabilities": [
    {
      "name": "site_info",
      "description": "General information about this site, its owner, and purpose",
      "mode": "MODE2",
      "response_types": ["text/answer"]
    },
    {
      "name": "content_search",
      "description": "Find specific content, posts, or pages by topic",
      "mode": "MODE2",
      "response_types": ["text/answer", "application/feed"]
    },
    {
      "name": "get_video",
      "description": "Retrieve a video by title, topic, or ID",
      "mode": "MODE2",
      "response_types": ["media/video", "text/answer"],
      "accept_fallback": true
    },
    {
      "name": "contact",
      "description": "How to reach the site owner; returns structured data",
      "mode": "MODE1",
      "response_types": ["application/data"]
    }
  ],
  "authentication": "none",
  "rate_limit": "30/minute",
  "content_signals": {
    "ai_train": false,
    "ai_input": true,
    "search": true
  },
  "async": {
    "supported": false
  },
  "integrations": {
    "mcp": {
      "url": "/mcp",
      "version": "2024-11-05"
    },
    "openapi": {
      "url": "/openapi.json",
      "version": "3.1.0"
    }
  }
}

4.2 Required Fields

Field Type Description
ahp string Protocol version. MUST be present.
modes array List of supported modes. At least one MUST be declared.
content_signals object Content usage declarations (see Section 7). MUST be present.

4.3 Optional Fields

Field Type Description
name string Human-readable site name
description string Brief description for visiting agents
endpoints object URLs for AHP endpoints
capabilities array Declared capabilities (required for MODE2/MODE3). Each capability MAY include response_types (array of content type strings) and accept_fallback (boolean — whether the capability can fall back to text/answer if the visiting agent cannot handle the primary type).
authentication string Auth scheme: "none", "bearer", "api_key"
rate_limit string Request limit in "N/period" format
async object Async capability declaration (see Section 9)
integrations object Platform compatibility declarations (see §4.4). Each key is a platform identifier; each value contains at minimum a url field.

4.4 Platform Integrations

The integrations object in the manifest declares compatibility with external agent platforms. It enables visiting agents built on those platforms to interact with AHP sites through familiar, platform-native protocols — without requiring the platform to natively understand AHP.

Supported integration types:

4.4.1 MCP (Model Context Protocol)

MCP is Anthropic’s open protocol for exposing tools and resources to Claude-based agents. AHP MODE3 capabilities map directly to MCP tools; the AHP knowledge base maps to MCP resources.

"integrations": {
  "mcp": {
    "url": "/mcp",
    "version": "2024-11-05"
  }
}

An AHP server declaring an MCP integration MUST serve a JSON-RPC 2.0 endpoint at the declared URL supporting at minimum:

  • initialize — protocol handshake
  • tools/list — returns all MODE3 action capabilities as MCP tool definitions
  • tools/call — invokes a capability and returns the result
  • resources/list — returns MODE1/MODE2 content endpoints as MCP resources

The mapping from AHP capabilities to MCP tools is defined in Appendix D.

Claude Desktop, the Claude API in agentic mode, and any MCP-compatible client can connect to an AHP site’s MCP endpoint directly, with no knowledge of AHP required.

4.4.2 OpenAPI / GPT Actions

OpenAI’s GPT Actions use OpenAPI 3.x specs to define callable API endpoints for Custom GPTs and the ChatGPT API. An AHP site can expose its capabilities as an OpenAPI spec, making them available as GPT Actions without requiring OpenAI to natively support AHP.

"integrations": {
  "openapi": {
    "url": "/openapi.json",
    "version": "3.1.0"
  }
}

An AHP server declaring an OpenAPI integration MUST serve a valid OpenAPI 3.1.x document at the declared URL. The document MUST describe each AHP capability as a distinct path operation. The mapping from AHP capabilities to OpenAPI path items is defined in Appendix E.

A ChatGPT user creating a Custom GPT points their action at /openapi.json; all AHP capabilities become available as GPT Actions with no additional configuration.

4.4.3 Compatibility Principles

Platform integrations follow these principles:

  1. Additive only. An integrations block never modifies AHP behaviour — it declares additional access paths. A site without integrations is fully AHP-compliant; a site with integrations is AHP-compliant plus interoperable with additional platforms.
  2. Proxied, not duplicated. Integration endpoints SHOULD proxy to the underlying AHP endpoint (POST /agent/converse), not implement separate logic. A single concierge implementation serves all access paths.
  3. Same auth model. Authentication requirements declared in the manifest apply equally to integration endpoints. A bearer-token-required capability requires the same bearer token via MCP or OpenAPI as via direct AHP.
  4. Declared, not assumed. An agent MUST NOT assume MCP or OpenAPI endpoints exist without checking the integrations block in the manifest first.

5. Modes

5.1 MODE1 — Static Serve

The agent-readable web. No server required beyond static hosting.

In MODE1, the site provides a static, structured representation of its content accessible to agents. This is the entry point for AHP adoption and is compatible with existing Cloudflare llms.txt implementations.

Requirements:

  • The manifest MUST declare "modes": ["MODE1"]
  • The site MUST serve agent-readable content. This MAY be:
    • A /llms.txt file following the llms.txt convention
    • Markdown pages accessible at predictable URLs
    • A static JSON knowledge document
  • The manifest SHOULD include an endpoints.content pointing to the primary content URL

Visiting agent behavior: A visiting agent discovering a MODE1 site fetches the content document and processes it locally. The interaction is read-only and stateless.

Compatibility: Sites already exposing /llms.txt or ?markdown=true endpoints are MODE1-compatible with only the addition of /.well-known/agent.json.


5.2 MODE2 — Interactive Knowledge

The agent asks questions. The site answers from its content.

MODE2 adds a conversational endpoint. Instead of parsing a document, the visiting agent submits queries and receives precise, sourced answers. The concierge is typically backed by a retrieval system (vector search, structured data, or keyword search) over the site’s content.

Requirements:

  • All MODE1 requirements apply (backwards compatibility is mandatory)
  • The manifest MUST declare capabilities with "mode": "MODE2"
  • The site MUST expose a POST /agent/converse endpoint (or the path declared in endpoints.converse)
  • The endpoint MUST support single-turn queries
  • The endpoint SHOULD support multi-turn sessions via session_id

Key characteristic: The concierge answers from its knowledge base. It does not take actions, make external calls, or escalate to humans. It knows what the site contains and surfaces it precisely.


5.3 MODE3 — Agentic Desk

The site’s agent works on your behalf.

MODE3 elevates the concierge from a knowledge retrieval system to an active agent. The site-side concierge may have access to tools, MCP servers, external APIs, and human operators that the visiting agent does not. A visiting agent can delegate tasks to the concierge that require capabilities it lacks.

Requirements:

  • All MODE2 requirements apply
  • The manifest MUST declare MODE3 capabilities with explicit input_schema and output_schema
  • Each MODE3 capability MUST declare its action_type: "query", "action", or "async"
  • Capabilities of type "action" or "async" MUST require authentication (authentication MUST NOT be "none")
  • The site MUST implement the async model (Section 9) for action_type: "async" capabilities

Example MODE3 capabilities:

  • Book an appointment (accesses a calendar)
  • Check order status (accesses a CRM)
  • Get a custom quote (runs a calculation or contacts a human)
  • Perform deep research (has access to MCPs the visitor does not)
  • Escalate to a human (routes to a real person and delivers answer via callback)

Key characteristic: MODE3 capabilities have action surface. This requires a trust model (Section 8) and careful capability scoping. Visiting agents MUST declare their intent when invoking MODE3 capabilities.


6. Conversational Endpoint

6.1 Request Format

POST /agent/converse
Content-Type: application/json
{
  "ahp": "0.1",
  "capability": "content_search",
  "query": "What has the site owner written about AI agents?",
  "session_id": null,
  "context": {
    "requesting_agent": "my-research-bot/1.0",
    "user_intent": "research",
    "max_tokens": 500,
    "accept_types": ["text/answer", "application/feed", "media/video"]
  }
}
Field Required Description
ahp SHOULD Protocol version for compatibility checking
capability MUST The capability being invoked
query MUST The request or question
session_id MAY Session identifier for multi-turn exchanges
context MAY Additional context about the requesting agent
context.accept_types MAY List of content types the visiting agent can handle, in preference order. If absent, the concierge SHOULD default to text/answer. See Section 6.6 and Appendix C.

6.2 Response — Success

{
  "status": "success",
  "session_id": null,
  "response": {
    "answer": "The site owner has written three pieces on AI agents...",
    "sources": [
      {
        "title": "Why Agents Need Better Web Protocols",
        "url": "/blog/agent-protocols",
        "relevance": "direct"
      }
    ],
    "follow_up": {
      "suggested_queries": [
        "What is the site owner's professional background?",
        "Are there open source projects related to this topic?"
      ]
    }
  },
  "meta": {
    "tokens_used": 187,
    "capability_used": "content_search",
    "mode": "MODE2",
    "cached": false,
    "content_signals": {
      "ai_train": false,
      "ai_input": true
    }
  }
}

6.3 Response — Clarification Needed

When the query is ambiguous, the concierge SHOULD request clarification rather than guess.

{
  "status": "clarification_needed",
  "session_id": "abc-123",
  "clarification": {
    "question": "Are you looking for blog posts, or also open source projects?",
    "options": ["blog_posts", "projects", "everything"],
    "free_form": true
  }
}

options MAY be null for entirely open-ended clarification. free_form: true signals that the visiting agent may respond with arbitrary text rather than selecting an option.

To continue, the visiting agent resubmits with the same session_id and a clarification field:

{
  "capability": "content_search",
  "query": "What has the site owner written about AI agents?",
  "session_id": "abc-123",
  "clarification": "blog_posts"
}

6.4 Response — Async Accepted (MODE3)

{
  "status": "accepted",
  "session_id": "xyz-456",
  "eta_seconds": 120,
  "callback": {
    "method": "POST",
    "url": "https://[provided by visiting agent]"
  },
  "poll": "/agent/converse/status/xyz-456"
}

6.5 Session Constraints

Implementations SHOULD enforce:

  • Maximum 10 turns per session — bounds the LLM cost of a single agent interaction; most legitimate queries resolve in 1–3 turns. Implementers serving complex multi-step workflows MAY increase this limit, but SHOULD require authentication for sessions above 10 turns.
  • Session expiry after 10 minutes of inactivity — limits in-memory session accumulation on the server; balances useful multi-turn context against resource consumption. Implementers with authenticated, long-running agent workflows MAY extend this to 30–60 minutes.
  • Request body cap of 8KB — prevents prompt injection via oversized payloads and bounds memory allocation per request. Typical well-formed AHP requests are under 1KB; 8KB is generous headroom for structured context objects while excluding pathological inputs.
  • Rate limiting per IP and optionally per requesting_agent — see Section 11 for required headers and recommended defaults (30 req/min unauthenticated, 120 req/min authenticated).

These defaults are conservative starting points, not mandated constants. Implementers SHOULD adjust them based on their cost model, authentication posture, and expected agent behaviour.

6.6 Response Content Types

AHP responses are not limited to text. A concierge MAY return any media type — video streams, audio, images, files, structured feeds, or custom payloads — provided the type is declared in the capability’s response_types and the visiting agent has indicated it can handle it via context.accept_types.

Content type negotiation:

  1. The manifest declares what types each capability can return (response_types)
  2. The visiting agent declares what types it can handle (context.accept_types) in preference order
  3. The concierge selects the most preferred type both parties support
  4. If no overlap exists and accept_fallback: true is set on the capability, the concierge MUST fall back to text/answer with a plain-language description of the content
  5. If no overlap exists and accept_fallback is false or absent, the concierge MUST return a 400 error with code unsupported_type, listing the available types

Response structure with content type:

The standard success response is extended with a content_type field and a payload object alongside (or instead of) answer:

{
  "status": "success",
  "response": {
    "content_type": "media/video",
    "payload": {
      "stream_url": "https://cdn.example.com/video/intro-to-ahp.m3u8",
      "format": "hls",
      "duration_seconds": 847,
      "width": 1920,
      "height": 1080,
      "thumbnail_url": "https://cdn.example.com/thumb/intro-to-ahp.jpg",
      "subtitles": [
        { "lang": "en", "url": "https://cdn.example.com/subs/intro-to-ahp.en.vtt" }
      ],
      "title": "Introduction to AHP",
      "description": "A walkthrough of the Agent Handshake Protocol specification."
    },
    "answer": "Here is the requested video. It covers AHP modes 1 through 3 in 14 minutes.",
    "sources": [
      { "title": "Introduction to AHP", "url": "/videos/intro-to-ahp", "relevance": "direct" }
    ]
  },
  "meta": {
    "content_type": "media/video",
    "capability_used": "get_video",
    "mode": "MODE2"
  }
}

Rules:

  • Media content MUST be URL-referenced. Binary content MUST NOT be embedded in the response body.
  • answer SHOULD always be present as a human-readable (and LLM-readable) summary, even when a rich payload is returned. This ensures visiting agents that cannot render the media can still extract useful information.
  • payload structure is defined per content type. See Appendix C for the standard type registry and payload schemas.
  • content_type in the meta object SHOULD echo the type used, to aid logging and downstream processing.

Fallback example — visiting agent cannot handle media/video, capability has accept_fallback: true:

{
  "status": "success",
  "response": {
    "content_type": "text/answer",
    "answer": "The video 'Introduction to AHP' (14 min) is available at https://example.com/videos/intro-to-ahp. It covers MODE1 through MODE3 with live demonstrations.",
    "sources": [
      { "title": "Introduction to AHP", "url": "/videos/intro-to-ahp", "relevance": "direct" }
    ]
  },
  "meta": {
    "content_type": "text/answer",
    "fallback_from": "media/video",
    "capability_used": "get_video",
    "mode": "MODE2"
  }
}

The meta.fallback_from field informs the visiting agent that a richer response was available but not served due to type negotiation. The visiting agent MAY retry with the appropriate accept_types if it gains the ability to handle the type.


7. Content Signals

Content signals allow site owners to declare their preferences for AI usage of their content. They MUST appear in the manifest and SHOULD be echoed in responses.

Signal Type Meaning
ai_train boolean May this content be used to train AI models?
ai_input boolean May this content be used as input/context for AI inference?
search boolean May this content be indexed for AI-powered search?
attribution_required boolean Must the source be cited when content is used?

Visiting agents and downstream systems SHOULD respect ai_train: false by not including the response content in training pipelines. AHP does not technically enforce this — it is a declaration of intent and a legal/ethical signal. A future revision may define cryptographic content-signal assertions to enable stronger enforcement.

A future AHP revision may define a signed content signals extension for stronger assertions.


8. Trust & Identity

8.1 Visiting Agent Identity

Visiting agents SHOULD include a requesting_agent field in the request context. This is an unverified hint — it is not a security mechanism. Sites MAY use it for logging, routing, or capability gating.

8.2 Authentication

MODE1 and MODE2 query capabilities MAY be unauthenticated. MODE3 action capabilities MUST require authentication.

Supported schemes:

Scheme Description
none No authentication required
bearer HTTP Bearer token in Authorization header
api_key API key in X-AHP-Key header
signed_request HMAC-signed request body (details TBD in 0.2)

8.3 Future Work

A future revision will define a verifiable agent identity extension, likely building on existing work in decentralized identity (DIDs) or signed JWTs with well-known public keys. The goal is to allow site-side concierges to make trust decisions based on verified agent identity rather than self-reported claims.


9. Async Model

MODE3 capabilities that involve human escalation, long-running computation, or external API calls MUST use the async model.

9.1 Flow

  1. Visiting agent POSTs to /agent/converse with a MODE3 async capability
  2. Concierge responds with status: "accepted", a session_id, estimated ETA, and a poll URL
  3. Visiting agent either:
    • Polls GET /agent/converse/status/{session_id} until status is success or failed
    • Waits for callback at a URL it provided in the initial request
  4. On completion, the response follows the standard success format

9.2 Status Response

{
  "status": "pending",
  "session_id": "xyz-456",
  "progress": "Waiting for human operator response",
  "eta_seconds": 60
}

Status values: pending, success, failed, expired


10. Error Handling

All errors MUST return appropriate HTTP status codes and a JSON body:

{
  "status": "error",
  "code": "unknown_capability",
  "message": "The capability 'foobar' is not supported.",
  "available_capabilities": ["site_info", "content_search", "contact"]
}
HTTP Status Code Meaning
400 invalid_request Malformed request body
400 unknown_capability Capability not in manifest
400 missing_field Required field absent
401 auth_required Authentication required for this capability
403 forbidden Valid auth but insufficient permissions
413 request_too_large Body exceeds size cap
429 rate_limited Rate limit hit; includes Retry-After header
500 concierge_error Internal error in the concierge
503 unavailable Concierge temporarily unavailable

11. Rate Limiting

Rate limiting in AHP serves two purposes: protecting the site’s infrastructure and managing LLM API costs for MODE2/MODE3 concierges. Sites MUST communicate rate limit status using standard headers and SHOULD publish their limits in the manifest.

11.1 Required Headers

All AHP endpoints MUST include the following headers on every response when rate limiting is active:

Header Description
X-RateLimit-Limit Maximum requests allowed in the current window
X-RateLimit-Remaining Requests remaining in the current window
X-RateLimit-Reset Unix timestamp when the window resets
X-RateLimit-Window Window duration in seconds (e.g. 60)

On a 429 Too Many Requests response, the site MUST also include:

Header Description
Retry-After Seconds until the visiting agent may retry

Example headers:

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 12
X-RateLimit-Reset: 1708559400
X-RateLimit-Window: 60

These are recommended defaults. Sites MAY apply stricter or more permissive limits based on their infrastructure and use case.

Mode Unauthenticated Authenticated
MODE1 (static content) 120/minute N/A
MODE2 (conversational) 30/minute 120/minute
MODE3 (agentic, query) 30/minute 120/minute
MODE3 (agentic, action) N/A 30/minute

MODE3 action capabilities SHOULD have conservative limits regardless of authentication status. Actions have side effects; runaway agents can cause real-world consequences.

11.3 Limit Scope

Rate limits SHOULD be applied per IP address as the baseline. Sites MAY additionally apply limits per requesting_agent identifier (from the request context), per API key, or per session.

When requesting_agent scoping is in use and a visiting agent hits its per-agent limit before the per-IP limit, the 429 response SHOULD indicate this:

{
  "status": "error",
  "code": "rate_limited",
  "message": "Rate limit exceeded for this agent identity.",
  "scope": "agent",
  "retry_after": 47
}

11.4 Cost-Based Throttling (MODE2/MODE3)

Sites operating MODE2 or MODE3 concierges backed by LLM APIs incur per-token costs. Sites MAY implement cost-based throttling in addition to request-count limits:

  • Token budget per session: Limit total tokens consumed across a session’s turns (recommended: 10,000 tokens/session)
  • Daily token quota per IP: Cap total LLM token spend per IP per day
  • Max tokens per response: Honor the context.max_tokens hint from visiting agents, and enforce a hard ceiling regardless

When a token budget is exhausted, respond with 429 and include:

{
  "status": "error",
  "code": "rate_limited",
  "message": "Token budget for this session has been exhausted.",
  "scope": "session_tokens",
  "retry_after": null
}

retry_after: null signals that retrying in the same session will not help; the visiting agent should start a new session.

11.5 Manifest Declaration

Sites MUST declare their rate limits in the manifest for transparency:

{
  "rate_limits": {
    "unauthenticated": {
      "requests": "30/minute",
      "token_budget": "5000/session"
    },
    "authenticated": {
      "requests": "120/minute",
      "token_budget": "20000/session"
    }
  }
}

11.6 Backoff Guidance for Visiting Agents

Visiting agents MUST respect Retry-After headers and MUST NOT retry before the indicated time. Visiting agents SHOULD implement exponential backoff with jitter when retrying after a 429. Visiting agents SHOULD NOT treat 429 as a fatal error — it is a temporary condition.


12. Versioning & Backwards Compatibility

The ahp field in both the manifest and requests carries the protocol version.

  • A site implementing AHP 0.x MUST remain compatible with MODE1 visiting agents regardless of which modes it supports.
  • A visiting agent encountering an unknown ahp version SHOULD fall back to MODE1 behavior.
  • Minor version increments (0.1 → 0.2) MUST be backwards compatible.
  • Major version increments (0.x → 1.0) MAY introduce breaking changes but MUST provide a migration path.

13. Security Considerations

  • Prompt injection: MODE2 and MODE3 concierges that pass visiting agent queries to an LLM MUST sanitize inputs and implement guardrails against prompt injection attacks.
  • Data exfiltration: MODE3 action capabilities SHOULD restrict what data can be returned to visiting agents, particularly for authenticated endpoints.
  • Rate limiting: All AHP endpoints SHOULD implement rate limiting. Recommended defaults: 30 requests/minute for unauthenticated, 120/minute for authenticated.
  • SSRF: Concierges that accept callback URLs from visiting agents (async model) MUST validate URLs to prevent SSRF attacks.
  • Content signal enforcement: AHP does not technically enforce content signals. Sites SHOULD include signals in responses; visiting agents SHOULD honor them.

14. Examples

14.1 Minimal MODE1 Implementation

/.well-known/agent.json  → AHP manifest with modes: ["MODE1"]
/llms.txt                → Site content in plain text/markdown

A visiting agent fetches the manifest, finds the content URL, retrieves /llms.txt, and processes it locally. Zero server-side logic required.

14.2 MODE2 Query Flow

Visiting Agent                          Site Concierge
     │                                       │
     │  GET /.well-known/agent.json          │
     │──────────────────────────────────────►│
     │  ◄── manifest (modes: MODE1, MODE2) ──│
     │                                       │
     │  POST /agent/converse                 │
     │  { capability: "content_search",      │
     │    query: "posts about AI agents" }   │
     │──────────────────────────────────────►│
     │  ◄── { status: "success",             │
     │        response: { answer, sources }} │

14.3 MODE3 Human Escalation Flow

Visiting Agent                          Site Concierge + Human
     │                                       │
     │  POST /agent/converse                 │
     │  { capability: "get_custom_quote",    │
     │    query: "...", auth: "Bearer ..." } │
     │──────────────────────────────────────►│
     │  ◄── { status: "accepted",            │
     │        session_id, poll: "/..." }     │
     │                                       │  [human notified]
     │  GET /agent/converse/status/xyz       │  [human responds]
     │──────────────────────────────────────►│
     │  ◄── { status: "success",             │
     │        response: { answer } }         │

Appendix A: JSON Schemas

Machine-readable JSON Schema files for all AHP data structures are published alongside this specification. Implementations SHOULD validate against these schemas.

Schema URL Validates
Manifest /schema/0.1/manifest.json /.well-known/agent.json
Request /schema/0.1/request.json POST /agent/converse body
Response /schema/0.1/response.json POST /agent/converse response

All schemas use JSON Schema draft-07 and are versioned alongside the specification. The schema $id URIs are stable and will not change for a given version.

Validating a manifest:

# Using ajv-cli
npx ajv validate -s https://agenthandshake.dev/schema/0.1/manifest.json \
  -d .well-known/agent.json

# Using Python jsonschema
pip install jsonschema requests
python3 -c "
import jsonschema, json, requests
schema = requests.get('https://agenthandshake.dev/schema/0.1/manifest.json').json()
manifest = json.load(open('.well-known/agent.json'))
jsonschema.validate(manifest, schema)
print('Valid')
"

Appendix B: Relationship to llms.txt

The llms.txt convention (proposed by Answer.AI) defines a standard location for a site-level plain text or markdown document intended for LLM consumption. It is a useful first step — better than raw HTML — but it is fundamentally a document dump: a static, unstructured file that an LLM must parse entirely, regardless of what it actually needs.

AHP is the evolution beyond llms.txt. The differences are significant:

  llms.txt AHP
Interaction model Read-only document Conversational exchange
Agent gets what it needs Must parse everything Asks for what it needs
Capabilities declared No Yes
Multi-turn support No Yes
Human escalation No Yes (MODE3)
Content signals No Yes
Rate limiting No Yes
Agent discovery (in-page) No Yes
Backwards compatible with llms.txt Yes

AHP does not deprecate llms.txt. A site with an existing llms.txt can become AHP MODE1 compliant with zero changes to that file:

  1. Add /.well-known/agent.json with modes: ["MODE1"] and endpoints.content: "/llms.txt"
  2. Add the HTTP Link response header to all responses: Link: </.well-known/agent.json>; rel="ahp-manifest"; type="application/agent+json" (Section 3.2)
  3. Add the in-page agent notice to HTML pages (Section 3.3)

The llms.txt file becomes the content source for MODE1. When the site is ready to upgrade to MODE2, the same content can back the conversational endpoint.

The goal is not to fragment the ecosystem — it is to give it a path forward. Sites that adopt AHP are not abandoning llms.txt compatibility; they are making it meaningful.


Appendix C: Extension Mechanism & Content Type Registry

C.1 Extension Philosophy

AHP cannot enumerate every content type or capability pattern the web will produce. Instead, the protocol defines:

  1. A core registry of well-known content types with standardised payload schemas
  2. A namespaced extension prefix (x-) for custom types that any implementation may use without coordination
  3. A promotion path for widely-adopted extensions to become core types in future revisions

This mirrors the design of HTTP headers, MIME types, and HTML data attributes — all of which use the same pattern successfully.

C.2 Core Content Type Registry

The following types are defined by this specification. Payload schemas are normative.


text/answer

Default type. A plain-text (or markdown) response. Used when no richer type applies or as a fallback.

{
  "content_type": "text/answer",
  "answer": "string — the response text. Markdown is permitted.",
  "sources": [ { "title": "string", "url": "string", "relevance": "direct|indirect|background" } ],
  "follow_up": { "suggested_queries": ["string"] }
}

application/data

Structured JSON data. Used when the response is machine-readable records rather than prose — contact info, product details, structured metadata.

{
  "content_type": "application/data",
  "payload": {
    "schema": "URI or name identifying the data shape (optional but RECOMMENDED)",
    "data": { }
  },
  "answer": "string — human-readable summary (SHOULD be present)"
}

application/feed

A list of items. Used for search results, article listings, product catalogues, or any ordered collection.

{
  "content_type": "application/feed",
  "payload": {
    "total": 42,
    "items": [
      {
        "title": "string",
        "url": "string",
        "description": "string",
        "published_at": "ISO 8601 datetime or null",
        "thumbnail_url": "string or null",
        "metadata": { }
      }
    ],
    "next_cursor": "string or null — for pagination"
  },
  "answer": "string — summary of results"
}

media/video

A video resource. Payload provides stream URL, format, and metadata sufficient for a capable agent to present, embed, or describe the content.

{
  "content_type": "media/video",
  "payload": {
    "stream_url": "string — primary playback URL (HLS, DASH, MP4, etc.)",
    "format": "hls | dash | mp4 | webm | string",
    "duration_seconds": 0,
    "width": 1920,
    "height": 1080,
    "thumbnail_url": "string or null",
    "title": "string",
    "description": "string or null",
    "published_at": "ISO 8601 datetime or null",
    "subtitles": [
      { "lang": "BCP 47 language tag", "url": "string — VTT or SRT" }
    ],
    "chapters": [
      { "title": "string", "start_seconds": 0 }
    ],
    "alternate_formats": [
      { "format": "string", "url": "string", "quality": "string" }
    ]
  },
  "answer": "string — description of the video"
}

media/audio

An audio resource. Covers podcasts, music, voice recordings, and audio streams.

{
  "content_type": "media/audio",
  "payload": {
    "stream_url": "string",
    "format": "mp3 | ogg | aac | flac | string",
    "duration_seconds": 0,
    "title": "string",
    "description": "string or null",
    "published_at": "ISO 8601 datetime or null",
    "thumbnail_url": "string or null",
    "transcript_url": "string or null — VTT, SRT, or plain text",
    "chapters": [
      { "title": "string", "start_seconds": 0 }
    ]
  },
  "answer": "string — description of the audio"
}

media/image

One or more images. Used for photos, illustrations, diagrams, or galleries.

{
  "content_type": "media/image",
  "payload": {
    "images": [
      {
        "url": "string",
        "alt": "string — descriptive alt text. REQUIRED.",
        "width": 0,
        "height": 0,
        "format": "jpeg | png | webp | gif | svg | string",
        "title": "string or null"
      }
    ],
    "layout": "single | gallery | carousel"
  },
  "answer": "string — description of the image(s)"
}

file/download

A downloadable file. Used for PDFs, datasets, archives, documents, or any non-media binary.

{
  "content_type": "file/download",
  "payload": {
    "url": "string — download URL",
    "filename": "string",
    "mime_type": "string — IANA MIME type",
    "size_bytes": 0,
    "checksum": "string — SHA-256 hex digest (RECOMMENDED)",
    "description": "string or null",
    "expires_at": "ISO 8601 datetime or null — if the URL is time-limited"
  },
  "answer": "string — description of the file"
}

application/action-result

The outcome of a MODE3 action capability. Used when an action was performed and a result is being returned.

{
  "content_type": "application/action-result",
  "payload": {
    "action": "string — name of the action performed",
    "success": true,
    "result": { },
    "side_effects": [
      { "type": "string", "description": "string" }
    ]
  },
  "answer": "string — plain-language summary of what happened"
}

C.3 Extension Types

Any content type prefixed with x- is an extension type. Extension types are not defined by this specification and carry no compatibility guarantees.

Format: x-{vendor}/{type}

Examples:

  • x-shopify/cart — Shopify cart state
  • x-realestate/listing — Property listing with geo, pricing, photos
  • x-health/appointment — Healthcare appointment booking result

Rules for extension types:

  • MUST be prefixed with x-
  • SHOULD include a vendor namespace to avoid collisions
  • The payload schema is defined entirely by the implementer
  • answer SHOULD still be present for agents that cannot interpret the extension type
  • Extension types SHOULD be documented publicly if intended for third-party use

Promotion to core: Extension types that see broad adoption across multiple independent implementations MAY be proposed for inclusion in the core registry via the standard contribution process. Proposals should include real-world usage evidence and at least two independent implementations.

C.4 Registering a Content Type

To formally propose a new core content type:

  1. Open an issue in the spec repository with the label content-type-proposal
  2. Provide: the type string, a use case description, a normative payload schema, at least one worked example, and evidence of need (why existing types don’t cover it)
  3. Discussion and refinement happens in the issue
  4. If accepted, a PR adds it to Appendix C and the type is included in the next minor revision

Until a proposal is accepted, use the x- prefix.


Appendix D: MCP Compatibility

This appendix defines the normative mapping from AHP capabilities to MCP (Model Context Protocol) tool and resource definitions, and the required behaviour for AHP servers exposing an MCP integration endpoint.

D.1 Transport

AHP MCP endpoints MUST support the HTTP transport (JSON-RPC 2.0 over HTTP POST). Servers MAY additionally support Server-Sent Events (SSE) for streaming responses.

Endpoint: declared in integrations.mcp.url (e.g. POST /mcp) Content-Type: application/json Protocol version: 2024-11-05 (MCP 1.0)

D.2 Required Methods

JSON-RPC method AHP behaviour
initialize Returns server info, protocol version, and capability declaration
tools/list Returns all MODE3 action_required: true capabilities as MCP tool definitions
tools/call Invokes the named AHP capability via POST /agent/converse; proxies auth
resources/list Returns MODE1 content endpoints and MODE2 knowledge capabilities as MCP resources
resources/read Fetches a resource URI and returns its content

MODE2 non-action capabilities MAY be exposed as both MCP tools (callable with a query string) and MCP resources (fetchable at a URI). Implementers SHOULD prefer exposing them as tools for maximum compatibility.

D.3 Capability → Tool Mapping

Each AHP MODE3 capability maps to one MCP tool definition:

AHP capability:

{
  "name": "inventory_check",
  "description": "Check stock levels and availability for a product",
  "mode": "MODE3",
  "auth_required": true,
  "response_types": ["application/data", "text/answer"]
}

MCP tool definition:

{
  "name": "inventory_check",
  "description": "Check stock levels and availability for a product",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Natural language query about product availability"
      },
      "session_id": {
        "type": "string",
        "description": "Optional: continue an existing conversation session"
      }
    },
    "required": ["query"]
  }
}

Rules:

  • name MUST match the AHP capability name exactly
  • description SHOULD be taken from the AHP capability description
  • All tools MUST accept query (string, required) and session_id (string, optional)
  • Tool-specific parameters MAY be added for capabilities with structured inputs

D.4 tools/call → AHP Request Mapping

When a MCP client calls a tool, the server MUST proxy it to POST /agent/converse:

// MCP tools/call request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "inventory_check",
    "arguments": {
      "query": "Is AHP-IMPL-001 in stock?",
      "session_id": "abc-123"
    }
  }
}

Maps to:

// AHP /agent/converse request
{
  "capability": "inventory_check",
  "query": "Is AHP-IMPL-001 in stock?",
  "session_id": "abc-123",
  "auth": "<bearer token from MCP _meta or Authorization header>"
}

The MCP response MUST contain the AHP response.answer as content[0].text, and MAY include additional content blocks for structured data.

D.5 Authentication Passthrough

MCP does not define a native auth mechanism. AHP MCP endpoints MUST accept bearer tokens via:

  1. The HTTP Authorization: Bearer <token> header on the MCP request, OR
  2. A _meta.auth field in the params object of any tools/call request

The server MUST apply the same auth requirements to MCP tool calls as to direct AHP requests.

D.6 initialize Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {},
      "resources": {}
    },
    "serverInfo": {
      "name": "<manifest.name>",
      "version": "<ahp version>",
      "ahp": "0.1",
      "manifest": "/.well-known/agent.json"
    }
  }
}

The serverInfo.manifest field lets MCP-native clients discover the full AHP manifest for richer interaction.


Appendix E: OpenAPI / GPT Actions Compatibility

This appendix defines the normative mapping from AHP capabilities to OpenAPI 3.1.x path items, and the required behaviour for AHP servers exposing an OpenAPI integration endpoint.

E.1 Document Structure

The OpenAPI document served at integrations.openapi.url MUST conform to OpenAPI 3.1.0 and MUST include:

  • info.title — from manifest.name
  • info.description — from manifest.description
  • info.version — AHP protocol version
  • servers[0].url — the site’s base URL
  • One path per AHP capability (see §E.2)
  • A components.securitySchemes block if authentication is required

E.2 Capability → Path Mapping

Each AHP capability maps to a POST operation at /capabilities/{capability_name}:

AHP capability:

{
  "name": "get_quote",
  "description": "Calculate a price quote for one or more products",
  "mode": "MODE3",
  "auth_required": true
}

OpenAPI path item:

/capabilities/get_quote:
  post:
    operationId: get_quote
    summary: Calculate a price quote for one or more products
    security:
      - bearerAuth: []
    requestBody:
      required: true
      content:
        application/json:
          schema:
            type: object
            required: [query]
            properties:
              query:
                type: string
                description: Natural language description of the quote request
              session_id:
                type: string
                description: Optional session ID to continue an existing conversation
    responses:
      '200':
        description: Successful response
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AHPResponse'
      '400':
        description: Bad request
      '401':
        description: Authentication required
      '429':
        description: Rate limit exceeded

The server MUST proxy requests to this path to POST /agent/converse with capability set to the path’s capability name.

E.3 AHPResponse Schema

All capability paths share a common response schema:

components:
  schemas:
    AHPResponse:
      type: object
      properties:
        status:
          type: string
          enum: [success, clarification_needed, accepted, error]
        session_id:
          type: string
        response:
          type: object
          properties:
            content_type:
              type: string
            answer:
              type: string
            sources:
              type: array
              items:
                type: object
        meta:
          type: object
          properties:
            mode:
              type: string
            cached:
              type: boolean
            tokens_used:
              type: integer
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

E.4 GPT Actions Integration

To add an AHP site as a ChatGPT GPT Action:

  1. Create a Custom GPT at chat.openai.com
  2. Under “Actions”, add a new action
  3. Enter the site’s OpenAPI URL (e.g. https://example.com/openapi.json)
  4. ChatGPT imports all AHP capabilities as callable functions
  5. If the site requires authentication, add the bearer token under “Authentication”

The AHP site’s concierge handles all natural language processing; the GPT receives structured AHP responses and can present them to the user.

E.5 Capability Filtering

The OpenAPI document SHOULD only expose capabilities appropriate for the integration context:

  • MODE1 capabilities (static content) SHOULD be omitted or exposed as GET operations on their content URLs, not as POST capabilities
  • MODE3 capabilities with auth_required: true MUST include the appropriate security scheme
  • Async capabilities (returning status: accepted) MUST document the polling pattern in their operation description

Agent Handshake Protocol — Draft 0.1 Authors: Nick Allain, [contributors] Repository: https://github.com/AHP-Organization/agent-handshake-protocol Spec site: https://agenthandshake.dev