> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Catalog Auditor

> Validates that the site publishes a spec-compliant ARD ai-catalog.json for agentic resource discovery

<Info>
  **AI Readiness Auditor** — validates that the site publishes a spec-compliant ARD `ai-catalog.json` so agents can discover MCP servers, A2A agents, and other capabilities.
</Info>

<CardGroup cols={2}>
  <Card title="AI Readiness" icon="bot">
    Agents and registries can find the site's capabilities from a well-known catalog instead of hard-coded installs.
  </Card>

  <Card title="Interoperability" icon="globe">
    Entries follow the AI Catalog envelope: domain-anchored URNs, media types, and strict url-or-data content.
  </Card>
</CardGroup>

The [Agentic Resource Discovery](https://agenticresourcediscovery.org/spec/) specification defines how publishers advertise agentic resources in a static `ai-catalog.json` manifest. Registries crawl that file from a well-known URL (or from `robots.txt` / HTML hints) and index `identifier`, `type`, `capabilities`, and `representativeQueries` for search. This auditor discovers the catalog for the audited origin and checks publishing headers plus the [AI Catalog JSON Schema](https://raw.githubusercontent.com/ards-project/ard-spec/refs/heads/main/spec/schemas/ai-catalog.schema.json) MUST/SHOULD rules.

## How it works

The auditor resolves the site origin from the audited URL and locates `ai-catalog.json`:

1. **Well-known URI** — `GET {origin}/.well-known/ai-catalog.json`.
2. **Agentmap fallback** — if the well-known path is missing or returns non-JSON (for example an HTML catch-all), parse `/robots.txt` for an `Agentmap:` directive.
3. **HTML fallback** — otherwise look for `<link rel="ai-catalog" href="...">` on the audited page.
4. **Header checks** — the catalog response should be JSON (`Content-Type: application/json` or a `+json` type) with `Access-Control-Allow-Origin` so crawlers can fetch it.
5. **Schema checks** — `specVersion` must be `1.0`; `entries` must be an array; each entry needs `identifier`, `displayName`, `type`, and exactly one of `url` or `data`; identifiers must match `urn:air:<publisher>:<namespace>:<agent-name>`. Nested `application/ai-catalog+json` `data` objects are checked one level deep with the same entry rules.
6. **Specification pin** — every run emits `37A-AiCatalogAuditor-100` (`INFORMATION`) naming ARD [v0.91](https://agenticresourcediscovery.org/spec/) (26 Aug 2026) and [ai-catalog.schema.json](https://github.com/ards-project/ard-spec/blob/4a8a6b8fdd3ac4a50dcb63213573159c1eed7856/spec/schemas/ai-catalog.schema.json) commit `4a8a6b8fdd3ac4a50dcb63213573159c1eed7856` (20 Jun 2026). That check does not change overall status.
7. **Aggregate** — any failing rule makes the audit `FAIL`; otherwise warnings (CORS, empty entries, omitted host, `representativeQueries` size, invalid `updatedAt`) make it `WARNING`; otherwise `SUCCESS`.

A missing catalog or a JSON parse error produces a `FAIL` and still includes the specification pin check.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`. Nested inline catalogs reuse the same entry rules (402–414, 301, 303–304); hosting rules (400, 401, 300) and host object rules (404, 302) apply only to the HTTP catalog response. `37A-AiCatalogAuditor-100` is emitted on every run, including missing-catalog and invalid-JSON failures. CheckCode suffixes use status ranges: `100` information, `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode                                                     | Status        | When it fires                                                                                                                                                    | Recommendation                                                                                                                                                  |
| ------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <a id="37A-AiCatalogAuditor-100" />`37A-AiCatalogAuditor-100` | `INFORMATION` | Every run. Records that this auditor implements ARD v0.91 and the pinned `ai-catalog.schema.json` commit.                                                        | This auditor implements ARD v0.91; catalog documents still use specVersion "1.0". Schema pin 4a8a6b8fdd3ac4a50dcb63213573159c1eed7856.                          |
| <a id="37A-AiCatalogAuditor-400" />`37A-AiCatalogAuditor-400` | `FAIL`        | The catalog HTTP response has a missing or blank `Content-Type` header.                                                                                          | Send `Content-Type: application/json` for `{catalogUrl}`.                                                                                                       |
| <a id="37A-AiCatalogAuditor-401" />`37A-AiCatalogAuditor-401` | `FAIL`        | `Content-Type` is present but is not `application/json` or a `+json` type.                                                                                       | Serve `{catalogUrl}` as `application/json` instead of `{contentType}`.                                                                                          |
| <a id="37A-AiCatalogAuditor-402" />`37A-AiCatalogAuditor-402` | `FAIL`        | `specVersion` is missing or is not `"1.0"`.                                                                                                                      | Set `specVersion` to `"1.0"` on `{catalogUrl}`; current value is `{observed}`.                                                                                  |
| <a id="37A-AiCatalogAuditor-403" />`37A-AiCatalogAuditor-403` | `FAIL`        | `entries` is missing or is not an array.                                                                                                                         | Add an `entries` array on `{catalogUrl}` listing the advertised agentic resources.                                                                              |
| <a id="37A-AiCatalogAuditor-404" />`37A-AiCatalogAuditor-404` | `FAIL`        | `host` is present but `host.displayName` is missing or blank.                                                                                                    | Set `host.displayName` on `{catalogUrl}` to a human-readable publisher name.                                                                                    |
| <a id="37A-AiCatalogAuditor-405" />`37A-AiCatalogAuditor-405` | `FAIL`        | A catalog entry has no `identifier`.                                                                                                                             | Set `identifier` on entry `{label}` in `{catalogUrl}` to a `urn:air:<publisher>:<namespace>:<agent-name>` URN.                                                  |
| <a id="37A-AiCatalogAuditor-406" />`37A-AiCatalogAuditor-406` | `FAIL`        | A catalog entry has no `displayName`.                                                                                                                            | Set `displayName` on entry `{label}` in `{catalogUrl}`.                                                                                                         |
| <a id="37A-AiCatalogAuditor-407" />`37A-AiCatalogAuditor-407` | `FAIL`        | A catalog entry has no `type`.                                                                                                                                   | Set `type` on entry `{label}` in `{catalogUrl}` to an IANA media type such as `application/mcp-server-card+json`.                                               |
| <a id="37A-AiCatalogAuditor-408" />`37A-AiCatalogAuditor-408` | `FAIL`        | An entry `identifier` is present but does not match `urn:air:<publisher>:<namespace>:<agent-name>`.                                                              | Change `{identifier}` in `{catalogUrl}` to a domain-anchored URN such as `urn:air:example.com:agent:assistant`.                                                 |
| <a id="37A-AiCatalogAuditor-409" />`37A-AiCatalogAuditor-409` | `FAIL`        | An entry has both `url` and `data`.                                                                                                                              | Keep only `url` or only `data` on entry `{label}` in `{catalogUrl}`; the schema forbids both.                                                                   |
| <a id="37A-AiCatalogAuditor-410" />`37A-AiCatalogAuditor-410` | `FAIL`        | An entry has neither `url` nor `data`.                                                                                                                           | Add `url` or inline `data` on entry `{label}` in `{catalogUrl}`.                                                                                                |
| <a id="37A-AiCatalogAuditor-411" />`37A-AiCatalogAuditor-411` | `FAIL`        | A `trustManifest` object is present without `identity`.                                                                                                          | Set `trustManifest.identity` on `{label}` in `{catalogUrl}` to a SPIFFE ID, DID, or HTTPS FQDN URI.                                                             |
| <a id="37A-AiCatalogAuditor-412" />`37A-AiCatalogAuditor-412` | `FAIL`        | An attestation object has no `type`.                                                                                                                             | Set `attestation.type` on `{label}` in `{catalogUrl}` (e.g. `SOC2-Type2`).                                                                                      |
| <a id="37A-AiCatalogAuditor-413" />`37A-AiCatalogAuditor-413` | `FAIL`        | An attestation object has no `uri`.                                                                                                                              | Set `attestation.uri` on `{label}` in `{catalogUrl}` to the attestation document URL.                                                                           |
| <a id="37A-AiCatalogAuditor-414" />`37A-AiCatalogAuditor-414` | `FAIL`        | An attestation object has no `mediaType`.                                                                                                                        | Set `attestation.mediaType` on `{label}` in `{catalogUrl}` (e.g. `application/pdf`).                                                                            |
| <a id="37A-AiCatalogAuditor-415" />`37A-AiCatalogAuditor-415` | `FAIL`        | No catalog was found at `/.well-known/ai-catalog.json`, `robots.txt` `Agentmap:`, or `<link rel="ai-catalog">`.                                                  | Publish `ai-catalog.json` at `/.well-known/ai-catalog.json` for `{url}`, or advertise it with an `Agentmap:` robots.txt directive or `<link rel="ai-catalog">`. |
| <a id="37A-AiCatalogAuditor-416" />`37A-AiCatalogAuditor-416` | `FAIL`        | A catalog was found but the body is not valid JSON.                                                                                                              | Fix the JSON served for `{url}`; parser reported `{detail}`.                                                                                                    |
| <a id="37A-AiCatalogAuditor-300" />`37A-AiCatalogAuditor-300` | `WARNING`     | The catalog response has no `Access-Control-Allow-Origin` header.                                                                                                | Add `Access-Control-Allow-Origin: *` on `{catalogUrl}` so discovery crawlers can fetch the catalog.                                                             |
| <a id="37A-AiCatalogAuditor-301" />`37A-AiCatalogAuditor-301` | `WARNING`     | `entries` is an empty array.                                                                                                                                     | Add at least one catalog entry on `{catalogUrl}` so agents can discover capabilities.                                                                           |
| <a id="37A-AiCatalogAuditor-302" />`37A-AiCatalogAuditor-302` | `WARNING`     | The root `host` object is omitted.                                                                                                                               | Add a `host` object with `displayName` on `{catalogUrl}` so publishers can be identified.                                                                       |
| <a id="37A-AiCatalogAuditor-303" />`37A-AiCatalogAuditor-303` | `WARNING`     | `representativeQueries` is present but does not contain 2–5 strings.                                                                                             | Change `representativeQueries` on `{label}` in `{catalogUrl}` from size `{observed}` to 2–5 examples.                                                           |
| <a id="37A-AiCatalogAuditor-304" />`37A-AiCatalogAuditor-304` | `WARNING`     | `updatedAt` is present but is not an ISO 8601 date-time.                                                                                                         | Set `updatedAt` on `{label}` in `{catalogUrl}` to an ISO 8601 timestamp such as `2026-01-15T12:00:00Z` instead of `{observed}`.                                 |
| <a id="37A-AiCatalogAuditor-500" />`37A-AiCatalogAuditor-500` | `ERROR`       | The audited URL is `null` or blank.                                                                                                                              | Pass a non-blank page URL instead of `{observed}` so the auditor can discover `ai-catalog.json`.                                                                |
| <a id="37A-AiCatalogAuditor-501" />`37A-AiCatalogAuditor-501` | `ERROR`       | The audited URL is not a valid URI.                                                                                                                              | Pass an absolute HTTP(S) URL instead of `{url}`; parser reported `{detail}`.                                                                                    |
| <a id="37A-AiCatalogAuditor-502" />`37A-AiCatalogAuditor-502` | `ERROR`       | Discovery failed with an unexpected exception.                                                                                                                   | Retry the audit of `{url}`; the auditor failed with `{detail}`.                                                                                                 |
| <a id="37A-AiCatalogAuditor-200" />`37A-AiCatalogAuditor-200` | `SUCCESS`     | Every rule above passed; one `SUCCESS` check is added with the message `AI catalog at "{catalogUrl}" is valid: specVersion {specVersion}, {entryCount} entries.` | —                                                                                                                                                               |

## Output documentation

| Status          | Description                                                     | Test logic                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **INFORMATION** | Provenance only; does not change overall status                 | Every run includes `37A-AiCatalogAuditor-100` with ARD v0.91 and schema commit `4a8a6b8f…` in the message and `Check.data`.                                                                                                                                                                                                                                                                                                                                                  |
| **SUCCESS**     | A catalog was found and every publishing and schema rule passed | Well-known, `Agentmap`, or HTML `rel="ai-catalog"` returned parseable JSON; `Content-Type` is JSON; `specVersion` is `1.0`; `entries` is a non-empty array; each entry has a valid `urn:air` identifier, `displayName`, `type`, and exactly one of `url` or `data`; CORS is present. The specification pin check is still present.                                                                                                                                           |
| **WARNING**     | The catalog is usable but incomplete for crawlers or search     | Missing `Access-Control-Allow-Origin` (`300`); empty `entries` (`301`); omitted `host` (`302`); `representativeQueries` not sized 2–5 (`303`); or `updatedAt` not ISO 8601 (`304`). No FAIL rule fired. The specification pin check is still present.                                                                                                                                                                                                                        |
| **FAIL**        | The catalog is missing, not JSON, or violates a schema MUST     | Not found at well-known / Agentmap / HTML link (`415`); unparseable JSON (`416`); missing or non-JSON `Content-Type` (`400`, `401`); `specVersion` not `1.0` (`402`); no `entries` array (`403`); `host.displayName` missing (`404`); missing entry fields (`405`–`407`); invalid URN (`408`); both or neither `url`/`data` (`409`, `410`); `trustManifest` without `identity` (`411`); incomplete attestations (`412`–`414`). The specification pin check is still present. |
| **ERROR**       | The request cannot be audited                                   | The audited URL is `null` or blank (`500`), not a valid URI (`501`), or discovery threw (`502`). Checks include the specification pin (`100`) and the process error.                                                                                                                                                                                                                                                                                                         |

## Risks and considerations

### Discovery

* Agents and federated registries look first at `/.well-known/ai-catalog.json`. An HTML SPA that serves `200` for every path hides the catalog unless `Content-Type` is JSON or an `Agentmap` / HTML link points at the real file.
* CORS (`Access-Control-Allow-Origin: *`) is required for browser-based crawlers. A catalog that exists but cannot be read cross-origin is effectively unpublished.

### Schema fidelity

* Identifiers must stay `urn:air:` domain-anchored names. HTTP URLs in `identifier` break federation uniqueness and trust binding described in the [ARD specification](https://agenticresourcediscovery.org/spec/).
* Strict value-or-reference (`url` XOR `data`) keeps parsers from choosing between two payloads. Both or neither makes the entry unusable.
* `representativeQueries` (2–5 examples) are how registries build semantic embeddings. Omitting them is allowed; a list of the wrong size is a warning because search ranking will be weak or rejected by schema-strict consumers.
