Skip to content

Commit 91c2cbf

Browse files
feat: add TOON output format for LLM-efficient data serialization (#30)
* feat: add TOON output format for LLM-efficient data serialization Co-Authored-By: cody.sears@opensea.io <cody.sears@opensea.io> * docs: add TOON format to README output formats section Co-Authored-By: cody.sears@opensea.io <cody.sears@opensea.io> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 1a12f14 commit 91c2cbf

16 files changed

Lines changed: 641 additions & 16 deletions

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ opensea --format table collections stats mfers
8989
| `swaps` | Get swap quotes for token trading |
9090
| `accounts` | Get account details |
9191

92-
Global options: `--api-key`, `--chain` (default: ethereum), `--format` (json/table), `--base-url`
92+
Global options: `--api-key`, `--chain` (default: ethereum), `--format` (json/table/toon), `--base-url`
9393

9494
Full command reference with all options and flags: [docs/cli-reference.md](docs/cli-reference.md)
9595

@@ -137,6 +137,33 @@ Table - human-readable output:
137137
opensea --format table collections list --limit 5
138138
```
139139

140+
TOON - [Token-Oriented Object Notation](https://github.com/toon-format/toon), a compact format that uses ~40% fewer tokens than JSON. Ideal for piping output into LLM / AI agent context windows:
141+
142+
```bash
143+
opensea --format toon tokens trending --limit 5
144+
```
145+
146+
Example TOON output for a list of tokens:
147+
148+
```
149+
tokens[3]{name,symbol,chain,market_cap,price_usd}:
150+
Ethereum,ETH,ethereum,250000000000,2100.50
151+
Bitcoin,BTC,bitcoin,900000000000,48000.00
152+
Solana,SOL,solana,30000000000,95.25
153+
next: abc123
154+
```
155+
156+
TOON collapses uniform arrays of objects into CSV-like tables with a single header row, while nested objects use YAML-like indentation. The encoder follows the [TOON v3.0 spec](https://github.com/toon-format/spec/blob/main/SPEC.md) and is implemented without external dependencies.
157+
158+
TOON is also available programmatically via the SDK:
159+
160+
```typescript
161+
import { formatToon } from "@opensea/cli"
162+
163+
const data = await client.tokens.trending({ limit: 5 })
164+
console.log(formatToon(data))
165+
```
166+
140167
## Exit Codes
141168

142169
- `0` - Success

src/cli.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
swapsCommand,
1212
tokensCommand,
1313
} from "./commands/index.js"
14+
import type { OutputFormat } from "./output.js"
1415
import { parseIntOption } from "./parse.js"
1516

1617
const BANNER = `
@@ -33,7 +34,7 @@ program
3334
.addHelpText("before", BANNER)
3435
.option("--api-key <key>", "OpenSea API key (or set OPENSEA_API_KEY env var)")
3536
.option("--chain <chain>", "Default chain", "ethereum")
36-
.option("--format <format>", "Output format (json or table)", "json")
37+
.option("--format <format>", "Output format (json, table, or toon)", "json")
3738
.option("--base-url <url>", "API base URL")
3839
.option("--timeout <ms>", "Request timeout in milliseconds", "30000")
3940
.option("--verbose", "Log request and response info to stderr")
@@ -64,9 +65,11 @@ function getClient(): OpenSeaClient {
6465
})
6566
}
6667

67-
function getFormat(): "json" | "table" {
68+
function getFormat(): OutputFormat {
6869
const opts = program.opts<{ format: string }>()
69-
return opts.format === "table" ? "table" : "json"
70+
if (opts.format === "table") return "table"
71+
if (opts.format === "toon") return "toon"
72+
return "json"
7073
}
7174

7275
program.addCommand(collectionsCommand(getClient, getFormat))

src/commands/accounts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import type { Account } from "../types/index.js"
56

67
export function accountsCommand(
78
getClient: () => OpenSeaClient,
8-
getFormat: () => "json" | "table",
9+
getFormat: () => OutputFormat,
910
): Command {
1011
const cmd = new Command("accounts").description("Query accounts")
1112

src/commands/collections.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import type {
@@ -12,7 +13,7 @@ import type {
1213

1314
export function collectionsCommand(
1415
getClient: () => OpenSeaClient,
15-
getFormat: () => "json" | "table",
16+
getFormat: () => OutputFormat,
1617
): Command {
1718
const cmd = new Command("collections").description(
1819
"Manage and query NFT collections",

src/commands/events.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import type { AssetEvent } from "../types/index.js"
67

78
export function eventsCommand(
89
getClient: () => OpenSeaClient,
9-
getFormat: () => "json" | "table",
10+
getFormat: () => OutputFormat,
1011
): Command {
1112
const cmd = new Command("events").description("Query marketplace events")
1213

src/commands/listings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import type { Listing } from "../types/index.js"
67

78
export function listingsCommand(
89
getClient: () => OpenSeaClient,
9-
getFormat: () => "json" | "table",
10+
getFormat: () => OutputFormat,
1011
): Command {
1112
const cmd = new Command("listings").description("Query NFT listings")
1213

src/commands/nfts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import type { Contract, NFT } from "../types/index.js"
67

78
export function nftsCommand(
89
getClient: () => OpenSeaClient,
9-
getFormat: () => "json" | "table",
10+
getFormat: () => OutputFormat,
1011
): Command {
1112
const cmd = new Command("nfts").description("Query NFTs")
1213

src/commands/offers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import type { Offer } from "../types/index.js"
67

78
export function offersCommand(
89
getClient: () => OpenSeaClient,
9-
getFormat: () => "json" | "table",
10+
getFormat: () => OutputFormat,
1011
): Command {
1112
const cmd = new Command("offers").description("Query NFT offers")
1213

src/commands/search.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseIntOption } from "../parse.js"
56
import {
@@ -17,7 +18,7 @@ import type {
1718

1819
export function searchCommand(
1920
getClient: () => OpenSeaClient,
20-
getFormat: () => "json" | "table",
21+
getFormat: () => OutputFormat,
2122
): Command {
2223
const cmd = new Command("search").description(
2324
"Search for collections, NFTs, tokens, and accounts",

src/commands/swaps.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Command } from "commander"
22
import type { OpenSeaClient } from "../client.js"
3+
import type { OutputFormat } from "../output.js"
34
import { formatOutput } from "../output.js"
45
import { parseFloatOption } from "../parse.js"
56
import type { SwapQuoteResponse } from "../types/index.js"
67

78
export function swapsCommand(
89
getClient: () => OpenSeaClient,
9-
getFormat: () => "json" | "table",
10+
getFormat: () => OutputFormat,
1011
): Command {
1112
const cmd = new Command("swaps").description(
1213
"Get swap quotes for token trading",

0 commit comments

Comments
 (0)