Skip to main content
Glama
README.md
# Agent Doctor

An AI-agent-facing service that **audits ERC-8004 agent registrations** and tells
owners exactly how to fix them.

**Why:** of 170,000+ agents registered in the ERC-8004 registries, only **3–15%
expose BOTH a valid registration file AND a live declared endpoint**
([arXiv:2606.26028](https://arxiv.org/abs/2606.26028)). Agent Doctor diagnoses
that gap: it resolves the on-chain identity, fetches and validates the
registration file, probes every declared endpoint for liveness, verifies the
ENSIP-25 bidirectional ENS link, and returns a graded report (`A`–`F`) with a
concrete, ordered fix list.

## What it checks

| Check | Meaning |
| --- | --- |
| `identity.resolve` | `tokenURI(agentId)` on the IdentityRegistry (`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`, same address on Ethereum, Base, Arbitrum, Optimism, Polygon, Linea) returns a non-empty agentURI |
| `registration.fetch` | the agentURI (https / ipfs / data:) serves valid JSON |
| `registration.structure` / `core_fields` / `services` | registration-v1 shape: `type`, `name`, `description`, `image`, well-formed `services[]` (lenient — soft fields warn, not fail) |
| `registration.registrations` | `registrations[]` actually references this chain + registry + agentId (`eip155:{chainId}:{registry}`) |
| `endpoint.*` | liveness probe per declared service — generic HTTP GET; A2A additionally looks for the agent card (endpoint itself, then `/.well-known/agent-card.json`); MCP is reachability-only (no handshake in MVP); ENS/DID/email/agentWallet are identifiers, skipped here |
| `ens.*.bidirectional` | [ENSIP-25](https://docs.ens.domains/ensip/25): the registration file references the ENS name **and** the name carries the text record `agent-registration[<ERC-7930 registry>][<agentId>]` — either side alone fails |

Grades: `A` all pass · `B` warnings only · `C` endpoint/ENS failures ·
`D` broken registration file · `F` identity unresolvable.

## Quickstart

```bash
cd agent-doctor
python3 -m venv .venv
.venv/bin/pip install web3 fastapi uvicorn httpx mcp pytest
```

### CLI

```bash
.venv/bin/python -m agent_doctor check --chain base --agent-id 42
.venv/bin/python -m agent_doctor check --chain base --agent-id 42 --ens myagent.eth
.venv/bin/python -m agent_doctor check --ens myagent.eth          # best-effort discovery
.venv/bin/python -m agent_doctor check --chain base --agent-id 42 --json
```

Exit code `0` for grades A/B, `1` otherwise (CI-friendly).

### HTTP API

```bash
.venv/bin/uvicorn agent_doctor.api:app --port 8004
curl 'http://127.0.0.1:8004/check?chain=base&agentId=42'
curl 'http://127.0.0.1:8004/check?ens=myagent.eth'
curl 'http://127.0.0.1:8004/healthz'
```

### MCP (stdio)

```bash
.venv/bin/python -m agent_doctor.mcp_server
```

Exposes one tool, `diagnose_agent(chain, agent_id, ens, timeout)`. Claude Code
registration example:

```bash
claude mcp add agent-doctor -- /path/to/agent-doctor/.venv/bin/python -m agent_doctor.mcp_server
```

### Configuration

- `AGENT_DOCTOR_RPC_<CHAIN>` (e.g. `AGENT_DOCTOR_RPC_BASE`) — override the
  default publicnode RPCs; ENS always resolves on Ethereum mainnet.
- `AGENT_DOCTOR_IPFS_GATEWAY` — default `https://ipfs.io/ipfs/`.
- `AGENT_DOCTOR_HTTP_TIMEOUT` — default 10 s.

## Tests

```bash
.venv/bin/python -m pytest            # unit tests (offline)
.venv/bin/python -m pytest --live     # + live read-only test against the real Base registry
```

Fixtures include the ENSIP-25 worked example: registry
`0x8004A169…a432` on `eip155:1`, agentId 42 →
`agent-registration[0x000100000101148004a169fb4a3325136eb29fa0ceb6d2e539a432][42]`.

## Register Agent Doctor itself (owner-only steps)

The repo ships its own dogfood identity in `identity/`:
`registration.json` (valid per our own validator), `mint_identity.py`
(unsigned tx builder — never touches keys), `ens_records.py` (exact ENSIP-25
record printer). To go live:

1. **Registration file is hosted** — this repo is public, so the canonical
   agentURI is
   `https://raw.githubusercontent.com/Maxwell012/agent-doctor/main/identity/registration.json`.
   The `agentdoctor.example.com` service endpoints inside it are still
   placeholders — replace them once the HTTP API is deployed somewhere real.
2. **Fund a wallet on Base** (a few dollars of ETH for gas).
3. **Mint the identity** — build the unsigned tx and sign it in your own wallet:
   ```bash
   .venv/bin/python identity/mint_identity.py --agent-uri https://raw.githubusercontent.com/Maxwell012/agent-doctor/main/identity/registration.json --from 0xYourAddress
   ```
   Function: `register(string agentURI)` on
   `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432` (Base, chainId 8453).
   Read the minted `agentId` from the `Registered` event, then update
   `registrations[].agentId` in `registration.json` (currently a `0`
   placeholder) and re-upload.
4. **Buy the ENS name** — `agent-doctor.eth` (checked free on mainnet
   2026-07-13); register it in the ENS app, set its ETH address record to the
   minting wallet.
5. **Set the ENSIP-25 text record** (bidirectional link):
   ```bash
   .venv/bin/python identity/ens_records.py --ens <yourname>.eth --chain base --agent-id <agentId>
   ```
   Set the printed key to value `1` in the ENS app; also update the ENS
   `services[]` entry in `registration.json` if the name differs from the
   placeholder. Optionally set profile text records (`avatar`, `description`,
   `url`).
6. **Optional:** buy the $1 verification certificate at
   [veral.tech](https://veral.tech) for an extra trust signal.
7. **Self-check until grade A:**
   ```bash
   .venv/bin/python -m agent_doctor check --chain base --agent-id <agentId> --ens <yourname>.eth
   ```

## Design notes / known limits (MVP)

- Read-only chain access; no keys, no signing, no state.
- The deployed registry proxy does **not** answer `totalSupply()` /
  enumeration (observed 2026-07-09), so ENS-only diagnosis without an agentId
  is best-effort and warns instead of guessing.
- MCP probe is reachability-only; a real MCP handshake (initialize/tools-list)
  is a natural next step.
- Registration schema is validated leniently on purpose — the EIP-8004
  registration-v1 field list is not a closed schema.