{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://autoagentprotocol.org/v3.1/schemas/agent-card.schema.json",
  "title": "AgentCard",
  "description": "A2A v1.0 AgentCard carrying the AAP automotive-retail extension. Published at /.well-known/agent-card.json on a dealer-controlled domain. AAP requires a JSONRPC interface in 'supportedInterfaces[]'. Additional bindings are outside AAP's conformance requirements, but all bindings on the same card MUST expose equivalent functionality, behavior, errors and authentication under A2A section 5.1. The card MUST declare exactly one AAP extension, whose 'uri' equals 'https://autoagentprotocol.org/extensions/aap/v3.1' and whose 'required' flag is true.",
  "type": "object",
  "properties": {
    "name": { "type": "string", "description": "Human-readable agent name." },
    "description": { "type": "string", "description": "Short description of what this agent does." },
    "supportedInterfaces": {
      "type": "array",
      "minItems": 1,
      "description": "The A2A v1.0 service interfaces (transport + endpoint). A JSONRPC interface is REQUIRED. AAP defines no A2A HTTP+JSON or gRPC binding; its native Poppy OpenAPI API is advertised separately in poppy.json. A dealer may advertise additional bindings only when they expose equivalent functionality under A2A section 5.1; unrelated services belong on separate cards. This array is preference-ordered, so the JSONRPC entry SHOULD be first for AAP clients.",
      "items": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^(?:[hH][tT][tT][pP][sS]://(?:[^\\s/?#@]*@)?(?:\\[[^\\s/?#\\]]+\\]|[^\\s/?#@:\\[\\]]+)(?::\\d*)?|[hH][tT][tT][pP]://(?:[lL][oO][cC][aA][lL][hH][oO][sS][tT]|127\\.0\\.0\\.1|\\[::1\\])(?::\\d+)?)(?:[/?#]|$)",
            "description": "A2A AgentInterface.url requires a valid absolute HTTPS URL in production, including for GRPC. Loopback HTTP is permitted only for local development and MUST NOT be published. A bare gRPC hostname:port is a channel target, not an AgentInterface URL."
          },
          "protocolBinding": {
            "type": "string",
            "minLength": 1,
            "description": "The A2A transport binding available at this URL. A2A defines this as an open string so bindings can be added; the core values are 'JSONRPC', 'GRPC' and 'HTTP+JSON', and a custom binding uses a URI. Other bindings on this card MUST serve equivalent functionality under A2A section 5.1. AAP clients use 'JSONRPC'."
          },
          "protocolVersion": {
            "type": "string",
            "description": "The A2A protocol version this interface exposes (Major.Minor), e.g. '1.0'."
          },
          "tenant": {
            "type": "string",
            "description": "Optional opaque routing value when several agents are served behind one endpoint. When present, a client MUST echo it in the 'tenant' field of every request to this interface."
          }
        },
        "required": ["url", "protocolBinding", "protocolVersion"],
        "additionalProperties": true
      },
      "contains": {
        "type": "object",
        "properties": { "protocolBinding": { "const": "JSONRPC" } },
        "required": ["protocolBinding"]
      }
    },
    "provider": {
      "type": "object",
      "description": "Organization operating the agent.",
      "properties": {
        "organization": { "type": "string" },
        "url": { "type": "string", "format": "uri" }
      },
      "required": ["organization", "url"],
      "additionalProperties": true
    },
    "version": { "type": "string", "description": "Agent document version string (semver recommended)." },
    "documentationUrl": {
      "type": "string",
      "format": "uri",
      "description": "URL to human-readable documentation describing this agent's behavior."
    },
    "capabilities": {
      "type": "object",
      "description": "A2A capability flags and extensions.",
      "properties": {
        "streaming": { "type": "boolean" },
        "pushNotifications": { "type": "boolean" },
        "extensions": {
          "type": "array",
          "description": "Declared A2A extensions. MUST include exactly one AAP profile entry, marked required: true. Other, unrelated extensions may also be declared.",
          "items": { "$ref": "#/$defs/extension" },
          "contains": {
            "type": "object",
            "properties": {
              "uri": { "const": "https://autoagentprotocol.org/extensions/aap/v3.1" },
              "required": { "const": true },
              "params": {
                "$ref": "./aap-extension-params.schema.json"
              }
            },
            "required": ["uri", "required", "params"]
          },
          "allOf": [{
            "contains": {
              "type": "object",
              "properties": {
                "uri": {
                  "anyOf": [
                    { "const": "https://autoagentprotocol.org/extensions/aap/v3.1" },
                    { "pattern": "^https://autoagentprotocol\\.org/extensions/aap/" }
                  ]
                }
              },
              "required": ["uri"]
            },
            "minContains": 1,
            "maxContains": 1
          }]
        }
      },
      "required": ["extensions"],
      "additionalProperties": true
    },
    "defaultInputModes": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string" },
      "description": "Default media types accepted by this agent across all skills."
    },
    "defaultOutputModes": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string" },
      "description": "Default media types produced by this agent across all skills."
    },
    "skills": {
      "type": "array",
      "minItems": 1,
      "description": "Skills the agent exposes. AAP defines 5 standard skill IDs (`dealer.information`, `inventory.facets`, `inventory.search`, `inventory.vehicle`, `lead.submit`); a compliant agent declares the subset it implements. Buyer agents MUST inspect `skills[]` to discover what is supported.",
      "items": { "$ref": "#/$defs/skill" }
    },
    "securitySchemes": {
      "type": "object",
      "description": "Map of scheme name to an A2A v1.0 SecurityScheme (a oneof over apiKeySecurityScheme, httpAuthSecurityScheme, oauth2SecurityScheme, openIdConnectSecurityScheme, mtlsSecurityScheme). AAP agents are public by default (no scheme).",
      "additionalProperties": true
    },
    "securityRequirements": {
      "type": "array",
      "description": "A2A v1.0 SecurityRequirement alternatives. The v0.3 name for this field was 'security'; A2A v1.0 parsers read only 'securityRequirements', so a card using the old name is treated as allowing anonymous access. Empty or absent means anonymous access is allowed.",
      "items": {
        "type": "object",
        "description": "An empty requirement permits anonymous access. ProtoJSON may omit the empty schemes map entirely. Non-empty requirements use the schemes wrapper, not the v0.3 map shape.",
        "properties": {
          "schemes": {
            "type": "object",
            "description": "Map of security-scheme name to the scopes required from it.",
            "additionalProperties": {
              "type": "object",
              "properties": { "list": { "type": "array", "items": { "type": "string" } } },
              "additionalProperties": true
            }
          }
        },
        "anyOf": [{ "required": ["schemes"] }, { "maxProperties": 0 }],
        "additionalProperties": true
      }
    },
    "signatures": {
      "type": "array",
      "description": "Optional A2A AgentCardSignature entries (JWS over the card). AAP does not require signing; a buyer agent that finds signatures SHOULD verify them before trusting the card.",
      "items": {
        "type": "object",
        "properties": {
          "protected": {
            "type": "string",
            "description": "Required base64url-encoded protected JWS header JSON object."
          },
          "signature": {
            "type": "string",
            "description": "Required base64url-encoded JWS signature."
          },
          "header": {
            "type": "object",
            "description": "Optional unprotected JWS header values."
          }
        },
        "required": ["protected", "signature"],
        "additionalProperties": true
      }
    },
    "iconUrl": { "type": "string", "format": "uri" }
  },
  "required": [
    "name",
    "description",
    "supportedInterfaces",
    "version",
    "capabilities",
    "defaultInputModes",
    "defaultOutputModes",
    "skills"
  ],
  "additionalProperties": true,
  "$defs": {
    "extension": {
      "type": "object",
      "properties": {
        "uri": { "type": "string", "format": "uri" },
        "description": { "type": "string" },
        "required": { "type": "boolean" },
        "params": { "type": "object" }
      },
      "required": ["uri"],
      "additionalProperties": true
    },
    "skill": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Skill identifier. AAP standard skill IDs: `dealer.information`, `inventory.facets`, `inventory.search`, `inventory.vehicle`, `lead.submit`. Agents MAY declare any subset, plus additional non-AAP skill IDs."
        },
        "name": { "type": "string" },
        "description": { "type": "string" },
        "tags": {
          "type": "array",
          "items": { "type": "string" },
          "minItems": 1,
          "description": "Keywords describing the skill. REQUIRED by A2A so clients/LLMs can categorize and rank skills."
        },
        "inputModes": { "type": "array", "items": { "type": "string" } },
        "outputModes": { "type": "array", "items": { "type": "string" } }
      },
      "required": ["id", "name", "description", "tags"],
      "additionalProperties": true,
      "description": "An A2A AgentSkill. AAP cards do NOT carry per-skill natural-language `examples` — AAP is JSON-only, with no NL prompt examples. AAP publishes each skill's request/response JSON Schema URLs in the AAP extension's `params.skills` map as `params.skills[\"<id>\"].request_schema` / `response_schema` (see capabilities.extensions[].params) — NOT as fields on the skill — because that is where A2A designates extension-specific configuration."
    }
  }
}
