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