eip-xxxx-tool-registry.md
Read draft →
---
eip: XXXX
title: Agent Tool Registry
description: Minimal onchain registry for AI agent tools with extensible predicate-based access control
status: Pre-Draft
type: Standards Track
category: ERC
created: 2026-04-17
requires: 165
---
ERC Pre-Draft · Open for Feedback

Publish a tool, gate it by anything onchain,
get paid.

ERC-XXXX is an onchain registry where anyone can publish an AI agent tool, attach an access predicate (NFT, subscription, allowlist, ZK proof, anything callable), and accept payment via x402 or any other protocol. No auth servers. No gatekeepers. Agents discover and verify in two RPC calls.

The 403 to x402’s 402.

An access-denied layer that complements pay-per-call protocols like x402.

Contracts
tool-registry →
SDK
tool-sdk →
Status
● Pre-Draft
Live on Base
Try the registry
Reference deployment of the v0.1 contracts. Read it from any RPC, no signup, no key.
0x7291BbFbC368C2D478eCe1eA30de31F612a34856
cast
# read total tools cast call 0x7291BbFbC368C2D478eCe1eA30de31F612a34856 \ "toolCount()(uint256)" \ --rpc-url https://mainnet.base.org # read tool #1 cast call 0x7291BbFbC368C2D478eCe1eA30de31F612a34856 \ "getToolConfig(uint256)((address,string,bytes32,address))" 1 \ --rpc-url https://mainnet.base.org
§01 · Why now

Trust and payment
got separated.

Tool calling for agents has three layers: discovery, access, and payment. MCP solves discovery and invocation but has no native trust or payment layer. x402 solves payment but says nothing about who is allowed to call. Ad-hoc API keys solve neither discoverably and tie identity to a single operator. ERC-XXXX is the missing piece: a permissionless directory with onchain identity and pluggable access control. Use it next to MCP and x402, not in place of them.

MCP
Discovery + invocation protocol
No trust anchor. No payment layer. No onchain identity.
Compose: ERC-XXXX as the registry, MCP as the wire.
x402
HTTP 402 micropayments
Payment without access control. Anyone who pays gets in.
Compose: predicate decides who can pay; x402 collects.
API keys
Operator-issued bearer tokens
Per-operator silos. No discovery. No composability.
Replace: predicates gate by onchain state, no key vault.
§02 · Architecture

From publisher to agent,
verifiably.

Publishers commit a manifest hash onchain and serve the manifest at a well-known path. The registry enforces origin binding. Agents discover, verify, and, if the predicate allows, invoke.

PUBLISHER
Tool operator
serves /.well-known, calls registerTool
REGISTRY · ONCHAIN
ERC-XXXX
creator · metadataURI · manifestHash · accessPredicate
AGENT
Consumer
fetches manifest, checks hash, calls hasAccess, invokes
What’s in the registry
#01
nft-appraisal-tool.vercel.app
0xa1f3...
NFT
#02
wallet-personality-tool.vercel.app
0x73bc...
NFT
#03
signal.xyz
0xc28e...
SUB
#04
vault.finance
0x4f19...
PAY
Each row is one ToolConfig: creator, metadataURI, manifestHash, accessPredicate.
§03 · Walkthrough

One tool,
end to end.

An NFT appraisal tool gated by holding a Chonk on Base. Three moves: register, gate, invoke.

01 · Publisher
Register with an NFT-gate predicate
Manifest is served at the well-known path; the SDK computes the JCS hash and submits the registration tx.
bash
npx @opensea/tool-sdk register \ --metadata https://nft-appraisal-tool.vercel.app/.well-known/ai-tool/nft-appraiser.json \ --network base \ --nft-gate 0x8...Chonks # → ToolRegistered(toolId: 42, accessPredicate: ERC721OwnerPredicate)
02 · Agent
Discover, verify, check access
Read the registry entry, validate the manifest (hash, origin, creator), then a single staticcall to the predicate. Fail-closed.
ts
const cfg = await registry.getToolConfig(42n); const manifest = await validateManifest(cfg); // hash + origin + creator const { granted } = await checkToolAccess({ toolId: 42n, account, chain: base }); // granted: false → ask the predicate what it wants const { requirements } = await predicate.getRequirements(42n); // requirements[0].kind === IERC721Holding(0x8...Chonks)
03 · Agent
Acquire, then invoke
Decode the requirement kind, satisfy it (buy, mint, subscribe, prove), re-check, then hit the endpoint with a SIWE header.
ts
// satisfy requirements[0] via your marketplace / mint / subscription of choice const retry = await checkToolAccess({ toolId: 42n, account, chain: base }); // { granted: true } const res = await authenticatedFetch(`${manifest.endpoint}/api/tool`, { body: JSON.stringify({ chain: 'base', contractAddress: '0x...Chonks', tokenId: '1234' }), signer: agentWallet, });
§04 · Trust Tiers

Three tiers.
One manifest field.

Tools declare a verifiability tier in their manifest. Each tier is a different trust model, from operator self-attestation up to fully reproducible end-to-end verifiable execution.

Why three? Progressive verifiability. Most operators ship at self-attested on day one and can graduate to TEE or reproducible builds without changing the registry, the manifest schema, or their consumers’ verification code.

self-attested

Operator's word.

The operator declares data retention and source visibility policies, committed onchain by manifest hash. Cannot be silently changed, but enforcement is reputational.

  • dataRetention
  • sourceVisibility
  • hash-committed
hardware-attested

TEE attestation.

The tool runs in a hardware enclave (SGX, Nitro, SEV-SNP). Agents fetch a fresh attestation report and verify the cryptographic chain of trust. Transparency logs prevent split-view attacks.

  • TEE attestation
  • maxAge freshness
  • transparency logs
verifiable

No trust required.

Source code is published with reproducible build instructions. Anyone can rebuild the enclave binary, compare the measurement against the attestation report, and verify end to end.

  • reproducible build
  • source -> binary -> enclave
  • full chain verification
§05 · Composability

Anyone can write a predicate.

The predicate is just an address. This is the same pattern as Seaport zones and Uniswap v4 hooks: a pluggable contract the protocol staticcalls into without caring about the implementation. The registry never changes; the policy space is open-ended.

ERC721OwnerPredicate.sol
// minimal IAccessPredicate: gate by holding any NFT in a collection contract NFTGate is IAccessPredicate { mapping(uint256 => address) public collectionFor; function setCollection(uint256 toolId, address c) external { require(REGISTRY.getToolConfig(toolId).creator == msg.sender); collectionFor[toolId] = c; } function hasAccess(uint256 toolId, address a, bytes calldata) external view returns (bool) { return IERC721(collectionFor[toolId]).balanceOf(a) > 0; } function getRequirements(uint256 toolId) external view returns (AccessRequirement[] memory r, RequirementLogic l) { r = new AccessRequirement[](1); r[0] = AccessRequirement({ kind: type(IERC721Holding).interfaceId, data: abi.encode(collectionFor[toolId]), label: "" }); l = RequirementLogic.OR; } }
The pattern
Deploy a contract implementing IAccessPredicate. Point any tool’s accessPredicate at its address. Done. No allowlist, no governance, no version bump.
Three required functions
  • hasAccess — yes / no
  • getRequirements — what would unlock it
  • name — diagnostic identity
NFT gate
balanceOf(collection) > 0
Subscription
ERC-5643 time-bound tier
Composite
AND/OR over leaf predicates
ZK proof
verifyProof(witness)
Allowlist
merkleProof(leaf, root)
Stake-weighted
staked(token) >= threshold
DAO vote
proposal.passed(toolId)
Your idea
implement IAccessPredicate
Feedback wanted

It’s pre-draft. Best time to push back.

The spec is unfinished on purpose. The questions where outside input changes the outcome the most right now:

  • 01Predicate ideas we haven’t drafted yet (reputation, attestation, oracle).
  • 02Manifest schema gaps: fields you’d need that aren’t there.
  • 03Failure modes we should spec around (predicate rugs, manifest drift, indexer lag).
ERC-XXXX · Pre-Draft · Standards Track · CC0-1.0