Shelf Protocol
robots.txt for commerce. The open directory AI agents query before they buy.
Merchants publish a tiny shelf.json describing what they sell and what agents
are allowed to do. Agents make one lookup before transacting. Shelf Protocol is the
index in the middle — the handshake layer between agents and the commercial web.
agent ──lookup──▶ Shelf Protocol Registry ◀──publish── merchant
"can I buy here, and how?" "here's my shelf.json"
Why it self-adopts (no sales motion)
- Merchants publish to avoid being invisible to AI shoppers. FOMO, not a sales call.
- Developers add one lookup because it's cheaper and safer than scraping. One line, spreads by copy-paste.
- The index compounds: more merchants → agents prefer it → more developers depend on it → more merchants join.
What's in this repo
| Path | What it is |
|---|---|
spec/SPEC.md |
The open standard. The shelf.json format + verification. |
spec/shelf.json.example |
A sample merchant file. |
server/ |
The registry API (FastAPI + SQLite). Register, lookup, search, verify, stats. |
sdk/shelfprotocol/ |
The one-line lookup client a developer drops into an agent, plus the MCP server. Pip-installable (pyproject.toml at repo root). |
demo/demo_agent.py |
A shopping agent that uses Shelf Protocol to decide what it's allowed to buy. |
web/index.html |
Developer landing page. |
tests/ |
Test suites (cd tests && for t in test_*.py; do python3 $t; done). |
Run it locally (90 seconds)
cd shelfprotocol
pip install -r server/requirements.txt
# 1. start the registry
python -m uvicorn server.main:app --port 8080
# interactive API docs at http://localhost:8080/docs
# 2. in another terminal, seed sample merchants
python -m server.seed
# 3. run the demo agent against it
SHELF_URL=http://localhost:8080 python demo/demo_agent.py
You'll watch an agent query Shelf Protocol, get back verified merchants, and either buy autonomously (within the merchant's declared limit) or escalate to a human.
Note: SQLite stores its file next to
server/db.pyby default. If you run on a network/mounted drive that errors withdisk I/O error, set a local path:export SHELF_DB=/tmp/shelf.sqlite.
Register a merchant
POST /v1/merchants with a shelf.json body (see spec/shelf.json.example for the full schema):
curl -X POST http://localhost:8080/v1/merchants \
-H "Content-Type: application/json" \
-d '{
"merchant": {
"name": "Acme Coffee Co.",
"domain": "acme-coffee.example",
"categories": ["food.beverages.coffee"]
},
"agent_policy": {
"agents_allowed": true,
"max_autonomous_order_usd": 250
},
"checkout": {
"protocol": "AP2",
"endpoint": "https://acme-coffee.example/agent/checkout"
},
"catalog": {
"feed_url": "https://acme-coffee.example/.well-known/shelf-catalog.json"
}
}'
The response includes an api_key (save it — shown only once) and a verification_dns_record to add to your DNS. Once the TXT record is live, confirm ownership with:
export SHELF_API_KEY=osk_... # the api_key from the registration response
curl -X POST http://localhost:8080/v1/merchants/acme-coffee.example/verify \
-H "X-Api-Key: $SHELF_API_KEY"
To change your listing later (limits, checkout endpoint, feed URL — anything
except the domain itself), send the same body to PUT /v1/merchants/<domain>
with your X-Api-Key header. Verification status survives updates.
PUT is a full replace of the merchant-declared fields, not a partial patch —
first GET /v1/merchants/<domain> to see your current listing, edit the
field(s) you want to change, then PUT the whole thing back. This matters
most if you're editing a listing you didn't originally register yourself (see
Seeding & claiming below) — you won't know its current checkout/catalog
values otherwise, and a PUT that omits them clears them.
The registry looks up _shelfprotocol.<domain> in DNS and confirms the TXT record
carries your verification token. On success it flips trust.verified_domain to
true, which is required before can_buy() will allow an agent to purchase
autonomously (see below).
Local demo:
.exampledomains can never resolve in real DNS. Start the server withSHELF_DNS_CHECK=offto skip the TXT lookup (the api_key check still applies).
Rate limits
The registry rate-limits per client IP on /v1/*: 120 reads/min (lookup,
search, stats) and 10 writes/min (register, verify). Exceeding a limit
returns 429 with a Retry-After header. Tune with
SHELF_RATE_LIMIT_READS_PER_MIN / SHELF_RATE_LIMIT_WRITES_PER_MIN,
or disable for local demos and tests with SHELF_RATE_LIMIT=off.
Seeding & claiming (solving the empty registry)
The registry can pre-index stores from their public product feeds:
python -m server.importer domains.txt # one store domain per line
Imported listings are unclaimed: discoverable in search, but inert —
verified_domain: false and a $0 autonomous ceiling, so can_buy() refuses
them. A merchant takes ownership of its listing with:
curl -X POST http://localhost:8080/v1/merchants/acme-coffee.example/claim
That returns an api_key and a DNS record; the claim completes when the
/verify DNS check passes. Until then the listing can't be edited, so a
claimant who doesn't control the domain's DNS can never control its listing.
Catalog feeds (products, not just merchants)
Publishing a catalog is two steps: declare a catalog.feed_url when you
register (as in the example above), and host a shelf-catalog.json file at
that URL (copy spec/shelf-catalog.json.example as a starting point). Then
ask the registry to crawl it:
curl -X POST http://localhost:8080/v1/merchants/acme-coffee.example/catalog/refresh \
-H "X-Api-Key: $SHELF_API_KEY"
The fetch is guarded (HTTPS to a public host only, no redirects, 5s timeout,
1MB / 1000-item caps; SHELF_CATALOG_FETCH_GUARD=off relaxes the
scheme/IP checks for local demos). Agents then query the cache:
from shelfprotocol import catalog, products
catalog("acme-coffee.example", q="decaf") # one merchant's items
products(q="espresso", verified=True) # across all merchants, verified first
The one line developers add
pip install shelfprotocol
from shelfprotocol import lookup, can_buy
profile = lookup("acme-coffee.example")
ok, why = can_buy(profile, amount_usd=40) # honors the merchant's declared limits
Defaults to the hosted registry at api.shelfprotocol.com; point at a
self-hosted one with SHELF_URL.
Use it from any MCP client
pip install "shelfprotocol[mcp]"
{
"mcpServers": {
"shelfprotocol": { "command": "shelfprotocol-mcp" }
}
}
Exposes lookup, search, can_buy, catalog, and products as tools —
add this to Claude Desktop, Claude Code, or any other MCP client and it can
check the registry before buying anything, without writing any code.
The MCP tool's can_buy(domain, amount_usd) is stricter than the raw SDK's
can_buy(profile, amount_usd, require_verified=True): it has no
require_verified argument at all, so a manipulated prompt can never talk
an agent into skipping domain verification through this tool. Code you write
yourself can still opt out deliberately with the SDK function directly.
How it makes money
- Free tier — registration and lookups are free. This is how you get scale.
- Verified listings — merchants pay for a verified badge that lets agents buy from them autonomously (higher conversion). This is the first revenue.
- Metered API — charge per lookup/search above a free threshold, the way Stripe/Twilio/AWS meter usage. Grows automatically with agent traffic.
- Enterprise — priority placement, richer data fields, private catalogs, reputation data feeds for big retailers and payment networks.
No step requires a sales team. Revenue scales with the number of agents in the world, which is the bet.
The roadmap that turns this into a moat
- v0 (this repo): the spec + registry + SDK + demo. Prove the loop.
- v1: hosted API. (Real DNS-TXT verification, rate limiting, product-catalog feeds: done.)
- v2: a reputation layer — log transaction outcomes, score merchants. Once agents check reputation before buying, the data itself becomes the moat.
- v3: become the default lookup baked into agent frameworks (LangChain, CrewAI, AutoGen). One integration seeds thousands of silent adoptions.
Honest risk
If Google, Anthropic, or Shopify ships their own version and bakes it into their platform, the window narrows. Speed and openness (so it feels like a neutral standard, not a vendor product) are the defenses. Move fast; publish the spec publicly; get into a framework template early.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shelfprotocol-0.1.0.tar.gz.
File metadata
- Download URL: shelfprotocol-0.1.0.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4472308d7a1759bd51df0d5b93ba168be13cdfd436c53e271888b331ecf474b
|
|
| MD5 |
875044c871b38cc647e851b6937948a9
|
|
| BLAKE2b-256 |
599e0f5a1ae54e78b169d17755fb3031a5d22317923e3afc8e30580a9da64a94
|
File details
Details for the file shelfprotocol-0.1.0-py3-none-any.whl.
File metadata
- Download URL: shelfprotocol-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45d955b91aaa15c38348d7dc72b6252c6495aeed13e3d8b480ab449a52188e89
|
|
| MD5 |
fc85fda79d67eef929b4b8fa699b9bba
|
|
| BLAKE2b-256 |
20826906dbb0a4c6ef7fa72edfb53bb3207f5f6518a6551adbfa11917a9fd196
|