# erc8004-agent-liveness [Health: Active]

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/nexus-mcp-infra/erc8004-agent-liveness  
**GitHub Stars:** 0  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/erc8004-agent-liveness

## Description
Checks whether an ERC-8004-registered agent is actually alive right now via a real MCP handshake

## Claude Desktop Quick Installation
Remote MCP endpoint (confidence: high). Install path detected from listing signals. Add as a URL/SSE server in your client:

```json
"mcpServers": {
  "erc8004-agent-liveness": {
    "url": "https://eips.ethereum.org/EIPS/eip-8004"
  }
}
```

## Documentation & README

# ERC-8004 Agent Liveness

Checks whether an agent registered in the real ERC-8004 "Trustless Agents" Identity Registry (Base
mainnet -- both the payment and the registry read, see "Chain scope" below)
is actually alive right now -- not just that it was registered once. NEXUS candidate #10 --
**manual build, not FORGE-generated**, same manual-Cloud-Run-asset pattern as candidates #3/#4/#6/#8/#9/#13/#16.

- `POST /verify-registered-agent {"agent_id": 3}` -- **$0.10/call.**
- MCP tool `verify_registered_agent` at `/mcp`, same params -- **currently free**, see "Known limitations".
- `GET /health`, `GET /.well-known/agent-card.json`, `GET /openapi.json` (has `x-payment-info`),
  `GET /.well-known/402index-verify.txt` (402index claim verification file).

## What this is (and why registration alone isn't enough)

[ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) is a real, live Ethereum standard for on-chain agent
identity: an agent mints an ERC-721 token in an `IdentityRegistry`, whose `tokenURI` points to an off-chain
registration file (JSON: name, description, declared endpoints, `active` flag, supported trust methods). It
went live on Ethereum mainnet 2026-01-29, with reference deployments on Base mainnet and Base/Ethereum/Linea
Sepolia testnets. Registration is a one-time on-chain action -- a real registered agent can go completely
dark (process killed, domain expired, endpoint changed) while its on-chain record persists unchanged forever.
This asset closes that gap: it resolves the real on-chain registration AND performs a real MCP `initialize`
handshake against whatever endpoint the registration declares, right now, at call time -- the same
liveness-vs-registration distinction `agent-verification-api` (candidate #3) already draws for
domain-claimed identities, applied here to on-chain-registered ones.

## Grounding (verified live this session, not assumed from any single source)

- **Contract addresses**, initially from a third-party summary, verified independently via `eth_getCode`
  against `https://sepolia.base.org` before being trusted: `IdentityRegistry`
  (`0x8004A818BFB912233c491871b3d84c89A494BD9e`) and `ReputationRegistry`
  (`0x8004B663056A597Dffe9eCcC1965A193B7388713`) both have real, non-empty deployed bytecode.
- **ABI**, pulled from the reference implementation (`github.com/erc-8004/erc-8004-contracts/abis`), tested
  live against 3 real registered agents (`agentId` 1-3) before being trusted for this asset:
  - Agent 1's `tokenURI` resolves to a `data:application/json;base64,...` URI.
  - Agent 2's resolves to `ipfs://bafkreiff...`.
  - Agent 3's resolves to a real `https://api.snack.money/agent/.../registration.json` URL.
  All 3 of ERC-8004's real URI schemes confirmed working live, not just the one the spec's own examples show.
- **`getSummary` requires a non-empty `clientAddresses` array** -- confirmed live (reverts with
  `"clientAddresses required"` otherwise). This asset calls `getClients(agentId)` first and only calls
  `getSummary` if that returns at least one address; agents with zero feedback correctly report
  `feedback_count: 0` without an RPC error.
- **`agentId=999999` (a real nonexistent token)** correctly reverts with a custom error, confirmed live --
  mapped to `AGENT_NOT_FOUND`, not a crash.

## MCP handshake engine: reused, not reimplemented

Per the task brief's explicit instruction, `_nexus_validate_public_url`, `_nexus_no_redirect_mcp_http_client`,
and `_mcp_handshake_check` are ported **verbatim** from `manual_assets/agent-verification-api/main.py`
(candidate #3) -- same SSRF pre-check, same no-redirect posture (the exact fix applied there after the
2026-08-22 security review found a redirect-based SSRF bypass), same bounded timeout. Not modified beyond the
asset-name constant. This asset's own contribution is upstream of that: resolving an ERC-8004 registration
file (3 URI schemes) and picking a real endpoint out of its `endpoints` array to hand to that engine.

## Chain scope

**Unified, as of 2026-09-04.** x402 payment settles in real USDC on **Base mainnet**. The on-chain
`IdentityRegistry` read -- the actual identity/liveness check -- is now also on **Base mainnet**, against the
verified `IdentityRegistryUpgradeable` deployment (`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`) -- see
"Mainnet registry corrected" below. `ReputationRegistry` reads still use the original (Sepolia-verified)
address -- its real mainnet counterpart was not verified this session, see "Pending". This asset still doesn't
expose a caller-selectable chain -- no evidence a buyer needs one for either rail.

## Mainnet registry corrected (2026-09-04)

The 2026-09-03 partial revert below was investigating the wrong contract. `0x8004A818BFB912233c491871b3d84c89A494BD9e`
is itself the deterministic Base SEPOLIA IdentityRegistry address from `erc-8004/erc-8004-contracts`'s
vanity-address deployment pattern -- it was never the real Base mainnet deployment, which is a *different*
deterministic address per network. The real Base mainnet IdentityRegistry is
`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`: implementation `0x7274e874ca62410a93bd8bf61c69d8045e399c02`
verified on Etherscan as `IdentityRegistryUpgradeable` (exact name match with the reference repo, compiler
v0.8.24+commit.e11b9ed9), `ownerOf` reads succeed, and a Basescan export shows 25 real Base mainnet
transactions against it (2026-09-04 09:02-10:06 UTC, 25/25 successful: 5x `register`, 20x `setMetadata`) --
versus 19 historical transactions against the old address (2026-01-31 to 2026-04-20), 17/19 reverted. Full
verification trail: `scripts/verify_mainnet_registry_candidate.py`, branch
`claude/erc8004-registry-address-verify-u2db8u`. `ReputationRegistry`'s real mainnet address was not verified
this session -- left unchanged, see "Pending".

## Mainnet cutover (2026-09-03) and same-day partial revert

Originally built and measured on Base Sepolia testnet (x402 payment + registry reads both). Cut over to Base
mainnet same session: x402 settlement moved to the CDP facilitator (`create_facilitator_config()`, same swap
already applied to `ws`/`live-entity-verification`), the payto wallet moved to `NEXUS_X402_PAYTO_ADDRESS`
(fail-fast env var, no placeholder default), and the registry RPC moved to Base mainnet too, at the same two
contract addresses -- that address reuse across chains was confirmed by the operator directly on Basescan
before the cutover, not independently re-verified from the session that made the code change (no outbound
network access to `mainnet.base.org` from that environment).

**Reverted a few hours later, registry-read side only.** Two independent sources (a live `eth_call`, and
Basescan's own "Read as Proxy" tab) confirmed `ownerOf`/`balanceOf` both revert with "execution reverted, no
data" against the IdentityRegistry proxy (`0x8004A818BFB912233c491871b3d84c89A494BD9e`) on Base mainnet --
while `register()` against that same proxy demonstrably works (19 real confirmed mint transactions). The
implementation contract behind the proxy on mainnet (`0xd53dE688e0b0ad436FBdbDa00036832FF6499234`, confirmed
via Basescan's proxy-implementation slot) has **no verified source or ABI anywhere on Etherscan/Basescan** --
so there's no way to confirm the deployed mainnet bytecode still matches
`github.com/erc-8004/erc-8004-contracts` (commit b9e466c) the way the Sepolia deployment's bytecode was
originally confirmed (see "Grounding" above). Continuing to call `ownerOf`/`tokenURI`/`getClients`/`getSummary`
by name against unverified mainnet bytecode risked the exact failure this asset exists to catch, but aimed at
its own buyers instead: a plausible wrong answer (`AGENT_NOT_FOUND` for a real agent) charged for as if
correct.

`BASE_RPC_URL` (default `https://mainnet.base.org`) and `_IDENTITY_REGISTRY_ADDRESS`
(`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`) now both point at the real, verified Base mainnet
IdentityRegistry -- see "Mainnet registry corrected" above. No CDP RPC node pattern exists elsewhere in this
codebase to reuse (checked `onchain-activity-index`, `x402-receipt-verifier`, `new-x402-listings-feed` --
none of them read on-chain data via RPC at all, only x402 payment went through CDP), so this reuses the same
public endpoint and env var name already used once in the original 2026-09-03 cutover attempt. Every
buyer-facing surface (agent-card, OpenAPI descriptions, this README) has been updated to say both payment and
the registry read are Base mainnet.

## Pending

Resolved 2026-09-04: the mainnet cutover this section used to say was blocked on decompiling
`0xd53dE688e0b0ad436FBdbDa00036832FF6499234` -- that turned out to be unnecessary. That address was never the
real mainnet IdentityRegistry implementation; the real one
(`0x7274e874ca62410a93bd8bf61c69d8045e399c02`) is independently verified on Etherscan as
`IdentityRegistryUpgradeable`, and the cutover is done (see "Mainnet registry corrected" above).

Still open, left explicit rather than guessed at:

- `ReputationRegistry`'s real Base mainnet address was not verified this session -- reputation reads still use
  the Sepolia-deterministic address over the now-mainnet RPC, which may be wrong. Needs the same
  bytecode/implementation-verification treatment `IdentityRegistry` just got before it's trusted.
- `_BASE_RPC` uses the free public `https://mainnet.base.org` -- no SLA. CDP's own SDK (already a dependency
  here) ships a "Base Node" RPC endpoint reusing the same CDP credentials already configured for the x402
  facilitator (`cdp.base_node_rpc_url.get_base_node_rpc_url`, verified by reading the actual cdp-sdk source),
  but switching to it needs an async-startup restructure (today's RPC client is built synchronously at module
  import) and its plan-tier availability isn't confirmed. Deferred until there's a concrete reliability/cost
  reason to take that on -- see the comment above `_BASE_RPC` in `main.py`.

## Deploy target: Cloud Run

Same pipeline as candidates #4/#3/#6/#8/#9/#13/#16 -- see `skills/infra-deploy-ops`.

```bash
# 1. First deploy -- PUBLIC_DOMAIN not known yet, every real request 421s until step 2.
./scripts/deploy_cloud_run.sh erc8004-agent-liveness manual_assets/erc8004-agent-liveness

# 2. Grab the printed *.run.app URL, then (only if it differs from env-vars.deploy.yaml's guess):
gcloud run services update erc8004-agent-liveness --region us-central1 --project nexus-505016 \
    --update-env-vars PUBLIC_DOMAIN=<the-real-domain>
```

## Known limitations (left unfixed on purpose -- CLAUDE.md SS3, no gate without evidence it's needed)

- **MCP tool calls are not charged.** Same in-process-call pattern as every other manual asset in this
  codebase.
- **`active: false` short-circuits to `REGISTERED_INACTIVE` even if the endpoint is actually live.** Trusts
  the registrant's own self-declaration over an independent liveness check in that one case -- an agent
  lying about being inactive (unusual incentive) would be misreported. Accepted: `active` is the registrant's
  own signal by spec design, overriding it would be second-guessing the standard's own field.
  `REGISTERED_UNREACHABLE` (the opposite failure mode -- declared active, not actually reachable) is this
  asset's actual value-add and is NOT similarly short-circuited.
  - **Endpoint selection is a heuristic, not a spec requirement.** ERC-8004's `endpoints` array is free-form
  (any `name`); this asset prefers `mcp`/`x402`/`a2a`/`web` (in that order) and falls back to the first entry.
  A registration using an unlisted `name` for its only real MCP-capable endpoint would still be picked (name
  matching isn't the only path -- unnamed-preference fallback covers it), but a registration with MULTIPLE
  endpoints where none of the preferred names points to the live one could report `REGISTERED_UNREACHABLE`
  based on the wrong endpoint.
- **IPFS resolution uses a single public gateway (`ipfs.io`).** No fallback gateway -- a registration whose
  CID isn't pinned/reachable via that specific gateway reports `REGISTRATION_FETCH_FAILED` even if the
  content exists on IPFS generally.
- **No per-caller rate limiting.** Fine for a 7-day disposable measurement window.
- **`reputation` is a self-reported, ungated, un-staked signal.** Any EVM address can call
  `ReputationRegistry.giveFeedback` for any `agentId` -- `feedback_count`/`average_value` are real on-chain
  numbers, but nothing stops an agent's own owner from Sybil-feeding themselves. Treat as an unverified
  signal, not a trust score (this caveat is also in the `reputation` field's own description in the API
  schema, not just here).

## Quality gate (2026-08-23, from design not retroactive)

Same 2-agent process as candidates #3/#4/#6/#8/#9/#13/#16 (security lens; functional+quality+buyer-experience
lens), run before the first deploy. Real findings, all fixed before going live:

- **Security (0 exploitable findings):** confirmed the 3 functions ported verbatim from
  `agent-verification-api/main.py` (`_nexus_validate_public_url`, `_nexus_no_redirect_mcp_http_client`,
  `_mcp_handshake_check`) carry the actual SSRF-redirect fix, byte-for-byte, and that the new
  `https://`/IPFS registration-fetch path applies the same defense one layer earlier, before any endpoint is
  extracted -- confirmed NOT to reintroduce that bug class. Two low/informational items, both addressed as
  defense-in-depth even though neither was a confirmed exploit: IPFS CID concatenation (confirmed live it
  couldn't escape the `ipfs.io` host, but now uses `urllib.parse.quote` to confine it to a single path
  segment anyway), and `data:` URI base64 decoding (confirmed linear/non-amplifying, no fix needed).
- **Functional/buyer-experience (1 must-fix, 1 medium, applied):** a registration file whose top-level JSON
  is a non-object (array/string/number -- registrant-controlled content) passed through as `"ok": True` with
  a non-dict `registration`, which every downstream consumer (`_pick_liveness_endpoint`, `_classify_verdict`,
  the response body) assumed was a dict -- an uncaught `AttributeError` became an unhandled 500 **after x402
  payment had already settled**. Fixed: both JSON-parse sites in `_resolve_registration_file` now reject
  non-dict results as `registration_not_an_object` before returning `"ok": True`. Also added the reputation
  gameability caveat (see "Known limitations" above and the `reputation` field's own schema description).
  Two low/nice-to-have items (duplicate-`name` endpoint shadowing, unverified field casing on 2 optional
  registration keys) left as-is -- no evidence yet either matters for real registrations, consistent with
  CLAUDE.md SS3.
- Verified end-to-end against real production data before AND after fixes: 4 real ERC-8004 agent IDs on Base
  Sepolia (1, 2, 3, and a real nonexistent 999999) each produced the correct verdict --
  `REGISTERED_UNREACHABLE` (real registration, endpoint doesn't answer MCP), `REGISTRATION_FETCH_FAILED` x2
  (a real IPFS gateway timeout, and a real dead `https://` registration URL -- both legitimate, not bugs),
  and `AGENT_NOT_FOUND` (real on-chain revert) -- plus real reputation data (74 feedback entries from 12
  distinct clients on agent 1).

## Measurement (candidate #10, 7-day window)

7-day window from 2026-08-23 (real deploy date) -> decision point 2026-08-30. Source of truth:
`traffic_events`/`revenue_events`/`mcp_call_events` (`asset_name = 'erc8004-agent-liveness'`), not Cloud Run
logs. Day 7: if zero real traffic (filtering crawlers), pause/delete the Cloud Run service
(`gcloud run services delete erc8004-agent-liveness --region us-central1 --project nexus-505016`).
<!-- _NEXUS_ERC8004_CDP_NODE_RPC_RESEARCH_NOTE -->

