api-spec-cli 0.2.2 → 0.2.4
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 +343 -267
- package/package.json +53 -41
- package/src/cli.js +183 -172
- package/src/commands/add.js +161 -80
- package/src/commands/auth.js +32 -0
- package/src/commands/call.js +220 -207
- package/src/commands/fetch.js +344 -287
- package/src/commands/grep.js +67 -64
- package/src/commands/list.js +78 -80
- package/src/commands/show.js +224 -215
- package/src/commands/specs.js +82 -69
- package/src/commands/types.js +167 -163
- package/src/commands/validate.js +295 -269
- package/src/glob.js +34 -15
- package/src/mcp-client.js +88 -63
- package/src/oauth/auth-flow.js +59 -0
- package/src/oauth/provider.js +192 -0
- package/src/oauth/tokens.js +53 -0
- package/src/registry.js +79 -53
- package/src/resolve.js +64 -65
package/package.json
CHANGED
|
@@ -1,41 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "api-spec-cli",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Agent-friendly CLI for exploring and calling OpenAPI and GraphQL APIs",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"spec": "bin/spec.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "bun test"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "api-spec-cli",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Agent-friendly CLI for exploring and calling OpenAPI and GraphQL APIs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"spec": "bin/spec.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "bun test",
|
|
11
|
+
"format": "prettier --write \"src/**/*.js\" \"test/**/*.js\"",
|
|
12
|
+
"format:check": "prettier --check \"src/**/*.js\" \"test/**/*.js\"",
|
|
13
|
+
"release": "bash release.sh"
|
|
14
|
+
},
|
|
15
|
+
"prettier": {
|
|
16
|
+
"semi": true,
|
|
17
|
+
"singleQuote": false,
|
|
18
|
+
"printWidth": 100,
|
|
19
|
+
"trailingComma": "es5"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin/",
|
|
23
|
+
"src/",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"openapi",
|
|
28
|
+
"graphql",
|
|
29
|
+
"cli",
|
|
30
|
+
"agent",
|
|
31
|
+
"api",
|
|
32
|
+
"swagger",
|
|
33
|
+
"ai-agent",
|
|
34
|
+
"mcp",
|
|
35
|
+
"introspection"
|
|
36
|
+
],
|
|
37
|
+
"author": "niradler55",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/niradler/api-spec-cli.git"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
48
|
+
"yaml": "^2.7.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"prettier": "^3.8.1"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -1,172 +1,183 @@
|
|
|
1
|
-
import { listOperations } from "./commands/list.js";
|
|
2
|
-
import { showOperation } from "./commands/show.js";
|
|
3
|
-
import { callOperation } from "./commands/call.js";
|
|
4
|
-
import { configCmd } from "./commands/config.js";
|
|
5
|
-
import { validateSpec } from "./commands/validate.js";
|
|
6
|
-
import { typesCmd } from "./commands/types.js";
|
|
7
|
-
import { addCmd } from "./commands/add.js";
|
|
8
|
-
import { specsCmd, registryMutate } from "./commands/specs.js";
|
|
9
|
-
import { grepCmd } from "./commands/grep.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--mcp-
|
|
23
|
-
--mcp-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
spec add <name> --
|
|
28
|
-
spec add <name> --
|
|
29
|
-
spec add <name> --mcp-
|
|
30
|
-
spec add <name> --mcp-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
--
|
|
34
|
-
--
|
|
35
|
-
--
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
spec
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
spec
|
|
46
|
-
spec
|
|
47
|
-
spec
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
spec
|
|
51
|
-
spec
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
spec
|
|
55
|
-
spec
|
|
56
|
-
spec
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
spec
|
|
61
|
-
spec
|
|
62
|
-
spec
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
spec call --spec <name> <op> --
|
|
66
|
-
spec call --spec <name> <op> --
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
--
|
|
70
|
-
--
|
|
71
|
-
--
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
spec
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
spec
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
case "
|
|
141
|
-
await
|
|
142
|
-
break;
|
|
143
|
-
case "
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
case "
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
1
|
+
import { listOperations } from "./commands/list.js";
|
|
2
|
+
import { showOperation } from "./commands/show.js";
|
|
3
|
+
import { callOperation } from "./commands/call.js";
|
|
4
|
+
import { configCmd } from "./commands/config.js";
|
|
5
|
+
import { validateSpec } from "./commands/validate.js";
|
|
6
|
+
import { typesCmd } from "./commands/types.js";
|
|
7
|
+
import { addCmd } from "./commands/add.js";
|
|
8
|
+
import { specsCmd, registryMutate } from "./commands/specs.js";
|
|
9
|
+
import { grepCmd } from "./commands/grep.js";
|
|
10
|
+
import { authCmd } from "./commands/auth.js";
|
|
11
|
+
import { out, err, setFormat } from "./output.js";
|
|
12
|
+
|
|
13
|
+
const HELP = `spec-cli — Explore and call APIs from the command line.
|
|
14
|
+
All output is JSON. Designed for AI agents but works for humans too.
|
|
15
|
+
|
|
16
|
+
Every command is stateless — specify the spec source on each call.
|
|
17
|
+
|
|
18
|
+
SPEC SOURCE (required on every list/show/call):
|
|
19
|
+
--spec <name> Use a registered spec (auto-fetches + caches)
|
|
20
|
+
--openapi <url-or-file> OpenAPI inline (no registration needed)
|
|
21
|
+
--graphql <url> GraphQL inline
|
|
22
|
+
--mcp-http <url> MCP streamable-HTTP inline
|
|
23
|
+
--mcp-sse <url> MCP SSE inline
|
|
24
|
+
--mcp-stdio "<cmd args>" MCP stdio inline
|
|
25
|
+
|
|
26
|
+
REGISTRY (register once, use anywhere):
|
|
27
|
+
spec add <name> --openapi <url> Register an OpenAPI spec
|
|
28
|
+
spec add <name> --graphql <url> Register a GraphQL endpoint
|
|
29
|
+
spec add <name> --mcp-http <url> Register an MCP server (streamable-HTTP)
|
|
30
|
+
spec add <name> --mcp-sse <url> Register an MCP server (SSE)
|
|
31
|
+
spec add <name> --mcp-stdio "<cmd>" Register an MCP server (stdio)
|
|
32
|
+
Options: --description <text> --base-url <url> --auth <token>
|
|
33
|
+
--header k=v (repeatable) --env KEY=VAL (repeatable, stdio only)
|
|
34
|
+
--cwd <path> (stdio only)
|
|
35
|
+
--allow-tool <glob> (repeatable)
|
|
36
|
+
--disable-tool <glob> (repeatable)
|
|
37
|
+
--oauth-flow browser|device OAuth flow (http/sse only, default: browser)
|
|
38
|
+
--oauth-client-id <id> Pre-registered OAuth client ID
|
|
39
|
+
--oauth-client-secret <secret> Client secret (stored securely, not in registry)
|
|
40
|
+
--oauth-callback-port <1-65535> Fixed local port for browser callback
|
|
41
|
+
|
|
42
|
+
spec specs List all registered specs
|
|
43
|
+
spec specs --compact false Show full entry config
|
|
44
|
+
spec remove <name> Delete from registry
|
|
45
|
+
spec enable <name> Enable a disabled spec
|
|
46
|
+
spec disable <name> Disable without removing
|
|
47
|
+
spec refresh <name> Force re-fetch and update cache
|
|
48
|
+
|
|
49
|
+
DISCOVER:
|
|
50
|
+
spec list --spec <name> All operations/tools (compact IDs)
|
|
51
|
+
spec list --spec <name> --filter user Search by keyword
|
|
52
|
+
spec list --spec <name> --tag pets OpenAPI tag or GraphQL kind
|
|
53
|
+
spec list --spec <name> --limit 10 Paginate
|
|
54
|
+
spec list --mcp-http <url> Inline: no registration needed
|
|
55
|
+
spec grep <pattern> Search across all registered specs
|
|
56
|
+
spec grep <pattern> --spec <name> Search within one spec
|
|
57
|
+
|
|
58
|
+
INSPECT:
|
|
59
|
+
spec show --spec <name> <op> Operation details (params, body, responses)
|
|
60
|
+
spec show --spec <name> <tool> MCP tool input schema
|
|
61
|
+
spec types --spec <name> List all schema/type names (OpenAPI/GraphQL)
|
|
62
|
+
spec types --spec <name> <TypeName> Inspect one type
|
|
63
|
+
|
|
64
|
+
CALL:
|
|
65
|
+
spec call --spec <name> <op> --var petId=1 Path/GraphQL vars
|
|
66
|
+
spec call --spec <name> <op> --query status=available Query params
|
|
67
|
+
spec call --spec <name> <op> --data '{"name":"Rex"}' JSON body / MCP args
|
|
68
|
+
spec call --spec <name> <op> --data-file args.json Body from file
|
|
69
|
+
spec call --spec <name> <op> --data - Read JSON body from stdin (pipe)
|
|
70
|
+
spec call --spec <name> <op> --header X-Custom=val Extra headers
|
|
71
|
+
spec call --spec <name> <op> --method PUT Override HTTP method
|
|
72
|
+
|
|
73
|
+
PER-CALL OVERRIDES (win over registry entry config):
|
|
74
|
+
--auth <token> Override auth for this call
|
|
75
|
+
--base-url <url> Override base URL for this call
|
|
76
|
+
--header k=v Merge/override headers for this call
|
|
77
|
+
|
|
78
|
+
CONFIG (persisted in .spec-cli/config.json — lowest priority):
|
|
79
|
+
spec config set baseUrl https://api.example.com
|
|
80
|
+
spec config set auth <token>
|
|
81
|
+
spec config set headers.X-API-Key <key>
|
|
82
|
+
spec config get
|
|
83
|
+
spec config unset auth
|
|
84
|
+
|
|
85
|
+
OTHER:
|
|
86
|
+
spec auth <name> Re-authenticate an OAuth-protected MCP spec
|
|
87
|
+
spec auth <name> --revoke Clear stored OAuth token
|
|
88
|
+
spec validate <file-or-url> Check OpenAPI spec for errors
|
|
89
|
+
--format json|text|yaml Output format (default: json)
|
|
90
|
+
|
|
91
|
+
ENV VARS (MCP):
|
|
92
|
+
MCP_MAX_RETRIES=3 Retry attempts on connection failure (default: 3)
|
|
93
|
+
MCP_RETRY_DELAY=1000 Base retry delay in ms, doubles each attempt (default: 1000)
|
|
94
|
+
SPEC_OAUTH_CALLBACK_PORT=3141 Default fixed port for browser OAuth callback
|
|
95
|
+
|
|
96
|
+
EXAMPLES:
|
|
97
|
+
spec add agno --mcp-http https://docs.agno.com/mcp --description "Agno docs"
|
|
98
|
+
spec add petstore --openapi https://petstore3.swagger.io/api/v3/openapi.json \\
|
|
99
|
+
--base-url https://petstore3.swagger.io/api/v3
|
|
100
|
+
spec specs
|
|
101
|
+
spec list --spec agno
|
|
102
|
+
spec show --spec agno search_agno
|
|
103
|
+
spec call --spec agno search_agno --var query="agents"
|
|
104
|
+
spec call --spec agno search_agno --var query="foo" --header X-Tenant=acme
|
|
105
|
+
spec list --mcp-http https://docs.agno.com/mcp (inline, no registration)`;
|
|
106
|
+
|
|
107
|
+
export async function run(args) {
|
|
108
|
+
// Extract --format before routing (supports both --format json and --format=json)
|
|
109
|
+
const newArgs = [];
|
|
110
|
+
for (let i = 0; i < args.length; i++) {
|
|
111
|
+
if (args[i] === "--format" && i + 1 < args.length) {
|
|
112
|
+
setFormat(args[++i]);
|
|
113
|
+
} else if (args[i].startsWith("--format=")) {
|
|
114
|
+
setFormat(args[i].slice(9));
|
|
115
|
+
} else {
|
|
116
|
+
newArgs.push(args[i]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
args = newArgs;
|
|
120
|
+
|
|
121
|
+
const cmd = args[0];
|
|
122
|
+
|
|
123
|
+
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
124
|
+
out({ help: HELP });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
switch (cmd) {
|
|
130
|
+
case "list":
|
|
131
|
+
case "ls":
|
|
132
|
+
await listOperations(args.slice(1));
|
|
133
|
+
break;
|
|
134
|
+
case "show":
|
|
135
|
+
await showOperation(args.slice(1));
|
|
136
|
+
break;
|
|
137
|
+
case "call":
|
|
138
|
+
await callOperation(args.slice(1));
|
|
139
|
+
break;
|
|
140
|
+
case "validate":
|
|
141
|
+
await validateSpec(args.slice(1));
|
|
142
|
+
break;
|
|
143
|
+
case "types":
|
|
144
|
+
case "type":
|
|
145
|
+
await typesCmd(args.slice(1));
|
|
146
|
+
break;
|
|
147
|
+
case "config":
|
|
148
|
+
case "cfg":
|
|
149
|
+
await configCmd(args.slice(1));
|
|
150
|
+
break;
|
|
151
|
+
case "add":
|
|
152
|
+
await addCmd(args.slice(1));
|
|
153
|
+
break;
|
|
154
|
+
case "specs":
|
|
155
|
+
case "registry":
|
|
156
|
+
await specsCmd(args.slice(1));
|
|
157
|
+
break;
|
|
158
|
+
case "remove":
|
|
159
|
+
await registryMutate("remove", args.slice(1));
|
|
160
|
+
break;
|
|
161
|
+
case "enable":
|
|
162
|
+
await registryMutate("enable", args.slice(1));
|
|
163
|
+
break;
|
|
164
|
+
case "disable":
|
|
165
|
+
await registryMutate("disable", args.slice(1));
|
|
166
|
+
break;
|
|
167
|
+
case "refresh":
|
|
168
|
+
await registryMutate("refresh", args.slice(1));
|
|
169
|
+
break;
|
|
170
|
+
case "grep":
|
|
171
|
+
await grepCmd(args.slice(1));
|
|
172
|
+
break;
|
|
173
|
+
case "auth":
|
|
174
|
+
await authCmd(args.slice(1));
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
err(`Unknown command: ${cmd}. Run 'spec help' for usage.`);
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
err(e.message);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|