api-spec-cli 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,282 +1,390 @@
1
- # api-spec-cli
2
-
3
- CLI for AI agents to explore and call OpenAPI, GraphQL, and MCP APIs. Output is JSON by default — compact, parseable, token-efficient.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install -g api-spec-cli
9
- ```
10
-
11
- Works with Node.js 18+. No other dependencies.
12
-
13
- ```bash
14
- # Or run without installing
15
- npx api-spec-cli <command>
16
- ```
17
-
18
- ## How It Works
19
-
20
- Every command is stateless — you specify the spec source on each call. Two paths:
21
-
22
- | Path | When to use |
23
- |---|---|
24
- | `--spec <name>` | Registered spec — auto-fetches and caches on first use |
25
- | Inline flags | Ad-hoc — no registration, fetched each call |
26
-
27
- ### Register once, use everywhere
28
-
29
- ```bash
30
- spec add petstore --openapi https://petstore3.swagger.io/api/v3/openapi.json \
31
- --base-url https://petstore3.swagger.io/api/v3 \
32
- --description "Petstore example"
33
-
34
- spec add hashnode --graphql https://gql.hashnode.com --auth YOUR_TOKEN
35
-
36
- spec add agno --mcp-http https://docs.agno.com/mcp --description "Agno docs"
37
-
38
- spec add fs --mcp-stdio "npx -y @modelcontextprotocol/server-filesystem /tmp"
39
- ```
40
-
41
- Registration is instant — does not connect. Connection happens on first `list`/`show`/`call` and the result is cached at `~/spec-cli-config/cache/<name>.json`.
42
-
43
- ### Or use inline (no registration)
44
-
45
- ```bash
46
- spec list --openapi https://petstore3.swagger.io/api/v3/openapi.json
47
- spec list --graphql https://gql.hashnode.com
48
- spec list --mcp-http https://docs.agno.com/mcp
49
- spec list --mcp-sse http://localhost:3000/sse
50
- spec list --mcp-stdio "npx -y @modelcontextprotocol/server-filesystem /tmp"
51
- ```
52
-
53
- Inline fetches every call, nothing cached.
54
-
55
- ---
56
-
57
- ## Discovery
58
-
59
- ### Search across specs
60
-
61
- `grep` searches operation/tool names and descriptions across all registered specs.
62
-
63
- ```bash
64
- spec grep search # Substring match across all specs
65
- spec grep "get*" # Glob: anything starting with "get"
66
- spec grep "*list*" # Glob: anything containing "list"
67
- spec grep search --spec agno # Limit to one spec
68
- ```
69
-
70
- Matches on name and description. Case-insensitive. Plain text = substring, `*`/`?` = glob.
71
-
72
- ### List all specs in the registry
73
-
74
- ```bash
75
- spec specs # Compact: name, type, enabled
76
- spec specs --compact false # Full: includes source, config
77
- ```
78
-
79
- ### List operations / tools
80
-
81
- `list` is compact by default — just IDs, no schemas. Use `--filter`, `--tag`, `--limit` to narrow down.
82
-
83
- ```bash
84
- spec list --spec agno # Registered spec (uses cache)
85
- spec list --spec petstore --filter pet # Search by keyword
86
- spec list --spec petstore --tag pets # OpenAPI: filter by tag
87
- spec list --spec hashnode --tag mutation # GraphQL: filter by kind
88
- spec list --spec petstore --limit 10 # First 10 only
89
- spec list --spec petstore --limit 10 --offset 10 # Next 10
90
- spec list --mcp-http https://docs.agno.com/mcp # Inline: no registration needed
91
- ```
92
-
93
- Compact output:
94
- ```json
95
- {
96
- "type": "mcp",
97
- "total": 1,
98
- "showing": 1,
99
- "operations": [
100
- { "id": "search_agno", "description": "Search across the Agno knowledge base..." }
101
- ]
102
- }
103
- ```
104
-
105
- Use `--compact false` for full details including `inputSchema` for MCP tools.
106
-
107
- ### Inspect one operation or tool
108
-
109
- `show` gives you everything to make a call — params, body schema, response schemas, related types — in one call.
110
-
111
- ```bash
112
- spec show --spec petstore getPetById # OpenAPI: by operationId
113
- spec show --spec petstore /pet/{petId} # OpenAPI: by path
114
- spec show --spec petstore "GET /pet/{petId}" # OpenAPI: by method + path
115
- spec show --spec hashnode publishPost # GraphQL: by operation name
116
- spec show --spec agno search_agno # MCP: by tool name
117
- ```
118
-
119
- MCP output includes the full `inputSchema` so you know exactly what arguments to pass.
120
-
121
- ### Drill into types (OpenAPI/GraphQL only)
122
-
123
- ```bash
124
- spec types --spec petstore # List all schema names
125
- spec types --spec petstore Pet # Inspect one schema
126
- spec types --spec hashnode PublishPostInput # GraphQL input type
127
- ```
128
-
129
- ---
130
-
131
- ## Calling APIs
132
-
133
- ```bash
134
- # OpenAPI
135
- spec call --spec petstore getPetById --var petId=1
136
- spec call --spec petstore findPetsByStatus --query status=available
137
- spec call --spec petstore addPet --data '{"name":"Rex","photoUrls":[]}'
138
-
139
- # GraphQL (auto-generates query from schema)
140
- spec call --spec hashnode me
141
- spec call --spec hashnode publication --var host=blog.hashnode.dev
142
-
143
- # MCP
144
- spec call --spec agno search_agno --var query="how to create an agent"
145
- spec call --spec agno search_agno --data '{"query":"agents"}'
146
-
147
- # Read body from stdin (explicit --data -)
148
- echo '{"query":"agents"}' | spec call --spec agno search_agno --data -
149
- cat body.json | spec call --spec petstore addPet --data -
150
-
151
- # Inline (no registration)
152
- spec call --openapi https://petstore3.swagger.io/api/v3/openapi.json \
153
- getPetById --var petId=1 --base-url https://petstore3.swagger.io/api/v3
154
- ```
155
-
156
- ### Per-call overrides
157
-
158
- Flags passed at call time win over registry entry config, which wins over `.spec-cli/config.json`.
159
-
160
- ```bash
161
- spec call --spec agno search_agno --var query="foo" --header X-Tenant=acme
162
- spec call --spec petstore getPetById --var petId=1 --auth staging-token
163
- spec list --spec petstore --base-url https://staging.api.example.com
164
- ```
165
-
166
- ---
167
-
168
- ## Registry Management
169
-
170
- ```bash
171
- spec remove <name> # Delete entry and remove cache
172
- spec enable <name> # Re-enable a disabled spec
173
- spec disable <name> # Disable without removing
174
- spec refresh <name> # Force re-fetch and update cache
175
- ```
176
-
177
- ---
178
-
179
- ## spec add options
180
-
181
- ```bash
182
- spec add <name> --openapi <url-or-file> [--base-url <url>] [--auth <token>] [--header k=v]
183
- spec add <name> --graphql <url> [--auth <token>] [--header k=v]
184
- spec add <name> --mcp-http <url> [--auth <token>] [--header k=v]
185
- spec add <name> --mcp-sse <url> [--auth <token>] [--header k=v]
186
- spec add <name> --mcp-stdio "<cmd args>" [--env KEY=VAL] [--cwd <path>]
187
- [--description <text>] (all types)
188
- ```
189
-
190
- All options are repeatable where it makes sense (`--header`, `--env`). `--auth` adds `Authorization: Bearer <token>` unless the header is already set.
191
-
192
- Operation filtering works for all spec types — MCP, OpenAPI, and GraphQL:
193
-
194
- ```bash
195
- # MCP: allow only read/list tools
196
- spec add <name> --mcp-http <url> \
197
- --allow-tool "read_*" --allow-tool "list_*" \
198
- --disable-tool "delete_*"
199
-
200
- # OpenAPI: allow only GET operations (by operationId)
201
- spec add <name> --openapi <url> \
202
- --allow-tool "get*" --allow-tool "find*"
203
-
204
- # GraphQL: allow specific operations by exact name
205
- spec add <name> --graphql <url> \
206
- --allow-tool "me" --allow-tool "publication"
207
- ```
208
-
209
- `--allow-tool` keeps only matching operations. `--disable-tool` removes matching operations (applied after allow). Both are repeatable.
210
-
211
- **Matching rules:**
212
- - Plain text → **exact match** (case-insensitive): `"me"` matches only `me`
213
- - Glob patterns anchored match: `"get*"` matches `getPetById`, `"*post*"` matches `createPost`
214
-
215
- Use `grep` for search (substring) — `--allow-tool` / `--disable-tool` for precise whitelists (exact or glob).
216
-
217
- ---
218
-
219
- ## Config
220
-
221
- Persistent config stored in `.spec-cli/config.json` (lowest priority — overridden by registry entry config and call-time flags).
222
-
223
- ```bash
224
- spec config set baseUrl https://api.example.com
225
- spec config set auth my-token # Auto-adds "Bearer " prefix
226
- spec config set auth "Basic dXNlcjpwYXNz" # Or explicit scheme
227
- spec config set headers.X-API-Key abc123 # Custom header (dot notation)
228
- spec config get
229
- spec config unset auth
230
- ```
231
-
232
- ## Validate
233
-
234
- ```bash
235
- spec validate https://api.example.com/openapi.json
236
- spec validate ./openapi.yaml
237
- ```
238
-
239
- Reports broken `$ref` references, missing required fields, duplicate operationIds, invalid schema types, and more.
240
-
241
- ## Output Format
242
-
243
- JSON by default. Errors go to stderr as `{"error": "message"}` with a non-zero exit code.
244
-
245
- ```bash
246
- spec list --spec petstore --format text
247
- spec show --spec petstore getPetById --format yaml
248
- spec list --spec petstore --format=json # equals syntax also works
249
- ```
250
-
251
- ## Token Efficiency
252
-
253
- - `list` returns only IDs by default — no schemas
254
- - `show` resolves `$ref` compactly — nested refs show as names, not explosions
255
- - `types` lets you inspect one schema at a time
256
- - `--limit` / `--offset` paginate large APIs
257
- - `--filter` and `--tag` narrow results before output
258
-
259
- ## MCP Options
260
-
261
- ```bash
262
- # Retry on connection failure (useful for stdio servers that take time to start)
263
- MCP_MAX_RETRIES=3 # Attempts (default: 3)
264
- MCP_RETRY_DELAY=1000 # Base delay in ms, doubles each attempt, capped at 5s (default: 1000)
265
-
266
- # HTTP timeout for OpenAPI/GraphQL calls
267
- SPEC_HTTP_TIMEOUT=30000 # ms (default: 30000)
268
- ```
269
-
270
- Stdio env vars support `${VAR}` expansion from the host environment:
271
-
272
- ```bash
273
- spec add fs --mcp-stdio "npx -y server /tmp" --env "TOKEN=${MY_SECRET}"
274
- ```
275
-
276
- ## Storage
277
-
278
- | Path | Purpose |
279
- |---|---|
280
- | `~/spec-cli-config/registry.json` | Global named registry |
281
- | `~/spec-cli-config/cache/<name>.json` | Cached spec per registered entry |
282
- | `.spec-cli/config.json` | Project-local config (baseUrl, auth, headers) |
1
+ # api-spec-cli
2
+
3
+ CLI for AI agents to explore and call OpenAPI, GraphQL, and MCP APIs. Output is JSON by default — compact, parseable, token-efficient.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g api-spec-cli
9
+ ```
10
+
11
+ Works with Node.js 18+. No other dependencies.
12
+
13
+ ```bash
14
+ # Or run without installing
15
+ npx api-spec-cli <command>
16
+ ```
17
+
18
+ ## How It Works
19
+
20
+ Every command is stateless — you specify the spec source on each call. Two paths:
21
+
22
+ | Path | When to use |
23
+ |---|---|
24
+ | `--spec <name>` | Registered spec — auto-fetches and caches on first use |
25
+ | Inline flags | Ad-hoc — no registration, fetched each call |
26
+
27
+ ### Register once, use everywhere
28
+
29
+ ```bash
30
+ spec add petstore --openapi https://petstore3.swagger.io/api/v3/openapi.json \
31
+ --base-url https://petstore3.swagger.io/api/v3 \
32
+ --description "Petstore example"
33
+
34
+ spec add hashnode --graphql https://gql.hashnode.com --auth YOUR_TOKEN
35
+
36
+ spec add agno --mcp-http https://docs.agno.com/mcp --description "Agno docs"
37
+
38
+ spec add fs --mcp-stdio "npx -y @modelcontextprotocol/server-filesystem /tmp"
39
+ ```
40
+
41
+ Registration is instant — does not connect. Connection happens on first `list`/`show`/`call` and the result is cached at `~/spec-cli-config/cache/<name>.json`.
42
+
43
+ ### Or use inline (no registration)
44
+
45
+ ```bash
46
+ spec list --openapi https://petstore3.swagger.io/api/v3/openapi.json
47
+ spec list --graphql https://gql.hashnode.com
48
+ spec list --mcp-http https://docs.agno.com/mcp
49
+ spec list --mcp-sse http://localhost:3000/sse
50
+ spec list --mcp-stdio "npx -y @modelcontextprotocol/server-filesystem /tmp"
51
+ ```
52
+
53
+ Inline fetches every call, nothing cached.
54
+
55
+ ---
56
+
57
+ ## Discovery
58
+
59
+ ### Search across specs
60
+
61
+ `grep` searches operation/tool names and descriptions across all registered specs.
62
+
63
+ ```bash
64
+ spec grep search # Substring match across all specs
65
+ spec grep "get*" # Glob: anything starting with "get"
66
+ spec grep "*list*" # Glob: anything containing "list"
67
+ spec grep search --spec agno # Limit to one spec
68
+ ```
69
+
70
+ Matches on name and description. Case-insensitive. Plain text = substring, `*`/`?` = glob.
71
+
72
+ ### List all specs in the registry
73
+
74
+ ```bash
75
+ spec specs # Compact: name, type, enabled
76
+ spec specs --compact false # Full: includes source, config
77
+ ```
78
+
79
+ ### List operations / tools
80
+
81
+ `list` is compact by default — just IDs, no schemas. Use `--filter`, `--tag`, `--limit` to narrow down.
82
+
83
+ ```bash
84
+ spec list --spec agno # Registered spec (uses cache)
85
+ spec list --spec petstore --filter pet # Search by keyword
86
+ spec list --spec petstore --tag pets # OpenAPI: filter by tag
87
+ spec list --spec hashnode --tag mutation # GraphQL: filter by kind
88
+ spec list --spec petstore --limit 10 # First 10 only
89
+ spec list --spec petstore --limit 10 --offset 10 # Next 10
90
+ spec list --mcp-http https://docs.agno.com/mcp # Inline: no registration needed
91
+ ```
92
+
93
+ Compact output:
94
+ ```json
95
+ {
96
+ "type": "mcp",
97
+ "total": 1,
98
+ "showing": 1,
99
+ "operations": [
100
+ { "id": "search_agno", "description": "Search across the Agno knowledge base..." }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ Use `--compact false` for full details including `inputSchema` for MCP tools.
106
+
107
+ ### Inspect one operation or tool
108
+
109
+ `show` gives you everything to make a call — params, body schema, response schemas, related types — in one call.
110
+
111
+ ```bash
112
+ spec show --spec petstore getPetById # OpenAPI: by operationId
113
+ spec show --spec petstore /pet/{petId} # OpenAPI: by path
114
+ spec show --spec petstore "GET /pet/{petId}" # OpenAPI: by method + path
115
+ spec show --spec hashnode publishPost # GraphQL: by operation name
116
+ spec show --spec agno search_agno # MCP: by tool name
117
+ ```
118
+
119
+ MCP output includes the full `inputSchema` so you know exactly what arguments to pass.
120
+
121
+ ### Drill into types (OpenAPI/GraphQL only)
122
+
123
+ ```bash
124
+ spec types --spec petstore # List all schema names
125
+ spec types --spec petstore Pet # Inspect one schema
126
+ spec types --spec hashnode PublishPostInput # GraphQL input type
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Calling APIs
132
+
133
+ ```bash
134
+ # OpenAPI
135
+ spec call --spec petstore getPetById --var petId=1
136
+ spec call --spec petstore findPetsByStatus --query status=available
137
+ spec call --spec petstore addPet --data '{"name":"Rex","photoUrls":[]}'
138
+
139
+ # GraphQL (auto-generates query from schema)
140
+ spec call --spec hashnode me
141
+ spec call --spec hashnode publication --var host=blog.hashnode.dev
142
+
143
+ # MCP
144
+ spec call --spec agno search_agno --var query="how to create an agent"
145
+ spec call --spec agno search_agno --data '{"query":"agents"}'
146
+
147
+ # Read body from stdin (explicit --data -)
148
+ echo '{"query":"agents"}' | spec call --spec agno search_agno --data -
149
+ cat body.json | spec call --spec petstore addPet --data -
150
+
151
+ # Inline (no registration)
152
+ spec call --openapi https://petstore3.swagger.io/api/v3/openapi.json \
153
+ getPetById --var petId=1 --base-url https://petstore3.swagger.io/api/v3
154
+ ```
155
+
156
+ ### Per-call overrides
157
+
158
+ Flags passed at call time win over registry entry config, which wins over `.spec-cli/config.json`.
159
+
160
+ ```bash
161
+ spec call --spec agno search_agno --var query="foo" --header X-Tenant=acme
162
+ spec call --spec petstore getPetById --var petId=1 --auth staging-token
163
+ spec list --spec petstore --base-url https://staging.api.example.com
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Registry Management
169
+
170
+ ```bash
171
+ spec remove <name> # Delete entry and remove cache
172
+ spec enable <name> # Re-enable a disabled spec
173
+ spec disable <name> # Disable without removing
174
+ spec refresh <name> # Force re-fetch and update cache
175
+ ```
176
+
177
+ `spec add` is an upsert — re-adding an existing name overwrites the entry (and clears its stale cache), since there's no separate `update` command. The response includes `"overwritten": true` when an existing entry was replaced.
178
+
179
+ ## Usage Ranking
180
+
181
+ `spec` records which operations/tools you call so agents can surface the ones that matter:
182
+
183
+ ```bash
184
+ spec list --spec petstore --top 5 # the 5 most-called operations first
185
+ spec usage # recorded usage across all specs
186
+ spec usage petstore # ranked operations for one spec
187
+ ```
188
+
189
+ Counts are stored locally in `~/spec-cli-config/usage.json`. `--top` applies after `--filter`/`--tag` and overrides `--limit`. Set `SPEC_NO_USAGE=1` to disable tracking.
190
+
191
+ ## Secrets & Environment Overrides
192
+
193
+ Stored values can reference environment variables instead of holding raw secrets. Use `${VAR}` in `--auth` or header values — it's expanded from the environment at call time, never stored expanded:
194
+
195
+ ```bash
196
+ spec add gh --mcp-http https://api.example.com/mcp --header "Authorization=Bearer ${GH_TOKEN}"
197
+ spec config set auth '${API_TOKEN}'
198
+ ```
199
+
200
+ A `.env` file in the working directory is auto-loaded on startup (real environment variables take precedence; set `SPEC_NO_DOTENV=1` to disable).
201
+
202
+ Two environment variables override a registered spec's connection per call — useful in CI:
203
+
204
+ ```bash
205
+ SPEC_URL=https://staging.example.com/mcp spec call --spec gh some_tool # override MCP/GraphQL endpoint
206
+ SPEC_HEADER_X_TENANT=acme spec list --spec gh # add/override a header
207
+ ```
208
+
209
+ `SPEC_HEADER_<NAME>` maps underscores to dashes (`SPEC_HEADER_X_TENANT` → `X-Tenant`). Precedence: call-time flags > env override > registry entry > project config.
210
+
211
+ ## Agent Skill
212
+
213
+ Install a ready-made skill that teaches an agent the explore-then-call workflow:
214
+
215
+ ```bash
216
+ spec skill install # copy SKILL.md into ~/.claude/skills/api-spec-cli/
217
+ spec skill path # print the bundled SKILL.md location
218
+ ```
219
+
220
+ ---
221
+
222
+ ## spec add options
223
+
224
+ ```bash
225
+ spec add <name> --openapi <url-or-file> [--base-url <url>] [--auth <token>] [--header k=v]
226
+ spec add <name> --graphql <url> [--auth <token>] [--header k=v]
227
+ spec add <name> --mcp-http <url> [--auth <token>] [--header k=v]
228
+ spec add <name> --mcp-sse <url> [--auth <token>] [--header k=v]
229
+ spec add <name> --mcp-stdio "<cmd args>" [--env KEY=VAL] [--cwd <path>]
230
+ [--description <text>] (all types)
231
+ ```
232
+
233
+ All options are repeatable where it makes sense (`--header`, `--env`). `--auth` adds `Authorization: Bearer <token>` unless the header is already set.
234
+
235
+ Operation filtering works for all spec types — MCP, OpenAPI, and GraphQL:
236
+
237
+ ```bash
238
+ # MCP: allow only read/list tools
239
+ spec add <name> --mcp-http <url> \
240
+ --allow-tool "read_*" --allow-tool "list_*" \
241
+ --disable-tool "delete_*"
242
+
243
+ # OpenAPI: allow only GET operations (by operationId)
244
+ spec add <name> --openapi <url> \
245
+ --allow-tool "get*" --allow-tool "find*"
246
+
247
+ # GraphQL: allow specific operations by exact name
248
+ spec add <name> --graphql <url> \
249
+ --allow-tool "me" --allow-tool "publication"
250
+ ```
251
+
252
+ `--allow-tool` keeps only matching operations. `--disable-tool` removes matching operations (applied after allow). Both are repeatable.
253
+
254
+ **Matching rules:**
255
+ - Plain text **exact match** (case-insensitive): `"me"` matches only `me`
256
+ - Glob patterns → anchored match: `"get*"` matches `getPetById`, `"*post*"` matches `createPost`
257
+
258
+ Use `grep` for search (substring) — `--allow-tool` / `--disable-tool` for precise whitelists (exact or glob).
259
+
260
+ ---
261
+
262
+ ## OAuth / Authentication
263
+
264
+ MCP HTTP and SSE servers that require OAuth 2.1 are handled automatically. spec-cli detects the 401 on `spec add` and runs the flow before returning.
265
+
266
+ Two modes depending on whether the server supports Dynamic Client Registration (DCR):
267
+
268
+ - **DCR-enabled servers** (e.g. self-hosted with Cloudflare workers-oauth-provider, Stytch, Curity) — no flags needed, browser opens automatically
269
+ - **Pre-registered-only servers** (e.g. GitHub) — pass `--oauth-client-id` with your app's client ID
270
+
271
+ ### Interactive (browser) — default
272
+
273
+ ```bash
274
+ # DCR-enabled server: fully automatic
275
+ spec add myserver --mcp-http https://...
276
+
277
+ # GitHub (no DCR) — create an OAuth App at github.com/settings/developers first
278
+ # Set callback URL to http://127.0.0.1:8090/callback
279
+ spec add github --mcp-http https://api.githubcopilot.com/mcp/ \
280
+ --oauth-client-id <your-github-app-client-id> \
281
+ --oauth-callback-port 8090
282
+ ```
283
+
284
+ ### Headless / device flow
285
+
286
+ ```bash
287
+ spec add myserver --mcp-http https://... --oauth-flow device
288
+ # Prints a URL to stderr — open in any browser to authorize
289
+ ```
290
+
291
+ ### Machine / CI (client credentials)
292
+
293
+ ```bash
294
+ spec add myserver --mcp-http https://... \
295
+ --oauth-client-id <id> --oauth-client-secret <secret>
296
+ ```
297
+
298
+ ### Re-authenticate
299
+
300
+ ```bash
301
+ spec auth myserver # Re-run the OAuth flow
302
+ spec auth myserver --revoke # Clear stored token only
303
+ ```
304
+
305
+ Tokens are stored in `~/spec-cli-config/tokens/<name>.json` — separate from the cache, not touched by `spec refresh`. Access tokens are refreshed automatically: when a stored token expires, the refresh token is used and the rotated tokens are persisted, so the next command picks them up without re-authenticating.
306
+
307
+ ### OAuth flags
308
+
309
+ | Flag | Description |
310
+ | --- | --- |
311
+ | `--oauth-client-id <id>` | Skip DCR — use a pre-registered OAuth app client ID |
312
+ | `--oauth-client-secret <secret>` | Use client credentials flow (machine/CI) |
313
+ | `--oauth-callback-port <port>` | Fixed callback port (required for apps with exact redirect URL match, e.g. GitHub) |
314
+ | `--oauth-flow device` | Force device authorization flow (headless/SSH) |
315
+
316
+ ---
317
+
318
+ ## Config
319
+
320
+ Persistent config stored in `.spec-cli/config.json` (lowest priority — overridden by registry entry config and call-time flags).
321
+
322
+ ```bash
323
+ spec config set baseUrl https://api.example.com
324
+ spec config set auth my-token # Auto-adds "Bearer " prefix
325
+ spec config set auth "Basic dXNlcjpwYXNz" # Or explicit scheme
326
+ spec config set headers.X-API-Key abc123 # Custom header (dot notation)
327
+ spec config get
328
+ spec config unset auth
329
+ ```
330
+
331
+ ## Validate
332
+
333
+ ```bash
334
+ spec validate https://api.example.com/openapi.json
335
+ spec validate ./openapi.yaml
336
+ ```
337
+
338
+ Reports broken `$ref` references, missing required fields, duplicate operationIds, invalid schema types, and more.
339
+
340
+ ## Output Format
341
+
342
+ JSON by default. Errors go to stderr as `{"error": "message"}` with a non-zero exit code.
343
+
344
+ ```bash
345
+ spec list --spec petstore --format text
346
+ spec show --spec petstore getPetById --format yaml
347
+ spec list --spec petstore --format toon # Token-Oriented Object Notation (densest)
348
+ spec list --spec petstore --format=json # equals syntax also works
349
+ ```
350
+
351
+ `toon` ([Token-Oriented Object Notation](https://github.com/toon-format/spec)) is the most token-efficient format for tabular/list output — the best choice when feeding results back into a model. Errors are always JSON regardless of format.
352
+
353
+ ## Token Efficiency
354
+
355
+ - `list` returns only IDs by default — no schemas
356
+ - `show` resolves `$ref` compactly — nested refs show as names, not explosions
357
+ - `types` lets you inspect one schema at a time
358
+ - `--limit` / `--offset` paginate large APIs
359
+ - `--filter` and `--tag` narrow results before output
360
+
361
+ ## MCP Options
362
+
363
+ ```bash
364
+ # Retry on connection failure (useful for stdio servers that take time to start)
365
+ MCP_MAX_RETRIES=3 # Attempts (default: 3)
366
+ MCP_RETRY_DELAY=1000 # Base delay in ms, doubles each attempt, capped at 5s (default: 1000)
367
+
368
+ # HTTP timeout for OpenAPI/GraphQL calls
369
+ SPEC_HTTP_TIMEOUT=30000 # ms (default: 30000)
370
+ ```
371
+
372
+ Stdio env vars support `${VAR}` expansion from the host environment:
373
+
374
+ ```bash
375
+ spec add fs --mcp-stdio "npx -y server /tmp" --env "TOKEN=${MY_SECRET}"
376
+ ```
377
+
378
+ ## Storage
379
+
380
+ | Path | Purpose |
381
+ |---|---|
382
+ | `~/spec-cli-config/registry.json` | Global named registry |
383
+ | `~/spec-cli-config/cache/<name>.json` | Cached spec per registered entry |
384
+ | `~/spec-cli-config/tokens/<name>.json` | OAuth tokens per MCP entry |
385
+ | `~/spec-cli-config/usage.json` | Operation/tool call counts for `--top` and `spec usage` |
386
+ | `.spec-cli/config.json` | Project-local config (baseUrl, auth, headers) |
387
+
388
+ ## Planned
389
+
390
+ - `spec import <file>` — bulk import servers from VS Code `mcp.json` or Claude Desktop config format