---
name: ad
description: >-
  Interact with an Agent Directory (AD) instance. Use when user says
  "register agent", "find agents", "search AD", "look up capabilities",
  "check the directory", "what agents are registered", or needs to
  register, update, refresh, delete, or look up agents and capabilities.
  Do NOT use for editing the AD draft (that's manual IETF work) or
  deploying the AD worker (use app-deploy).
---

# Agent Directory (AD)

A soft-state registry for AI agents. Agents register with a name, base
URI, interaction protocols, and capabilities. Clients look up agents by
capability name, capability type, tag, or protocol.

Specification: [draft-jimenez-agent-directory-00](https://datatracker.ietf.org/doc/draft-jimenez-agent-directory/).

## Setup

Before any operation, ask the user:

> What is the URL of your Agent Directory? (default: `https://ad.jaime.win`)
> What Bearer token should I use to register/update/delete agents? (any opaque string; it's hashed and bound to the registration)

Store the answers as `{base_url}` and `{token}` for all subsequent
calls. Write methods MUST send `Authorization: Bearer {token}`; the
same token must be presented for any later refresh or delete on a
registration. Lookup and GET do not need a token.

## API

### Discovery

```
GET {base_url}/.well-known/ad
```

Returns `{"registration":"/ad/r","lookup":"/ad/l","max_count":500}`.

### Register

```
POST {base_url}/ad/r?agent={name}&lt={seconds}
Authorization: Bearer {token}
Content-Type: application/json

{
  "base": "https://api.open-meteo.com",
  "description": "Open-Meteo weather API",
  "protocols": ["http"],
  "capabilities": [
    {"name": "forecast", "type": "tool",
     "description": "Weather forecast by coordinates",
     "tags": ["weather", "geo"]}
  ],
  "version": "1.0",
  "vendor": "Open-Meteo",
  "identity": "https://id.example.com/agents/weather",
  "identity_type": "aip"
}
```

- `agent` (required): agent name, unique within the AD instance. Max 63 chars. Must start with a letter or digit and contain only letters, digits, `.`, `_`, or `-` (deployment constraint; spec allows any Unicode string).
- `lt` (optional): lifetime in seconds, default 86400, min 60, max 604800 (server clamps).
- `base` (required, body): absolute `http:` or `https:` URI for the agent (deployment constraint; spec allows any RFC 3986 URI).
- `protocols` (optional, body): array of interaction-protocol identifiers. Spec-defined values: `mcp`, `a2a`, `grpc`. Lowercase, may carry a version suffix like `mcp/1.0`.
- `capabilities[].type` (required per capability): one of `tool`, `skill`, `resource`, `prompt` (spec-defined). Other values are allowed but clients may ignore them.
- `capabilities[].tags` (optional): array of strings for tag filtering in lookup.
- `description`, `version`, `vendor`, `identity`, `identity_type` (optional, body). `identity_type` values: `aip`, `oidc`, `wimse`, `did`.
- Each capability MUST have `name` and `type`; capability names MUST be unique within a registration.
- Returns 201 (new) or 200 (replace) with empty body; the registration URI is in the `Location` header.

### Refresh / Update

```
POST {base_url}/ad/r/{id}
Authorization: Bearer {token}
```

Resets the lifetime. Optionally send a JSON body to update capabilities,
protocols, base, version, vendor, description, identity, or identity_type.
Requires the same token used at registration. Returns 204, or 403 if
the token does not match.

### Get Registration

```
GET {base_url}/ad/r/{id}
```

Returns full registration including remaining lifetime.

### Delete

```
DELETE {base_url}/ad/r/{id}
Authorization: Bearer {token}
```

Requires the same token used at registration. Returns 204, or 403 if
the token does not match.

### Lookup

Single endpoint with a `view` parameter:

```
GET {base_url}/ad/l                          # agents (default)
GET {base_url}/ad/l?view=cap                 # capabilities with agent context
GET {base_url}/ad/l?agent=weather            # filter by agent name
GET {base_url}/ad/l?cap_name=forecast        # filter by capability name
GET {base_url}/ad/l?cap_type=tool            # filter by capability type
GET {base_url}/ad/l?tag=weather              # filter by capability tag
GET {base_url}/ad/l?protocol=http            # filter by protocol
GET {base_url}/ad/l?cap_name=fore*           # wildcard prefix match
GET {base_url}/ad/l?page=0&count=50          # pagination (max 500)
```

Agent view returns summaries (name, type) per capability:
```json
{"agents": [{"agent":"weather","base":"https://api.open-meteo.com","description":"Open-Meteo weather API","protocols":["http"],"capabilities":[{"name":"forecast","type":"tool"}],"href":"/ad/r/1"}]}
```

Capability view returns full capability objects with agent context:
```json
{"capabilities": [{"name":"forecast","type":"tool","description":"Weather forecast by coordinates","tags":["weather","geo"],"agent":"weather","base":"https://api.open-meteo.com","protocols":["http"],"href":"/ad/r/1"}]}
```

To get full capability details (input/output schemas, etc.), GET the
individual registration resource via the `href` returned in lookup.

## Error Responses

All errors use RFC 9457 Problem Details (`application/problem+json`):

```json
{"type":"about:blank","title":"Missing agent parameter","detail":"The agent query parameter is required.","status":400}
```

Status codes used by the current deployment: 400, 401, 403, 404, 409.
This deployment implements proof-of-possession: the caller presents a
Bearer token at registration, the AD stores its SHA-256 hash, and later
mutations must present the same token. The token is opaque; there is
no identity provider. 429 rate limiting is not yet implemented.

## Gotchas

- Agent names are unique within the AD. Re-registering an existing name
  acts as an upsert and returns 200 (with the existing Location); a new
  name returns 201.
- `base` goes in the JSON body, not the query string. Only `agent` and
  `lt` are query parameters on registration.
- Capabilities are always objects `{"name":"x","type":"tool"}`, never
  bare strings. Both `name` and `type` are required.
- Registrations expire. Default lifetime is 24 hours, max 7 days. Agents
  must refresh with `POST {base_url}/ad/r/{id}` before expiry.
- The `view` parameter on `/ad/l` defaults to `agent`. Use `view=cap`
  for the capability-centric response.
- Wildcard matching on `agent` and `cap_name` only works as a suffix:
  `fore*` matches `forecast`, but `*cast` does not.
- Always ask for the AD URL before the first operation. Do not assume a
  hardcoded instance.
- This deployment rejects `base` URIs that are not `http:` or `https:`
  with 400. If you need a DID or other non-HTTP URI for `base`, use a
  different AD instance or put the URI in `identity` and set `base` to
  the HTTP endpoint where the agent answers.
- This deployment rejects agent names with spaces, slashes, or other
  characters outside `[A-Za-z0-9._-]` with 400.
