Skip to content

AI Catalog Go SDK

OpenSSF Scorecard

A Go toolkit for consuming, validating, and analyzing AI Catalog documents — the typed, nestable JSON format for making heterogeneous AI artifacts (MCP servers, A2A agents, datasets, model cards, nested catalogs, …) discoverable.

The SDK is a faithful implementation of the AI Catalog specification.

Scope

The repository follows the AI Catalog spec's own split between normative content (defines the format and conformance) and non-normative content (informative or convenience code):

  • Normative — a faithful implementation of the spec and the stable, supported API: the document/entry types, parsing, and querying (catalog); conformance validation (validate); and trust-manifest analysis and canonicalization (trust). For convenience, catalog also adds lookups the spec does not require (e.g. GetByTag, GetByPublisher, SearchByRegex) on the spec-defined types.
  • Non-normative — code the spec does not define:
    • provider (and the catalog.Source interface) — a supported convenience for loading a catalog from a local file or an HTTP endpoint.
    • examples/ — informative, spec-adjacent samples such as packaging a catalog as an OCI artifact (the spec only describes an informative "mapping to OCI"). Reference code to copy and adapt, not part of the supported API.

Installation

go get github.com/Agent-Card/ai-catalog-go

The minimum Go version is declared in go.mod.

Usage

Load a catalog

The provider package returns a catalog.Source — a loader for an AI Catalog. Each built-in loads the document as-is (nested catalog entries are left unresolved for the caller to follow as needed).

import (
	"context"

	"github.com/Agent-Card/ai-catalog-go/catalog"
	"github.com/Agent-Card/ai-catalog-go/provider"
)

ctx := context.Background()

// From a local file:
src, err := provider.JSON("ai-catalog.json")

// From an explicit URL:
src, err = provider.Web(ctx, "https://acme-corp.com/catalogs/finance.json")

// From a domain's well-known URI (RFC 8615):
src, err = provider.Web(ctx, "https://acme-corp.com"+catalog.WellKnownPath)

Web retrieves documents over HTTP; supply a custom client with provider.WithHTTPClient(myClient) or an entirely custom transport with provider.WithFetcher(...).

If you already hold a parsed *catalog.AICatalog, you don't need a Source — call its methods directly (see below).

Query entries

Source has a single method, Load, which returns the whole catalog in memory as a *catalog.AICatalog. Query it with the document's methods, which cover the entries of that document — follow any nested catalog entry yourself to query its contents:

doc, err := src.Load(ctx)
if err != nil {
	// handle load error
}

entry, ok := doc.GetByID("urn:air:acme-corp.com:mcp:weather")
mcpServers := doc.GetByType(catalog.MediaTypeMCPServerCard)
hits := doc.Search("weather")
matched, err := doc.SearchByRegex(`^urn:air:acme-corp\.com:`)

The same methods are available on any parsed document, so a Source is not required:

doc, _ := catalog.ParseFile("ai-catalog.json")

entry, ok := doc.GetByID("urn:air:acme-corp.com:mcp:weather")
agents := doc.GetByType(catalog.MediaTypeA2AAgentCard)
byTag := doc.GetByTag("finance")
byPublisher := doc.GetByPublisher("did:web:acme-corp.com")

Multiple versions of an artifact

A catalog may list several entries with the same identifier and different version values. The SDK selects among them per the spec:

all := doc.Versions("urn:air:acme.com:agent:finance")             // every version
v2, ok := doc.GetByIDAndVersion("urn:air:acme.com:agent:finance", "2.0.0")
latest, ok := doc.GetLatest("urn:air:acme.com:agent:finance")      // semver, then updatedAt

GetLatest prefers entries whose version parses as a Semantic Version (compared with golang.org/x/mod/semver, ties broken by the more recent updatedAt), falling back to the newest updatedAt when no version is parseable.

Resolve a display name

Follows the spec's resolution order for the steps that don't require fetching the artifact: the entry's displayName, otherwise the trailing segment of its identifier.

name := entry.ResolveDisplayName()
// "urn:air:acme-corp.com:mcp:weather" -> "weather"

Validate and detect conformance level

import "github.com/Agent-Card/ai-catalog-go/validate"

result := validate.Validate(doc)
if !result.IsValid {
	for _, d := range result.Errors {
		log.Printf("%s: %s", d.Path, d.Message)
	}
}
log.Printf("conformance level: %s", result.ConformanceLevel) // minimal | discoverable | trusted

// Validate whatever a Source is backed by:
result, err := validate.Source(ctx, src)

Analyze trust metadata

import "github.com/Agent-Card/ai-catalog-go/trust"

report := trust.AnalyzeCatalog(doc)
for _, f := range report.Findings {
	log.Printf("[%s] %s: %s", f.Severity, f.Path, f.Message)
}

// Verify an attestation digest against its bytes:
ok, err := trust.VerifyDigest("sha256:9f86d0...", data)

// Canonicalize a manifest (JCS, RFC 8785) prior to signing/verification:
canonical, err := trust.CanonicalizeTrustManifest(entry.TrustManifest)

A signature covers the document as published, so verify against the original bytes rather than a re-serialized document — otherwise any member this SDK does not model drops out of the payload and the signature will not match. The built-in providers keep those bytes and expose them through catalog.RawSource:

if rawSource, ok := src.(catalog.RawSource); ok {
	raw, err := rawSource.Raw(ctx)

	// Strips the top-level "signature" and canonicalizes the rest:
	payload, err := trust.CanonicalizeForSignature(raw)
}

Package as an OCI artifact

Mapping a catalog onto OCI is not part of the specification, so it lives in examples/oci rather than the SDK. Run it with:

go run ./examples/oci

Development

This repository uses Task:

task test   # run unit tests with race detector and coverage
task lint   # run golangci-lint

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

License

Apache-2.0. See LICENSE.

About

AI Catalog SDK written in Golang

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages