Skip to content

Commit 6a76e1d

Browse files
CodySearsOSryanio
andcommitted
Release v0.25.0
Origin-SHA: 979e4028929ccbcd7847c1b91219e74a69a60b77 Co-authored-by: Ryan Ghods <ryan@ryanio.com>
1 parent 1746b19 commit 6a76e1d

7 files changed

Lines changed: 97 additions & 9 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pnpm run type-check # TypeScript type checking
1919
| Path | Role |
2020
|------|------|
2121
| `src/index.ts` | Library entry point — public `tool-sdk` exports |
22-
| `src/cli.ts` | CLI entry point (Commander program wiring) |
22+
| `src/cli.ts` | CLI bin entry — imports `program` and calls `program.parse()` |
23+
| `src/cli/index.ts` | Commander program wiring — builds `program` and registers all commands |
2324
| `src/types.ts` | Shared public types |
2425
| `src/cli/commands/` | CLI commands: `auth`, `configure-erc20-gate`, `configure-subscription`, `configure-trait-gating`, `deploy`, `dry-run-gate`, `dry-run-predicate-gate`, `export`, `get-collections`, `get-erc20-config`, `get-trait-config`, `hash`, `init`, `inspect`, `pay`, `register`, `set-collection-tokens`, `set-collections`, `smoke`, `update-metadata`, `validate`, `verify` |
2526
| `src/lib/onchain/abis.ts` | TypeScript ABI definitions mirroring Solidity interfaces |

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @opensea/tool-sdk
22

3+
## 0.25.0
4+
5+
### Minor Changes
6+
7+
- 5deb6c1: The `register` command now validates that the manifest URL and declared endpoint share the same origin before sending the onchain transaction. Previously only `verify` and `deploy` checked this, so a developer could burn gas registering a tool the indexer would immediately reject. All three commands (`register`, `verify`, `deploy`) now show a consistent, actionable error message that explicitly calls out subdomains as different origins and explains how to fix it.
8+
39
## 0.24.0
410

511
### Minor Changes

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensea/tool-sdk",
3-
"version": "0.24.0",
3+
"version": "0.25.0",
44
"type": "module",
55
"description": "SDK and CLI for building ERC-8257 compliant AI agent tools",
66
"repository": {

skill/SKILL.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,25 @@ This SDK is for tool *providers and consumers*. To query OpenSea marketplace dat
4848

4949
| Term | Meaning |
5050
|------|---------|
51-
| **Tool** | An HTTPS endpoint with a JSON Schema interface, discoverable via `/.well-known/ai-tool/<slug>.json` |
51+
| **Tool** | A single REST API endpoint with a JSON Schema interface, discoverable via `/.well-known/ai-tool/<slug>.json`. Each tool should perform one focused operation. |
5252
| **Manifest** | JCS-canonicalized JSON describing the tool's name, endpoint, inputs, outputs, pricing, and access policy |
5353
| **ToolRegistry** | Onchain contract (Base) where tools are registered with a manifest hash and optional access predicate |
5454
| **Access Predicate** | An `IAccessPredicate` contract that gates who can invoke a tool (NFT ownership, subscriptions, trait gating, ERC-20 balance, composites) |
5555
| **x402** | HTTP 402-based pay-per-call protocol (caller signs a USDC `TransferWithAuthorization`; server settles after execution) |
5656
| **EIP-3009 auth** | Zero-value USDC `TransferWithAuthorization` signature used to authenticate callers for predicate-gated tools |
5757
| **Facilitator** | Third-party service that verifies and settles x402 payments (PayAI or Coinbase CDP) |
5858

59+
## Important Constraints
60+
61+
**One tool = one endpoint.** Each tool registration represents a single REST API endpoint with a singular focus and intention. The endpoint URL in your manifest should point to a specific API route that performs one well-defined operation (e.g., `POST /api/price-check` or `POST /api/translate`), not a generic HTML page, documentation site, or multi-purpose URL. Think of each registered tool the same way you think of an individual REST API endpoint. It should accept a specific input, perform a specific action, and return a specific output.
62+
63+
**Origin binding: manifest and endpoint must share the exact same origin.** Your `.well-known/ai-tool/<slug>.json` manifest and your tool's invocation endpoint must be served from the exact same origin (identical scheme, host, and port per [RFC 6454](https://datatracker.ietf.org/doc/html/rfc6454)). **Subdomains do not count as the same origin.** `example.com` and `api.example.com` are different origins. This is enforced at registration time. If the manifest origin and endpoint origin don't match exactly, the tool will be rejected and marked as deregistered.
64+
65+
- Valid: manifest at `https://my-tool.example.com/.well-known/ai-tool/my-tool.json`, endpoint at `https://my-tool.example.com/api/my-tool`
66+
- Invalid: manifest at `https://example.com/.well-known/ai-tool/my-tool.json`, endpoint at `https://api.example.com/api/my-tool`
67+
68+
If you need your API on a subdomain, serve the manifest from that same subdomain (e.g., both on `api.example.com`).
69+
5970
## Deployed Contracts (Ethereum mainnet, Base, Shape, Abstract)
6071

6172
Canonical v0.2 deployments — identical CREATE2 address on every supported chain.

src/cli/commands/deploy.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,29 @@ async function deployToVercel(options: DeployOptions): Promise<void> {
500500
const manifestOrigin = new URL(manifestUrl).origin
501501
const endpointOrigin = new URL(manifest.endpoint).origin
502502
if (manifestOrigin !== endpointOrigin) {
503-
console.error(pc.red("Error: Origin mismatch (anti-impersonation check)"))
504-
console.error(pc.dim(` Manifest served from: ${manifestOrigin}`))
505-
console.error(pc.dim(` Endpoint origin: ${endpointOrigin}`))
503+
console.error(
504+
pc.red(
505+
"Error: Origin mismatch between manifest URL and declared endpoint",
506+
),
507+
)
508+
console.error(pc.dim(` Manifest origin: ${manifestOrigin}`))
509+
console.error(pc.dim(` Endpoint origin: ${endpointOrigin}`))
510+
console.error()
511+
console.error(
512+
pc.yellow(
513+
" The manifest and endpoint must be served from the exact same origin (scheme + host + port).",
514+
),
515+
)
516+
console.error(
517+
pc.yellow(
518+
" Subdomains count as different origins (e.g. example.com ≠ api.example.com).",
519+
),
520+
)
521+
console.error(
522+
pc.yellow(
523+
" Move your manifest or endpoint so both share the same host, then retry.",
524+
),
525+
)
506526
process.exit(1)
507527
}
508528

src/cli/commands/register.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,36 @@ export const registerCommand = new Command("register")
209209
warnBareExtensionKeys(data)
210210

211211
const manifest = result.data
212+
213+
const manifestOrigin = new URL(options.metadata).origin
214+
const endpointOrigin = new URL(manifest.endpoint).origin
215+
if (manifestOrigin !== endpointOrigin) {
216+
console.error(
217+
pc.red(
218+
"Error: Origin mismatch between manifest URL and declared endpoint",
219+
),
220+
)
221+
console.error(pc.dim(` Manifest origin: ${manifestOrigin}`))
222+
console.error(pc.dim(` Endpoint origin: ${endpointOrigin}`))
223+
console.error()
224+
console.error(
225+
pc.yellow(
226+
" The manifest and endpoint must be served from the exact same origin (scheme + host + port).",
227+
),
228+
)
229+
console.error(
230+
pc.yellow(
231+
" Subdomains count as different origins (e.g. example.com ≠ api.example.com).",
232+
),
233+
)
234+
console.error(
235+
pc.yellow(
236+
" Move your manifest or endpoint so both share the same host, then retry.",
237+
),
238+
)
239+
process.exit(1)
240+
}
241+
212242
// Hash the manifest as served (ERC-8257 §2): the full fetched document,
213243
// not the schema-stripped copy.
214244
const hash = computeManifestHash(data as object)

src/cli/commands/verify.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,29 @@ export const verifyCommand = new Command("verify")
5858
const manifestOrigin = new URL(url).origin
5959
const endpointOrigin = new URL(manifest.endpoint).origin
6060
if (manifestOrigin !== endpointOrigin) {
61-
console.error(pc.red("Error: Origin mismatch (anti-impersonation check)"))
62-
console.error(pc.dim(` Manifest served from: ${manifestOrigin}`))
63-
console.error(pc.dim(` Endpoint origin: ${endpointOrigin}`))
61+
console.error(
62+
pc.red(
63+
"Error: Origin mismatch between manifest URL and declared endpoint",
64+
),
65+
)
66+
console.error(pc.dim(` Manifest origin: ${manifestOrigin}`))
67+
console.error(pc.dim(` Endpoint origin: ${endpointOrigin}`))
68+
console.error()
69+
console.error(
70+
pc.yellow(
71+
" The manifest and endpoint must be served from the exact same origin (scheme + host + port).",
72+
),
73+
)
74+
console.error(
75+
pc.yellow(
76+
" Subdomains count as different origins (e.g. example.com ≠ api.example.com).",
77+
),
78+
)
79+
console.error(
80+
pc.yellow(
81+
" Move your manifest or endpoint so both share the same host, then retry.",
82+
),
83+
)
6484
process.exit(1)
6585
}
6686

0 commit comments

Comments
 (0)