CtrlK
BlogDocsLog inGet started
Tessl Logo

opensea-tool-sdk

Build, register, and gate AI-callable tool endpoints using the OpenSea Tool Registry (ERC-8257) on Base. Scaffold HTTPS tools with JSON Schema interfaces, register them onchain, gate access via NFT ownership or x402 pay-per-call (USDC), and call gated tools. For querying OpenSea marketplace data use opensea-api instead.

76

Quality

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

SKILL.md
Quality
Evals
Security

OpenSea Tool SDK

Build, register, and gate AI-callable tool endpoints using the OpenSea Tool Registry (ERC-8257) on Base.

When to use this skill (scope_in)

Use opensea-tool-sdk when you need to:

  • Scaffold an AI-callable tool endpoint (HTTPS, JSON Schema, .well-known manifest) for Vercel, Cloudflare, or Express
  • Register a tool onchain on the Base ToolRegistry so other agents can discover it
  • Gate access via x402 pay-per-call (USDC) or predicates (ERC-721/ERC-1155 ownership, subscriptions, composites)
  • Call a gated tool: SIWE auth (authenticatedFetch), 402 payments (paidFetch), or both (paidAuthenticatedFetch)

When NOT to use this skill (scope_out, handoff)

NeedUse instead
Query NFT/token data, search, collection statsopensea-api
Buy/sell NFTsopensea-marketplace
Swap ERC20 tokensopensea-swaps
Set up wallet signing providersopensea-wallet

This SDK is for tool providers and consumers. To query OpenSea marketplace data (floor prices, listings, trades), use the opensea-api skill instead.

Concepts

TermMeaning
ToolAn HTTPS endpoint with a JSON Schema interface, discoverable via /.well-known/ai-tool/<slug>.json
ManifestJCS-canonicalized JSON describing the tool's name, endpoint, inputs, outputs, pricing, and access policy
ToolRegistryOnchain contract (Base) where tools are registered with a manifest hash and optional access predicate
Access PredicateAn IAccessPredicate contract that gates who can invoke a tool (NFT ownership, subscriptions, composites)
x402HTTP 402-based pay-per-call protocol (caller signs a USDC TransferWithAuthorization; server settles after execution)
SIWESign-In with Ethereum (EIP-4361), used to authenticate callers for predicate-gated tools
FacilitatorThird-party service that verifies and settles x402 payments (PayAI or Coinbase CDP)

Deployed Contracts (Ethereum mainnet + Base)

Canonical v0.2 deployments — identical CREATE2 address on both chains.

ContractAddress
ToolRegistry (v0.2)0x265BB2DBFC0A8165C9A1941Eb1372F349baD2cf1
ERC721OwnerPredicate (v0.2)0xc8721c9A776958FfFfEb602DA1b708bf1D318379
ERC1155OwnerPredicate (v0.2)0x77373Dc3c1AE9A1e937eF3e5E08F4807D47c7c11

1. Create a Tool

1a. Scaffold a project

npx @opensea/tool-sdk init --runtime vercel   # or: cloudflare, express

This generates:

  • src/manifest.ts: tool manifest definition
  • src/handler.ts: request handler with input/output schemas
  • api/index.ts: framework adapter entry point
  • public/llms.txt: agent-readable discovery page
  • api/well-known/[slug].ts: serves the manifest at /.well-known/ai-tool/<slug>.json

1b. Define the manifest

import { defineManifest } from "@opensea/tool-sdk"

export const manifest = defineManifest({
  name: "My Tool",
  description: "What this tool does",
  endpoint: "https://my-tool.example.com/api",
  creatorAddress: "0xYOUR_WALLET_ADDRESS",
  inputs: {
    type: "object",
    properties: {
      query: { type: "string", description: "Search query" },
    },
    required: ["query"],
  },
  outputs: {
    type: "object",
    properties: {
      result: { type: "string" },
    },
  },
  // Optional: add pricing for x402 paywall (see references/x402.md)
  // pricing: paywall.pricing,
  // Optional: add access requirements (see references/predicate-gating.md)
  // access: { logic: "OR", requirements: [...] },
})

1c. Write the handler

import { createToolHandler } from "@opensea/tool-sdk"
import { z } from "zod/v4"
import { manifest } from "./manifest.js"

const InputSchema = z.object({ query: z.string() })
const OutputSchema = z.object({ result: z.string() })

export const toolHandler = createToolHandler({
  manifest,
  inputSchema: InputSchema,
  outputSchema: OutputSchema,
  // gates: [],  // Add gates here (see references/x402.md and references/predicate-gating.md)
  handler: async (input) => {
    return { result: `Processed: ${input.query}` }
  },
})

1d. Wire up the adapter

Vercel:

import { toVercelHandler } from "@opensea/tool-sdk"
import { toolHandler } from "../src/handler.js"
export default toVercelHandler(toolHandler)

Express:

import { toExpressHandler } from "@opensea/tool-sdk"
import { toolHandler } from "./handler.js"
app.post("/api", toExpressHandler(toolHandler))

Cloudflare Workers:

import { toolHandler } from "./handler.js"
export default { fetch: toolHandler }

2. Register a Tool Onchain

2a. Via CLI

# Set up wallet
export PRIVATE_KEY=0x...
export RPC_URL=https://mainnet.base.org

# Register (open access, no predicate)
npx @opensea/tool-sdk register \
  --metadata https://my-tool.example.com/.well-known/ai-tool/my-tool.json \
  --network base

# Register with NFT gate (ERC-721 collection)
npx @opensea/tool-sdk register \
  --metadata https://my-tool.example.com/.well-known/ai-tool/my-tool.json \
  --network base \
  --nft-gate 0xCOLLECTION_ADDRESS

# Register with a custom access predicate
npx @opensea/tool-sdk register \
  --metadata https://my-tool.example.com/.well-known/ai-tool/my-tool.json \
  --network base \
  --access-predicate 0xPREDICATE_ADDRESS

# Dry run (no transaction)
npx @opensea/tool-sdk register --metadata ... --network base --dry-run

The CLI:

  1. Fetches the manifest from --metadata URL
  2. Validates the manifest schema
  3. Verifies manifest.creatorAddress matches your wallet
  4. Computes the JCS keccak256 manifest hash
  5. Calls ToolRegistry.registerTool(metadataURI, manifestHash, accessPredicate)
  6. Returns the toolId from the ToolRegistered event

2b. Via SDK (programmatic)

import { ToolRegistryClient, computeManifestHash } from "@opensea/tool-sdk"
import { createWalletFromEnv, walletAdapterToClient } from "@opensea/tool-sdk"
import { base } from "viem/chains"

const adapter = createWalletFromEnv()
const walletClient = await walletAdapterToClient(adapter, base)

const registry = new ToolRegistryClient({
  chain: base,
  rpcUrl: "https://mainnet.base.org",
  walletClient,
})

const { toolId, txHash } = await registry.registerTool({
  metadataURI: "https://my-tool.example.com/.well-known/ai-tool/my-tool.json",
  manifest,                                      // your ToolManifest object
  accessPredicate: "0x0000...0000",              // address(0) for open access
})

console.log(`Registered tool ${toolId} in tx ${txHash}`)

3. Gating tool access

Tools can be gated three ways:

GateMechanismReference
x402 paywallPay-per-call (USDC, EIP-3009)references/x402.md
Predicate gateOnchain check (NFT, subscription, composite)references/predicate-gating.md
CombinedSIWE auth and payment (predicate first, then x402)references/predicate-gating.md

For deployed predicate addresses, requirement encodings, and SDK helpers like describeToolAccess / decodeRequirement, see references/known-predicates.md.

4. Wallet Setup

The SDK supports multiple wallet providers via @opensea/wallet-adapters. Set environment variables and the SDK auto-detects the provider. See the opensea-wallet skill for the full provider table, env vars, setup walkthroughs, and signing-policy configuration.

import { createWalletFromEnv } from "@opensea/tool-sdk"

const adapter = createWalletFromEnv()
const address = await adapter.getAddress()

For Bankr (external signer):

import { createBankrAccount } from "@opensea/tool-sdk"

const account = await createBankrAccount("your-bankr-api-key")
// Use with authenticatedFetch or paidAuthenticatedFetch

5. Response Codes

CodeMeaningAction
200SuccessParse the JSON body per the manifest's outputs schema
400Invalid inputFix request body to match the manifest's inputs schema
401Missing/invalid SIWE authSign a SIWE message and include Authorization: SIWE <token>
402Payment requiredRead body.accepts[0] for payment requirements, sign and retry with X-Payment
403Access deniedInspect body.predicate to discover what's needed; acquire the required token/subscription
405Method not allowedUse POST
500Internal tool errorRetry or contact the tool creator
502Predicate/facilitator errorThe upstream predicate or payment facilitator misbehaved; retry later

6. Quick Reference: CLI Commands

CommandPurpose
initScaffold a new tool project
validateValidate a manifest file
hashCompute the JCS keccak256 hash of a manifest
exportExport the manifest as JSON
registerRegister a tool onchain
update-metadataUpdate a tool's metadata URI and manifest hash onchain
inspectLook up a tool's onchain config by ID
verifyVerify a manifest against its onchain hash
deployDeploy a tool to Vercel
authCall a predicate-gated tool (SIWE)
payCall an x402-paid tool (USDC)
smokeAuto-detect gate type and call
dry-run-gateSimulate an x402 gate check locally
dry-run-predicate-gateSimulate a predicate gate check locally

All CLI commands accept --wallet-provider privy|turnkey|fireblocks|bankr|private-key or auto-detect from env vars.

7. End-to-End Examples

Example A: Free open-access tool

# 1. Scaffold
npx @opensea/tool-sdk init --runtime vercel
# 2. Edit src/manifest.ts and src/handler.ts with your logic
# 3. Deploy
npx @opensea/tool-sdk deploy
# 4. Register (open access)
PRIVATE_KEY=0x... npx @opensea/tool-sdk register \
  --metadata https://my-tool.vercel.app/.well-known/ai-tool/my-tool.json \
  --network base
# 5. Call
curl -X POST https://my-tool.vercel.app/api \
  -H "Content-Type: application/json" \
  -d '{"query": "hello"}'

Example B: x402 paid tool (pay-per-call only, no identity check)

# Server: add paywall gate (see references/x402.md)
# Call via CLI:
PRIVATE_KEY=0x... npx @opensea/tool-sdk pay \
  https://my-tool.vercel.app/api \
  --body '{"query": "hello"}'

Example C: NFT-gated tool (identity check, no payment)

# Register with NFT gate
PRIVATE_KEY=0x... npx @opensea/tool-sdk register \
  --metadata https://my-tool.vercel.app/.well-known/ai-tool/my-tool.json \
  --network base \
  --nft-gate 0xCOLLECTION

# Server: add predicateGate (see references/predicate-gating.md)

# Call via CLI:
PRIVATE_KEY=0x... RPC_URL=https://mainnet.base.org \
  npx @opensea/tool-sdk auth \
  https://my-tool.vercel.app/api \
  --body '{"query": "hello"}'

Example D: NFT-gated and paid tool (both gates)

# Server: add both predicateGate and paywall.gate (see references/predicate-gating.md)
# Call via CLI:
PRIVATE_KEY=0x... RPC_URL=https://mainnet.base.org \
  npx @opensea/tool-sdk smoke \
  --endpoint https://my-tool.vercel.app/api \
  --expect 200

References

  • references/x402.md: pay-per-call protocol, server-side paywall, paidFetch
  • references/predicate-gating.md: SIWE-based access control, combined gates
  • references/known-predicates.md: deployed predicate contracts and SDK helpers
  • Tool SDK GitHub
Repository
ProjectOpenSea/opensea-skill
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.