ardregistry.net

Guide

Building an ARD client

The required surface is one POST endpoint, which is why a useful client fits on one screen.

A client sends POST /search to any registry with a plain-language text query and an optional structured filter, ranks what comes back, verifies any trust manifest, and then connects to the resource over its own protocol. Every conformant registry must expose that endpoint, so one client works against all of them.

What a client has to do

Less than you would expect. There is one required endpoint, and the rest of the specification is about what you may do beyond it.

StepWhat it involves
AskPOST /search with a query object. text is required, filter is optional.
ChooseRank the returned entries. score is semantic relevance only and explicitly not a safety signal, so do not treat it as one.
VerifyIf an entry carries a trustManifest, check it against the framework it declares before connecting. A client that skips this is the weak link in the chain.
ConnectOpen the resource over its own protocol, using the artifact at url. ARD is finished at this point.

The smallest useful client

works against any conformant registry
import json, urllib.request

def discover(text, registry="https://neuronto.com", n=5):
    body = json.dumps({"query": {"text": text}, "pageSize": n}).encode()
    req = urllib.request.Request(
        f"{registry}/search", data=body,
        headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=10) as r:
        return json.load(r).get("results", [])

for hit in discover("extract line items from a scanned invoice"):
    print(hit["score"], hit["displayName"], hit["url"])

That is a complete ARD client. Point it at a different registry and it still works, because the search interface is the one thing every registry must expose. That guarantee is the reason the required surface is so small.

Filtering

text and filter compose: an entry matches if it is semantically relevant AND satisfies every constraint. Within one key, values are ORed; across keys, ANDed.

a filtered search
{
  "query": {
    "text": "book a flight",
    "filter": {
      "type": ["application/a2a-agent-card+json"],
      "tags": ["travel"],
      "trustManifest.attestations.type": ["SOC2-Type2"]
    }
  },
  "federation": "auto",
  "pageSize": 5
}

Any term an entry carries can be a filter key, including namespaced terms you bind in the query's @context. A registry that does not index a term you filter on may answer 400.

Federation, and what it does not mean

ValueBehaviourCost
auto (default)The registry queries its upstreams and merges the results for you. One request, wider corpus.Latency is whatever the slowest upstream costs, unless the registry bounds it.
referralsYou get back a list of other registries to query yourself.More round trips, but you control the fan-out and the timeouts.
noneThat registry's own index only.Fastest. Often the right default for an interactive UI.

A claim to be careful with. Federation means a registry may ask others when answering you. It does not mean your entry is copied into them. Each registry crawls independently. What actually reaches all of them is your manifest, sitting on your own domain where any of them can fetch it.

Practical advice

Which registry to point at

Any of them, and the answer should be configurable rather than compiled in. The examples above use Neuronto, which is run by the people who write this site and is named for that reason as much as any other: it serves search at the path the specification names, answers /explore and /agents, and publishes its own manifest, so a client written against it is written against the spec rather than against one vendor's quirks. The probe showing how each registry actually behaves is the honest way to choose, and it does not flatter everybody equally.

Last reviewed 2026-09-04. Checked against ARD v0.91 (Proposal, 2026-08-26).