Skip to content

fernforge/ai-catalog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ai-catalog

The only TypeScript toolkit for Agentic Resource Discovery (ARD). The spec's reference tooling is a Python conformance CLI; if you're shipping a site or MCP server from Node, this is the side you were missing. Zero runtime dependencies, ESM + CJS, Node 18+.

npx ai-catalog validate ./.well-known/ai-catalog.json
error /entries/0/type  required `type` is missing — `mediaType` was renamed to `type` (ADR-0014)

That rename (ADR-0014) is one the spec's own shipped example still gets wrong. The validator tracks the normative spec text, so it catches the things a hand-edited catalog drifts on.

ARD lets a website advertise its AI agents, MCP servers, skills, and datasets at /.well-known/ai-catalog.json so agents can find them before invocation. This package is the TypeScript side: typed builders, a spec-aware validator with precise error paths, the full agent-driven discovery walk, and nested-catalog resolution.

npm install ai-catalog

Validate a catalog (CLI)

npx ai-catalog validate ./.well-known/ai-catalog.json
error /entries/0/type  required `type` is missing — `mediaType` was renamed to `type` (ADR-0014)
warn  /entries/1/identifier  should use urn:air:{publisher}:{namespace}:{name} for open/federated use

The validator splits the spec's normative MUST rules (missing required members, the exactly-one-of url/data rule, identifier/version uniqueness, trust-identity domain alignment) from SHOULD/RECOMMENDED rules (urn:air identifiers, known media types, RFC 3339 timestamps), so you see error vs warn instead of one undifferentiated wall. Pass --strict to fail on warnings too.

Point it at a live one — Hugging Face (an ARD co-author) publishes a catalog you can validate today:

npx ai-catalog discover https://huggingface.co
npx ai-catalog validate <(curl -s https://huggingface.co/.well-known/ai-catalog.json)

It passes, with one warn: their registry entry uses application/ai-registry+json, which isn't in the spec's recommended type set. That's the point of the split — a real-world file that's correct on every MUST but carries a media type the spec doesn't enumerate shows up as a note, not an error.

Discover what a site publishes

discover runs the spec's agent-driven procedure in order — HTTP Link header, then HTML <link rel="ai-catalog">, then the /.well-known/ fallback — and tells you which one hit:

npx ai-catalog discover https://example.com
import { discover } from "ai-catalog";

const { url, via, catalog } = await discover("https://example.com");
// via: "link-header" | "html-link" | "well-known"
console.log(`${catalog.entries.length} entries, found via ${via}`);

Build one, type-safe

import { CatalogBuilder, KnownType, buildAirUrn } from "ai-catalog";

const catalog = new CatalogBuilder({
  displayName: "Acme Services",
  identifier: "did:web:acme.com",
})
  .addEntry({
    identifier: buildAirUrn("acme.com", "mcp", "weather"),
    type: KnownType.McpServerCard,
    description: "Weather lookup MCP server.",
    tags: ["weather", "mcp"],
    url: "https://acme.com/.well-known/mcp/weather.json",
  })
  .addNestedCatalog("urn:air:acme.com:catalog:ml", "https://acme.com/ml/ai-catalog.json")
  .validateOrThrow(); // throws with every problem listed, or returns the catalog

import { writeFileSync } from "node:fs";
writeFileSync(".well-known/ai-catalog.json", JSON.stringify(catalog, null, 2));

Resolve nested catalogs

A catalog can point at child catalogs (type: application/ai-catalog+json). resolveCatalog walks the tree, flattens every leaf entry, tags each with the depth and source URL it came from, follows the spec's depth limit of 4, and won't loop on a cycle:

import { resolveCatalog, selectLatest, resolveDisplayName } from "ai-catalog";

const entries = await resolveCatalog("https://acme.com/.well-known/ai-catalog.json");

for (const e of entries) {
  console.log(e.depth, resolveDisplayName(e), e.type);
}

// Multi-version entries: pick the newest by SemVer, then updatedAt.
const latest = selectLatest(entries, "urn:air:acme.com:agent:finance");

CLI equivalent:

npx ai-catalog resolve https://acme.com --names

API

Export What it does
validateCatalog(doc, { strict? }) Structural validation → { valid, errors, warnings } with JSON-pointer paths
assertValidCatalog(doc) Throwing validator; narrows the type to AiCatalog
CatalogBuilder / defineCatalog(host?) Chainable typed builder
discover(siteUrl, opts?) Agent-driven discovery (Link header → HTML link → well-known)
fetchCatalog(url, fetch?, timeoutMs?) Fetch + validate a catalog from a URL
resolveCatalog(rootOrUrl, opts?) Walk nested catalogs, flatten to leaf entries
resolveDisplayName(entry, referencedName?) Spec display-name resolution order
selectLatest(entries, identifier) Newest entry among shared-identifier versions
parseAirUrn / buildAirUrn / isAirUrn / identifierTail urn:air helpers
parseLinkHeader / parseHtmlLink Standalone discovery parsers
KnownType, WELL_KNOWN_PATH, CATALOG_MEDIA_TYPE, SPEC_VERSION Spec constants

All functions that fetch accept an opts.fetch so you can inject a mock, a proxy, or an authenticated client; on Node 18+ the global fetch is the default.

Spec tracking

Tracks ARD v1.0 as published in Agent-Card/ai-catalog, including ADR-0014 (mediaTypetype) and the depth-4 nesting recommendation. ARD is a v0.9-era draft and still moving; if a field name shifts under you, open an issue.

License

Apache-2.0

About

Build, validate, discover, and resolve ai-catalog.json files for the Agentic Resource Discovery (ARD) spec. TypeScript, zero runtime deps.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors