
Research
/Security News
737 Chrome VPN Extensions Linked to Brand Impersonation and Browser Traffic Redirection
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.
Build, validate, discover, and resolve ai-catalog.json files for the Agentic Resource Discovery (ARD) spec. TypeScript, zero runtime deps.
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
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 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}`);
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));
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
| 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.
Tracks ARD v1.0 as published in Agent-Card/ai-catalog, including ADR-0014 (mediaType → type) 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.
Apache-2.0
FAQs
Build, validate, discover, and resolve ai-catalog.json files for the Agentic Resource Discovery (ARD) spec. TypeScript, zero runtime deps.
We found that ai-catalog demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.