Slide deck | Design doc | Quick start
A content marketplace on Base where the entire workflow — listing, discovering, paying, proving license ownership — is six MCP tool calls. No browser. No human in the loop. Any LLM.
A creator's agent lists content with two license tiers — Personal (display/print) and Commercial (full rights) — each with its own price and onchain terms. A buyer's agent searches, scores, picks a tier, and purchases in a single atomic transaction. Creator gets 97.5%. Platform takes 2.5%. Buyer gets an ERC-721 license NFT with the tier recorded. Point Claude Code at the MCP server and it trades content the same way it runs shell commands.
flowchart LR
subgraph world["External World"]
CC["Claude Code"]
CD["Claude Desktop"]
SDK["Agent SDK / Custom"]
end
CC & CD & SDK ==>|"MCP protocol"| MCP["ACL MCP Server<br>6 tools"]
MCP -->|"register_content"| IPFS["IPFS + SHA-256"]
IPFS -->|"content URI"| CR["ContentRegistry<br>(Base)"]
MCP -->|"search_content"| CR
CR -->|"listings + tier prices"| MCP
MCP -->|"purchase_license(tier)"| CAP{{"spend < cap?"}}
CAP -->|"✓"| CR
CR -->|"atomic split"| PAY["97.5% → Creator<br>2.5% → Platform"]
CR -->|"mint"| NFT["LicenseNFT<br>(ERC-721 + tier)"]
MCP -->|"verify_license"| NFT
NFT -->|"true / false"| MCP
| Layer | What it does |
|---|---|
| 2 Solidity contracts | ContentRegistry (marketplace logic, payment splitting, dual license tiers) + LicenseNFT (ERC-721 + ERC-2981 royalties, tier tracking) |
| License tiers | Every listing has a Personal and Commercial tier with independent prices and onchain terms — buyers choose their level of rights |
| MCP server | 6 tool calls that give any LLM native marketplace access — bounded by MCP_MAX_SPEND_ETH so humans stay in control |
| AI agent personas | Built-in /sell, /buy, /browse, /verify slash commands turn Claude Code into a seller or buyer agent — real AI making decisions through MCP, not scripted demos |
| IPFS integration | Decentralized content + metadata storage with SHA-256 integrity verification |
| Web dashboard + REST API | For humans who want to see what's happening onchain |
| 29 Foundry tests | Registration, tier selection, purchases, refunds, payment splitting, royalties, admin controls: all green |
| 100% E2E Verified | Automated test suite verifies full lifecycle: Register → Purchase (Personal) → Purchase (Commercial) → Price Update → Verify Ownership → Deactivate |
ethers.jsNonceManagerhandles high-concurrency agent transactions without nonce collisions- Price selection, license terms, payment splitting, and NFT minting settle in a single atomic Ethereum transaction
- Every agent purchase is bounded by
MCP_MAX_SPEND_ETH— agents cannot drain wallets without human oversight - Full metadata and license terms live on IPFS with SHA-256 content hashes recorded onchain for verification
The marketplace exposed as native LLM tool calls:
| Tool | What happens |
|---|---|
register_content |
Hashes file, uploads to IPFS, registers onchain with personal + commercial prices and separate license terms per tier |
search_content |
Queries onchain registry, returns active listings with both tier prices |
get_content |
Full listing details — both tier prices, creator, IPFS links, license terms |
purchase_license |
Takes a tier param (personal / commercial), pays the tier price, mints license NFT with tier recorded. Bounded by MCP_MAX_SPEND_ETH |
verify_license |
Single onchain call: does this wallet own a license for this content? |
get_licenses |
Every license NFT held by a wallet, including tier |
Any MCP-compatible client — Claude Code, Claude Desktop, or custom — gets full marketplace access out of the box. The .mcp.json ships in the repo root.
Human (Creator) Human (Buyer)
| |
v v
AI Session (Seller) AI Session (Buyer)
| |
|-- hash file (SHA-256) |-- query registry
|-- upload to IPFS |-- score listings (tags, price, type)
|-- build per-tier license terms |-- rank results
|-- registerContent(...) |-- choose tier (personal/commercial)
| |-- purchaseLicense(contentId, tier)
v v
ContentRegistry (Base) Atomic Settlement
| |-- creator gets 97.5%
| |-- platform gets 2.5%
| |-- buyer gets License NFT (tier recorded)
v v
Any agent: verifyLicense(address, contentId) → true
- Human tells their agent to list content — a photo, document, dataset, whatever.
- Seller agent hashes the file, uploads to IPFS, creates separate license terms for Personal and Commercial tiers, registers onchain with both prices.
- Buyer's agent searches the registry, scores listings against criteria, compares tiers, and purchases the best match at the right tier.
- Atomic settlement — payment splits, license NFT mints, tier is recorded onchain. All-or-nothing.
- Onchain verification — any agent, anywhere, one call:
verifyLicense(address, contentId). No trust required.
sequenceDiagram
participant HC as Human (Creator)
participant SA as AI Session (Seller)
participant IPFS
participant CR as ContentRegistry
participant NFT as LicenseNFT
participant BA as AI Session (Buyer)
participant HB as Human (Buyer)
HC->>SA: "List my photo (Personal: 0.001, Commercial: 0.01 ETH)"
SA->>SA: Hash file (SHA-256)
SA->>IPFS: Upload file + metadata + per-tier license terms
IPFS-->>SA: CIDs
SA->>CR: registerContent(hash, metadataURI, personalTerms, commercialTerms, prices)
CR-->>SA: contentId
SA-->>HC: Listed as Content #1
HB->>BA: "Find me landscape photos under 0.005 ETH"
BA->>CR: getActiveContents()
CR-->>BA: listings[]
BA->>BA: Score + rank by tags, price, type
BA-->>HB: Best match: "Sunset Over Mountains" (Personal 0.001 / Commercial 0.01)
HB->>BA: "Buy it (personal tier)"
BA->>CR: purchaseLicense(contentId, Personal) + ETH
CR->>CR: Split payment (creator 97.5% / platform 2.5%)
CR->>NFT: mintLicense(buyer, contentId, tier, price, termsURI)
NFT-->>BA: License NFT #1
BA->>NFT: verifyLicense(buyer, contentId)
NFT-->>BA: true
BA-->>HB: License verified, NFT in your wallet
| Component | Technology |
|---|---|
| Smart contracts | Solidity 0.8.24+, OpenZeppelin 5.x, Foundry |
| Agent interface | Model Context Protocol (MCP) |
| Agent runtime | TypeScript, Node.js, Express, ethers.js v6 |
| File storage | IPFS via local kubo node |
| Frontend | Vanilla HTML/CSS/JS |
| Target chain | Base (local anvil for dev, Sepolia/Mainnet for prod) |
- Node.js v18+ and npm
- Foundry (
anvil,forge) — install guide - IPFS (kubo) — optional, the demo simulates IPFS if not running
node -v && npm -v && anvil --version && forge --version
cd agent && npm install && cd ..Deploys contracts, registers content with dual tiers, purchases a personal license, verifies ownership, checks royalties — all on a local chain in ~5 seconds.
# Terminal 1
anvil
# Terminal 2
cd agent && npm run demoOpen this project in Claude Code. The MCP server connects automatically. Then use the built-in persona commands:
/sell # Become a seller agent — list content with dual tier pricing
/buy # Become a buyer agent — search, evaluate, purchase
/browse # Browse all marketplace listings
/verify # Check license ownership on-chainWhat happens: Claude Code assumes the persona, reasons about your request, and uses the 6 MCP tools to interact with the marketplace. This is a real AI agent making real decisions — not a scripted demo.
Examples:
/sell ~/photos/sunset.jpg --name "Beach Sunset" --personal 0.002 --commercial 0.02
/buy --type landscape --budget 0.005 --tags sunset,nature
/verify 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 1
The seller agent walks you through pricing strategy, uploads to IPFS, and registers on-chain. The buyer agent searches, scores, ranks listings, recommends a tier, and only purchases with your explicit approval.
# Terminal 1: start IPFS (optional)
ipfs daemon
# Terminal 2: start everything
chmod +x dev.sh && ./dev.shOpen frontend/index.html — register content with two price tiers, browse listings, pick a tier and buy, view trades, verify ownership.
Ships with .mcp.json at repo root — Claude Code auto-detects it.
Two wallets required. The contract enforces creators can't buy their own content. Anvil provides pre-funded test accounts.
Spending cap.
MCP_MAX_SPEND_ETH(default 0.1 ETH) bounds everypurchase_licensecall. Human stays in control.
For Claude Desktop or custom setups:
{
"mcpServers": {
"acl": {
"command": "npx",
"args": ["tsx", "src/mcp-server.ts"],
"cwd": "/absolute/path/to/agent",
"env": {
"RPC_URL": "http://127.0.0.1:8545",
"REGISTRY_ADDRESS": "<from dev.sh output>",
"LICENSE_NFT_ADDRESS": "<from dev.sh output>",
"PRIVATE_KEY": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
"MCP_MAX_SPEND_ETH": "0.1"
}
}
}
}Try it:
- "Register my photo sunset.jpg with personal price 0.001 ETH and commercial price 0.01 ETH, tags landscape, sunset."
- "Search for landscape content under 0.005 ETH and purchase the best match with a personal license."
- ContentRegistry.sol — Marketplace registry with dual license tiers. Content registration, tier-aware pricing, deactivation, license purchases with atomic payment splitting (creator + platform fee in basis points).
LicenseTierenum enforces Personal vs. Commercial at the contract level. - LicenseNFT.sol — ERC-721 license tokens with ERC-2981 royalties (5% to original creator on secondary sales). Each license records its tier onchain. Only mintable by ContentRegistry.
Security:
ReentrancyGuard+ checks-effects-interactions on all payment paths- SHA-256 content hashing for onchain integrity verification
.call{value}for ETH transfers (not.transfer())- 29 Foundry tests — all passing
cd contracts && forge test -vvBase URL: http://localhost:3001/api
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Status, signer addresses, IPFS status |
| GET | /stats |
Content count, license count |
| GET | /content |
Paginated active content with IPFS metadata + both tier prices |
| GET | /content/:id |
Single content item + metadata |
| POST | /content |
Register content (multipart upload, personal + commercial prices) |
| PUT | /content/:id/price |
Update tier-specific price |
| DELETE | /content/:id |
Deactivate listing |
| POST | /licenses/purchase |
Purchase a license (with tier selection) |
| GET | /licenses/:tokenId |
License NFT details (includes tier) |
| GET | /licenses/buyer/:address |
All licenses owned by a wallet |
| GET | /verify/:address/:contentId |
Check license ownership |
| GET | /trades |
Historical purchase events |
contracts/ Solidity contracts + Foundry config + 29 tests
agent/ TypeScript backend (Express API + ethers + IPFS)
src/mcp-server.ts MCP server — 6 marketplace tools
src/server.ts Express REST API + IPFS integration
src/demo.ts Quick demo script (deploy + register + purchase + verify)
.claude/skills/ AI agent persona commands (/sell, /buy, /browse, /verify)
frontend/ Static dashboard UI
index.html Browser dashboard with tier picker
dev.sh Local dev bootstrap (anvil + deploy + .env + API)
deck.html Slide deck for judges
.mcp.json MCP server config (auto-detected by Claude Code)
DESIGN.md Full architectural and protocol design document
Ask Claude Code to build a landing page with real stock photography right now. It will stop and ask you to go buy the images. With ACL's MCP server running, it wouldn't have to. That's what this is — agents that source, evaluate, price-compare across license tiers, and purchase licensed content autonomously, atomically, verifiably.
| Property | How ACL delivers |
|---|---|
| Agents that pay | Buyer agent pays onchain via purchase_license, choosing Personal or Commercial tier. Atomic settlement: ETH splits to creator (97.5%) and platform (2.5%), license NFT mints with tier recorded. Human scopes spending via MCP_MAX_SPEND_ETH. Auditable on Base. |
| Agents that trust | One onchain call: verifyLicense(address, contentId). No centralized registry can revoke access. SHA-256 content hashes stored onchain — any agent can verify integrity against IPFS independently. |
| Agents that cooperate | Seller and buyer agents negotiate through immutable contract terms. Pricing, tier selection, payment splitting, license minting — all enforced by ContentRegistry. Succeeds or reverts atomically. No platform rewrites the deal. |
Limitations — No authorship proof (hash uniqueness only, same as every decentralized marketplace). Basic onchain discovery (no full-text search). Single chain (Base). These are scope choices, not oversights.
For the full design rationale, see DESIGN.md.
