mpx-api 1.0.1 → 1.1.0
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 +128 -0
- package/bin/mpx-api.js +27 -1
- package/package.json +11 -6
- package/src/commands/request.js +25 -4
- package/src/lib/http-client.js +9 -1
- package/src/lib/output.js +27 -1
- package/src/mcp.js +180 -0
- package/src/schema.js +307 -0
package/README.md
CHANGED
|
@@ -182,6 +182,134 @@ mpx-api history -n 50
|
|
|
182
182
|
|
|
183
183
|
Cookies are automatically saved and sent with subsequent requests. Cookie jar is stored at `~/.mpx-api/cookies.json`.
|
|
184
184
|
|
|
185
|
+
## AI Agent Usage 🤖
|
|
186
|
+
|
|
187
|
+
**mpx-api is AI-native!** Every command supports structured JSON output, schema discovery, and MCP (Model Context Protocol) integration for seamless AI agent automation.
|
|
188
|
+
|
|
189
|
+
### JSON Output
|
|
190
|
+
|
|
191
|
+
Add `--json` to any command for machine-readable output:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# HTTP request with JSON output
|
|
195
|
+
mpx-api get https://api.github.com/users/octocat --json
|
|
196
|
+
|
|
197
|
+
# Output structure
|
|
198
|
+
{
|
|
199
|
+
"request": {
|
|
200
|
+
"method": "GET",
|
|
201
|
+
"url": "https://api.github.com/users/octocat",
|
|
202
|
+
"headers": {},
|
|
203
|
+
"body": null
|
|
204
|
+
},
|
|
205
|
+
"response": {
|
|
206
|
+
"status": 200,
|
|
207
|
+
"statusText": "OK",
|
|
208
|
+
"headers": { "content-type": "application/json" },
|
|
209
|
+
"body": { "login": "octocat", ... },
|
|
210
|
+
"rawBody": "...",
|
|
211
|
+
"responseTime": 145,
|
|
212
|
+
"size": 1234
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Schema Discovery
|
|
218
|
+
|
|
219
|
+
AI agents can discover all available commands, flags, and output formats:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
mpx-api --schema
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Returns a complete JSON schema describing:
|
|
226
|
+
- All commands and subcommands
|
|
227
|
+
- Available flags and their types
|
|
228
|
+
- Input/output schemas
|
|
229
|
+
- Usage examples
|
|
230
|
+
- Exit codes
|
|
231
|
+
|
|
232
|
+
Perfect for dynamic tool discovery by AI assistants!
|
|
233
|
+
|
|
234
|
+
### MCP Server Mode
|
|
235
|
+
|
|
236
|
+
Start mpx-api as an MCP (Model Context Protocol) server for AI agent integration:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
mpx-api mcp
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Add to your MCP client configuration (e.g., Claude Desktop, Cline):
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"mcpServers": {
|
|
247
|
+
"mpx-api": {
|
|
248
|
+
"command": "npx",
|
|
249
|
+
"args": ["mpx-api", "mcp"]
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Available MCP tools:**
|
|
256
|
+
|
|
257
|
+
- `http_request` - Send HTTP requests with full control over method, headers, body
|
|
258
|
+
- `get_schema` - Get the complete tool schema for dynamic discovery
|
|
259
|
+
|
|
260
|
+
**Example MCP usage:**
|
|
261
|
+
|
|
262
|
+
AI agents can now make API requests on your behalf:
|
|
263
|
+
- "Make a GET request to https://api.github.com/users/octocat"
|
|
264
|
+
- "POST to https://api.example.com/users with JSON body {name: 'Alice'}"
|
|
265
|
+
- "What commands does mpx-api support?" (via get_schema)
|
|
266
|
+
|
|
267
|
+
### Quiet Mode
|
|
268
|
+
|
|
269
|
+
Suppress non-essential output with `--quiet` or `-q`:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
mpx-api get https://api.example.com/data --quiet --json
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Perfect for scripting and automation where you only want the result data.
|
|
276
|
+
|
|
277
|
+
### Composability
|
|
278
|
+
|
|
279
|
+
All commands are designed for Unix-style composition:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Pipe output to jq
|
|
283
|
+
mpx-api get https://api.github.com/users/octocat --json | jq '.response.body.login'
|
|
284
|
+
|
|
285
|
+
# Use in scripts
|
|
286
|
+
STATUS=$(mpx-api get https://api.example.com/health --json | jq -r '.response.status')
|
|
287
|
+
if [ "$STATUS" -eq 200 ]; then
|
|
288
|
+
echo "API is healthy"
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
# Batch processing
|
|
292
|
+
cat urls.txt | while read url; do
|
|
293
|
+
mpx-api get "$url" --json >> results.jsonl
|
|
294
|
+
done
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Exit Codes
|
|
298
|
+
|
|
299
|
+
Predictable exit codes for automation:
|
|
300
|
+
|
|
301
|
+
- `0` - Success (2xx or 3xx HTTP status)
|
|
302
|
+
- `1` - Request failed or 4xx/5xx HTTP status
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
# Check if request succeeded
|
|
306
|
+
if mpx-api get https://api.example.com/endpoint --quiet; then
|
|
307
|
+
echo "Success!"
|
|
308
|
+
else
|
|
309
|
+
echo "Request failed"
|
|
310
|
+
fi
|
|
311
|
+
```
|
|
312
|
+
|
|
185
313
|
## Pro Features 💎
|
|
186
314
|
|
|
187
315
|
Upgrade to **mpx-api Pro** ($12/mo) for advanced features:
|
package/bin/mpx-api.js
CHANGED
|
@@ -15,6 +15,10 @@ import { registerHistoryCommand } from '../src/commands/history.js';
|
|
|
15
15
|
import { registerLoadCommand } from '../src/commands/load.js';
|
|
16
16
|
import { registerDocsCommand } from '../src/commands/docs.js';
|
|
17
17
|
|
|
18
|
+
// AI-native features
|
|
19
|
+
import { getSchema } from '../src/schema.js';
|
|
20
|
+
import { startMCPServer } from '../src/mcp.js';
|
|
21
|
+
|
|
18
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
23
|
const __dirname = dirname(__filename);
|
|
20
24
|
|
|
@@ -22,10 +26,19 @@ const pkg = JSON.parse(
|
|
|
22
26
|
readFileSync(join(__dirname, '../package.json'), 'utf8')
|
|
23
27
|
);
|
|
24
28
|
|
|
29
|
+
// Handle --schema flag early (before commander parsing)
|
|
30
|
+
if (process.argv.includes('--schema')) {
|
|
31
|
+
console.log(JSON.stringify(getSchema(), null, 2));
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
program
|
|
26
36
|
.name('mpx-api')
|
|
27
37
|
.description('Developer-first API testing, mocking, and documentation CLI')
|
|
28
|
-
.version(pkg.version)
|
|
38
|
+
.version(pkg.version)
|
|
39
|
+
.option('--json', 'Output structured JSON (machine-readable)')
|
|
40
|
+
.option('--quiet, -q', 'Suppress non-essential output')
|
|
41
|
+
.option('--schema', 'Output JSON schema describing all commands and flags');
|
|
29
42
|
|
|
30
43
|
// Register HTTP method commands (get, post, put, patch, delete, head, options)
|
|
31
44
|
registerRequestCommands(program);
|
|
@@ -49,4 +62,17 @@ registerHistoryCommand(program);
|
|
|
49
62
|
registerLoadCommand(program);
|
|
50
63
|
registerDocsCommand(program);
|
|
51
64
|
|
|
65
|
+
// MCP subcommand
|
|
66
|
+
program
|
|
67
|
+
.command('mcp')
|
|
68
|
+
.description('Start MCP (Model Context Protocol) stdio server')
|
|
69
|
+
.action(async () => {
|
|
70
|
+
try {
|
|
71
|
+
await startMCPServer();
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error(JSON.stringify({ error: err.message, code: 'ERR_MCP_START' }));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
52
78
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mpx-api",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Developer-first API testing, mocking, and documentation CLI",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Developer-first API testing, mocking, and documentation CLI with AI-native features (JSON output, MCP server)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"mpx-api": "
|
|
7
|
+
"mpx-api": "bin/mpx-api.js"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"api",
|
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
"postman",
|
|
17
17
|
"httpie",
|
|
18
18
|
"openapi",
|
|
19
|
-
"swagger"
|
|
19
|
+
"swagger",
|
|
20
|
+
"mcp",
|
|
21
|
+
"ai-native",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"automation"
|
|
20
24
|
],
|
|
21
25
|
"author": "Mesaplex <support@mesaplex.com>",
|
|
22
26
|
"license": "MIT",
|
|
23
27
|
"repository": {
|
|
24
28
|
"type": "git",
|
|
25
|
-
"url": "https://github.com/mesaplexdev/mpx-api.git"
|
|
29
|
+
"url": "git+https://github.com/mesaplexdev/mpx-api.git"
|
|
26
30
|
},
|
|
27
31
|
"bugs": {
|
|
28
32
|
"url": "https://github.com/mesaplexdev/mpx-api/issues"
|
|
@@ -33,12 +37,13 @@
|
|
|
33
37
|
},
|
|
34
38
|
"type": "module",
|
|
35
39
|
"scripts": {
|
|
36
|
-
"test": "node --test test
|
|
40
|
+
"test": "node --test test/*.test.js",
|
|
37
41
|
"test:watch": "node --test --watch test/**/*.test.js",
|
|
38
42
|
"lint": "echo 'TODO: Add eslint'",
|
|
39
43
|
"prepublishOnly": "npm test"
|
|
40
44
|
},
|
|
41
45
|
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
42
47
|
"chalk": "^5.3.0",
|
|
43
48
|
"commander": "^12.0.0",
|
|
44
49
|
"yaml": "^2.3.4",
|
package/src/commands/request.js
CHANGED
|
@@ -17,8 +17,13 @@ export function registerRequestCommands(program) {
|
|
|
17
17
|
.option('--no-follow', 'Do not follow redirects')
|
|
18
18
|
.option('--no-verify', 'Skip SSL certificate verification')
|
|
19
19
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
20
|
-
.action(async (url, options) => {
|
|
20
|
+
.action(async (url, options, command) => {
|
|
21
21
|
try {
|
|
22
|
+
// Get global options
|
|
23
|
+
const globalOpts = command.optsWithGlobals();
|
|
24
|
+
const jsonOutput = globalOpts.json || false;
|
|
25
|
+
const quiet = options.quiet || globalOpts.quiet || false;
|
|
26
|
+
|
|
22
27
|
const client = new HttpClient({
|
|
23
28
|
followRedirects: options.follow,
|
|
24
29
|
verifySsl: options.verify,
|
|
@@ -34,7 +39,11 @@ export function registerRequestCommands(program) {
|
|
|
34
39
|
try {
|
|
35
40
|
requestOptions.json = JSON.parse(options.json);
|
|
36
41
|
} catch (err) {
|
|
37
|
-
|
|
42
|
+
if (jsonOutput) {
|
|
43
|
+
console.log(JSON.stringify({ error: `Invalid JSON: ${err.message}` }));
|
|
44
|
+
} else {
|
|
45
|
+
formatError(new Error(`Invalid JSON: ${err.message}`));
|
|
46
|
+
}
|
|
38
47
|
process.exit(1);
|
|
39
48
|
}
|
|
40
49
|
} else if (options.data) {
|
|
@@ -57,7 +66,14 @@ export function registerRequestCommands(program) {
|
|
|
57
66
|
// Format and display response
|
|
58
67
|
formatResponse(response, {
|
|
59
68
|
verbose: options.verbose,
|
|
60
|
-
quiet:
|
|
69
|
+
quiet: quiet,
|
|
70
|
+
jsonOutput: jsonOutput,
|
|
71
|
+
request: {
|
|
72
|
+
method: method.toUpperCase(),
|
|
73
|
+
url,
|
|
74
|
+
headers: requestOptions.headers,
|
|
75
|
+
body: requestOptions.json || requestOptions.body,
|
|
76
|
+
}
|
|
61
77
|
});
|
|
62
78
|
|
|
63
79
|
// Exit with non-zero code for 4xx/5xx errors
|
|
@@ -65,7 +81,12 @@ export function registerRequestCommands(program) {
|
|
|
65
81
|
process.exit(1);
|
|
66
82
|
}
|
|
67
83
|
} catch (err) {
|
|
68
|
-
|
|
84
|
+
const globalOpts = command.optsWithGlobals();
|
|
85
|
+
if (globalOpts.json) {
|
|
86
|
+
console.log(JSON.stringify({ error: err.message, code: err.code || 'ERR_REQUEST' }));
|
|
87
|
+
} else {
|
|
88
|
+
formatError(err);
|
|
89
|
+
}
|
|
69
90
|
process.exit(1);
|
|
70
91
|
}
|
|
71
92
|
});
|
package/src/lib/http-client.js
CHANGED
|
@@ -38,9 +38,17 @@ export class HttpClient {
|
|
|
38
38
|
async request(method, url, options = {}) {
|
|
39
39
|
const startTime = Date.now();
|
|
40
40
|
|
|
41
|
+
// Set default User-Agent, allow override via options.headers
|
|
42
|
+
const defaultHeaders = {
|
|
43
|
+
'user-agent': 'mpx-api/1.0.1',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Merge headers, with user headers taking precedence
|
|
47
|
+
const headers = { ...defaultHeaders, ...(options.headers || {}) };
|
|
48
|
+
|
|
41
49
|
const requestOptions = {
|
|
42
50
|
method: method.toUpperCase(),
|
|
43
|
-
headers
|
|
51
|
+
headers,
|
|
44
52
|
body: options.body,
|
|
45
53
|
};
|
|
46
54
|
|
package/src/lib/output.js
CHANGED
|
@@ -3,8 +3,34 @@ import { highlight } from 'cli-highlight';
|
|
|
3
3
|
|
|
4
4
|
const MAX_BODY_SIZE = 50 * 1024; // 50KB max for terminal display
|
|
5
5
|
|
|
6
|
+
export function formatResponseJSON(response, request = {}) {
|
|
7
|
+
return JSON.stringify({
|
|
8
|
+
request: {
|
|
9
|
+
method: request.method || response.method,
|
|
10
|
+
url: request.url || response.url,
|
|
11
|
+
headers: request.headers || {},
|
|
12
|
+
body: request.body || null
|
|
13
|
+
},
|
|
14
|
+
response: {
|
|
15
|
+
status: response.status,
|
|
16
|
+
statusText: response.statusText,
|
|
17
|
+
headers: response.headers,
|
|
18
|
+
body: response.body,
|
|
19
|
+
rawBody: response.rawBody,
|
|
20
|
+
responseTime: response.responseTime,
|
|
21
|
+
size: response.size
|
|
22
|
+
}
|
|
23
|
+
}, null, 2);
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
export function formatResponse(response, options = {}) {
|
|
7
|
-
const { verbose = false, quiet = false } = options;
|
|
27
|
+
const { verbose = false, quiet = false, jsonOutput = false } = options;
|
|
28
|
+
|
|
29
|
+
// Handle JSON output mode
|
|
30
|
+
if (jsonOutput) {
|
|
31
|
+
console.log(formatResponseJSON(response, options.request || {}));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
8
34
|
|
|
9
35
|
if (quiet) {
|
|
10
36
|
// Only output body
|
package/src/mcp.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) Server
|
|
3
|
+
*
|
|
4
|
+
* Exposes mpx-api capabilities as MCP tools for AI agent integration.
|
|
5
|
+
* Runs over stdio transport.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import {
|
|
11
|
+
ListToolsRequestSchema,
|
|
12
|
+
CallToolRequestSchema
|
|
13
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
+
|
|
15
|
+
import { HttpClient } from './lib/http-client.js';
|
|
16
|
+
import { getSchema } from './schema.js';
|
|
17
|
+
import { readFileSync } from 'fs';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
import { dirname, join } from 'path';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
24
|
+
|
|
25
|
+
export async function startMCPServer() {
|
|
26
|
+
const server = new Server(
|
|
27
|
+
{ name: 'mpx-api', version: pkg.version },
|
|
28
|
+
{ capabilities: { tools: {} } }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// List available tools
|
|
32
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
33
|
+
return {
|
|
34
|
+
tools: [
|
|
35
|
+
{
|
|
36
|
+
name: 'http_request',
|
|
37
|
+
description: 'Send an HTTP request (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). Returns structured response with status, headers, body, and timing.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
method: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
|
|
44
|
+
description: 'HTTP method'
|
|
45
|
+
},
|
|
46
|
+
url: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Target URL'
|
|
49
|
+
},
|
|
50
|
+
headers: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
description: 'Request headers as key-value pairs',
|
|
53
|
+
additionalProperties: { type: 'string' }
|
|
54
|
+
},
|
|
55
|
+
json: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
description: 'JSON body (automatically sets Content-Type: application/json)'
|
|
58
|
+
},
|
|
59
|
+
body: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Raw request body (use either json or body, not both)'
|
|
62
|
+
},
|
|
63
|
+
followRedirects: {
|
|
64
|
+
type: 'boolean',
|
|
65
|
+
default: true,
|
|
66
|
+
description: 'Follow HTTP redirects'
|
|
67
|
+
},
|
|
68
|
+
verifySsl: {
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
default: true,
|
|
71
|
+
description: 'Verify SSL certificates'
|
|
72
|
+
},
|
|
73
|
+
timeout: {
|
|
74
|
+
type: 'number',
|
|
75
|
+
default: 30000,
|
|
76
|
+
description: 'Request timeout in milliseconds'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: ['method', 'url']
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'get_schema',
|
|
84
|
+
description: 'Get the full JSON schema describing all mpx-api commands, flags, and output formats.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Handle tool calls
|
|
95
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
96
|
+
const { name, arguments: args } = request.params;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
switch (name) {
|
|
100
|
+
case 'http_request': {
|
|
101
|
+
const client = new HttpClient({
|
|
102
|
+
followRedirects: args.followRedirects !== false,
|
|
103
|
+
verifySsl: args.verifySsl !== false,
|
|
104
|
+
timeout: args.timeout || 30000,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const requestOptions = {
|
|
108
|
+
headers: args.headers || {},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Handle JSON or raw body
|
|
112
|
+
if (args.json) {
|
|
113
|
+
requestOptions.json = args.json;
|
|
114
|
+
} else if (args.body) {
|
|
115
|
+
requestOptions.body = args.body;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const method = (args.method || 'GET').toLowerCase();
|
|
119
|
+
const response = await client.request(method, args.url, requestOptions);
|
|
120
|
+
|
|
121
|
+
// Return structured response
|
|
122
|
+
const result = {
|
|
123
|
+
request: {
|
|
124
|
+
method: method.toUpperCase(),
|
|
125
|
+
url: args.url,
|
|
126
|
+
headers: requestOptions.headers,
|
|
127
|
+
body: args.json || args.body || null
|
|
128
|
+
},
|
|
129
|
+
response: {
|
|
130
|
+
status: response.status,
|
|
131
|
+
statusText: response.statusText,
|
|
132
|
+
headers: response.headers,
|
|
133
|
+
body: response.body,
|
|
134
|
+
rawBody: response.rawBody,
|
|
135
|
+
responseTime: response.responseTime,
|
|
136
|
+
size: response.size
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
content: [{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: JSON.stringify(result, null, 2)
|
|
144
|
+
}]
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
case 'get_schema': {
|
|
149
|
+
return {
|
|
150
|
+
content: [{
|
|
151
|
+
type: 'text',
|
|
152
|
+
text: JSON.stringify(getSchema(), null, 2)
|
|
153
|
+
}]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
default:
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
160
|
+
isError: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
} catch (err) {
|
|
164
|
+
return {
|
|
165
|
+
content: [{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: JSON.stringify({
|
|
168
|
+
error: err.message,
|
|
169
|
+
code: err.code || 'ERR_REQUEST',
|
|
170
|
+
stack: err.stack
|
|
171
|
+
}, null, 2)
|
|
172
|
+
}],
|
|
173
|
+
isError: true
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const transport = new StdioServerTransport();
|
|
179
|
+
await server.connect(transport);
|
|
180
|
+
}
|
package/src/schema.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Module
|
|
3
|
+
*
|
|
4
|
+
* Returns a machine-readable JSON schema describing all commands,
|
|
5
|
+
* flags, inputs, and outputs for AI agent discovery.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
15
|
+
|
|
16
|
+
export function getSchema() {
|
|
17
|
+
return {
|
|
18
|
+
tool: 'mpx-api',
|
|
19
|
+
version: pkg.version,
|
|
20
|
+
description: pkg.description,
|
|
21
|
+
homepage: pkg.homepage,
|
|
22
|
+
commands: {
|
|
23
|
+
'http-methods': {
|
|
24
|
+
description: 'Send HTTP requests (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)',
|
|
25
|
+
usage: 'mpx-api <method> <url> [options]',
|
|
26
|
+
methods: ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'],
|
|
27
|
+
arguments: {
|
|
28
|
+
url: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
required: true,
|
|
31
|
+
description: 'Target URL for the HTTP request'
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
flags: {
|
|
35
|
+
'-H, --header': {
|
|
36
|
+
type: 'array',
|
|
37
|
+
description: 'Add request headers (format: "key:value" or "key: value")',
|
|
38
|
+
repeatable: true
|
|
39
|
+
},
|
|
40
|
+
'-j, --json': {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'Send JSON data (automatically sets Content-Type: application/json)'
|
|
43
|
+
},
|
|
44
|
+
'-d, --data': {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Send raw request body'
|
|
47
|
+
},
|
|
48
|
+
'-v, --verbose': {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
default: false,
|
|
51
|
+
description: 'Show response headers in output'
|
|
52
|
+
},
|
|
53
|
+
'-q, --quiet': {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
default: false,
|
|
56
|
+
description: 'Only output response body (no headers, no formatting)'
|
|
57
|
+
},
|
|
58
|
+
'--no-follow': {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
default: false,
|
|
61
|
+
description: 'Do not follow HTTP redirects'
|
|
62
|
+
},
|
|
63
|
+
'--no-verify': {
|
|
64
|
+
type: 'boolean',
|
|
65
|
+
default: false,
|
|
66
|
+
description: 'Skip SSL certificate verification'
|
|
67
|
+
},
|
|
68
|
+
'--timeout': {
|
|
69
|
+
type: 'number',
|
|
70
|
+
default: 30000,
|
|
71
|
+
description: 'Request timeout in milliseconds'
|
|
72
|
+
},
|
|
73
|
+
'--json-output': {
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
default: false,
|
|
76
|
+
description: 'Output results as structured JSON'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
output: {
|
|
80
|
+
json: {
|
|
81
|
+
description: 'Structured response data when --json-output is used',
|
|
82
|
+
schema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
request: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
method: { type: 'string' },
|
|
89
|
+
url: { type: 'string' },
|
|
90
|
+
headers: { type: 'object' },
|
|
91
|
+
body: { type: ['string', 'object', 'null'] }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
response: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
status: { type: 'number' },
|
|
98
|
+
statusText: { type: 'string' },
|
|
99
|
+
headers: { type: 'object' },
|
|
100
|
+
body: { type: ['string', 'object', 'null'] },
|
|
101
|
+
rawBody: { type: 'string' },
|
|
102
|
+
responseTime: { type: 'number', description: 'Response time in milliseconds' },
|
|
103
|
+
size: { type: 'number', description: 'Response size in bytes' }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
error: {
|
|
110
|
+
description: 'Error response when request fails',
|
|
111
|
+
schema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
error: { type: 'string' },
|
|
115
|
+
message: { type: 'string' },
|
|
116
|
+
code: { type: 'string' }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
exitCodes: {
|
|
122
|
+
0: 'Success (2xx or 3xx status)',
|
|
123
|
+
1: 'Request error or 4xx/5xx status'
|
|
124
|
+
},
|
|
125
|
+
examples: [
|
|
126
|
+
{ command: 'mpx-api get https://api.example.com/users --json-output', description: 'GET request with JSON output' },
|
|
127
|
+
{ command: 'mpx-api post https://api.example.com/users -j \'{"name":"John"}\' --json-output', description: 'POST JSON data with structured output' },
|
|
128
|
+
{ command: 'mpx-api get https://api.example.com/data -H "Authorization: Bearer token" --json-output', description: 'GET with custom headers' }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
collection: {
|
|
132
|
+
description: 'Manage and run request collections',
|
|
133
|
+
subcommands: {
|
|
134
|
+
init: {
|
|
135
|
+
usage: 'mpx-api collection init [options]',
|
|
136
|
+
description: 'Initialize a new collection in current directory',
|
|
137
|
+
flags: {
|
|
138
|
+
'-n, --name': {
|
|
139
|
+
type: 'string',
|
|
140
|
+
default: 'API Collection',
|
|
141
|
+
description: 'Collection name'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
add: {
|
|
146
|
+
usage: 'mpx-api collection add <name> <method> <url> [options]',
|
|
147
|
+
description: 'Add a request to the collection',
|
|
148
|
+
arguments: {
|
|
149
|
+
name: { type: 'string', required: true, description: 'Request name' },
|
|
150
|
+
method: { type: 'string', required: true, description: 'HTTP method' },
|
|
151
|
+
url: { type: 'string', required: true, description: 'Request URL' }
|
|
152
|
+
},
|
|
153
|
+
flags: {
|
|
154
|
+
'-H, --header': { type: 'array', description: 'Request headers' },
|
|
155
|
+
'-j, --json': { type: 'string', description: 'JSON body' },
|
|
156
|
+
'-d, --data': { type: 'string', description: 'Request body' }
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
run: {
|
|
160
|
+
usage: 'mpx-api collection run [file] [options]',
|
|
161
|
+
description: 'Run a collection',
|
|
162
|
+
arguments: {
|
|
163
|
+
file: { type: 'string', required: false, description: 'Collection file (default: .mpx-api/collection.yaml)' }
|
|
164
|
+
},
|
|
165
|
+
flags: {
|
|
166
|
+
'-e, --env': { type: 'string', description: 'Environment name to use' },
|
|
167
|
+
'--base-url': { type: 'string', description: 'Override base URL' },
|
|
168
|
+
'--json-output': { type: 'boolean', description: 'Output results as JSON' }
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
list: {
|
|
172
|
+
usage: 'mpx-api collection list [file]',
|
|
173
|
+
description: 'List requests in a collection'
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
env: {
|
|
178
|
+
description: 'Manage environments',
|
|
179
|
+
subcommands: {
|
|
180
|
+
list: {
|
|
181
|
+
usage: 'mpx-api env list',
|
|
182
|
+
description: 'List all environments',
|
|
183
|
+
flags: {
|
|
184
|
+
'--json-output': { type: 'boolean', description: 'Output as JSON' }
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
set: {
|
|
188
|
+
usage: 'mpx-api env set <name> <key> <value>',
|
|
189
|
+
description: 'Set an environment variable',
|
|
190
|
+
arguments: {
|
|
191
|
+
name: { type: 'string', required: true, description: 'Environment name' },
|
|
192
|
+
key: { type: 'string', required: true, description: 'Variable key' },
|
|
193
|
+
value: { type: 'string', required: true, description: 'Variable value' }
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
get: {
|
|
197
|
+
usage: 'mpx-api env get <name> <key>',
|
|
198
|
+
description: 'Get an environment variable',
|
|
199
|
+
flags: {
|
|
200
|
+
'--json-output': { type: 'boolean', description: 'Output as JSON' }
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
delete: {
|
|
204
|
+
usage: 'mpx-api env delete <name>',
|
|
205
|
+
description: 'Delete an environment'
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
mock: {
|
|
210
|
+
description: 'Start a mock API server',
|
|
211
|
+
usage: 'mpx-api mock [file] [options]',
|
|
212
|
+
arguments: {
|
|
213
|
+
file: { type: 'string', required: false, description: 'Mock definition file' }
|
|
214
|
+
},
|
|
215
|
+
flags: {
|
|
216
|
+
'-p, --port': { type: 'number', default: 3000, description: 'Server port' },
|
|
217
|
+
'--watch': { type: 'boolean', description: 'Watch file for changes' }
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
test: {
|
|
221
|
+
description: 'Run API tests',
|
|
222
|
+
usage: 'mpx-api test <file> [options]',
|
|
223
|
+
arguments: {
|
|
224
|
+
file: { type: 'string', required: true, description: 'Test file to run' }
|
|
225
|
+
},
|
|
226
|
+
flags: {
|
|
227
|
+
'-e, --env': { type: 'string', description: 'Environment name' },
|
|
228
|
+
'--json-output': { type: 'boolean', description: 'Output results as JSON' }
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
history: {
|
|
232
|
+
description: 'View request history',
|
|
233
|
+
usage: 'mpx-api history [options]',
|
|
234
|
+
flags: {
|
|
235
|
+
'-n, --limit': { type: 'number', default: 10, description: 'Number of entries to show' },
|
|
236
|
+
'--json-output': { type: 'boolean', description: 'Output as JSON' }
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
load: {
|
|
240
|
+
description: 'Load test API endpoint (Pro)',
|
|
241
|
+
usage: 'mpx-api load <url> [options]',
|
|
242
|
+
arguments: {
|
|
243
|
+
url: { type: 'string', required: true, description: 'Target URL' }
|
|
244
|
+
},
|
|
245
|
+
flags: {
|
|
246
|
+
'-c, --concurrency': { type: 'number', default: 10, description: 'Concurrent requests' },
|
|
247
|
+
'-n, --requests': { type: 'number', default: 100, description: 'Total requests' },
|
|
248
|
+
'--json-output': { type: 'boolean', description: 'Output results as JSON' }
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
docs: {
|
|
252
|
+
description: 'Generate API documentation (Pro)',
|
|
253
|
+
usage: 'mpx-api docs <file> [options]',
|
|
254
|
+
arguments: {
|
|
255
|
+
file: { type: 'string', required: true, description: 'Collection or OpenAPI file' }
|
|
256
|
+
},
|
|
257
|
+
flags: {
|
|
258
|
+
'-o, --output': { type: 'string', description: 'Output directory' },
|
|
259
|
+
'--format': { type: 'string', enum: ['html', 'markdown'], default: 'html', description: 'Output format' }
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
mcp: {
|
|
263
|
+
description: 'Start MCP (Model Context Protocol) stdio server for AI agent integration',
|
|
264
|
+
usage: 'mpx-api mcp',
|
|
265
|
+
examples: [
|
|
266
|
+
{ command: 'mpx-api mcp', description: 'Start MCP stdio server' }
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
globalFlags: {
|
|
271
|
+
'--json-output': {
|
|
272
|
+
type: 'boolean',
|
|
273
|
+
default: false,
|
|
274
|
+
description: 'Output structured JSON for machine consumption'
|
|
275
|
+
},
|
|
276
|
+
'--quiet': {
|
|
277
|
+
type: 'boolean',
|
|
278
|
+
default: false,
|
|
279
|
+
description: 'Suppress non-essential output'
|
|
280
|
+
},
|
|
281
|
+
'--schema': {
|
|
282
|
+
type: 'boolean',
|
|
283
|
+
default: false,
|
|
284
|
+
description: 'Output this schema as JSON'
|
|
285
|
+
},
|
|
286
|
+
'--version': {
|
|
287
|
+
type: 'boolean',
|
|
288
|
+
description: 'Show version number'
|
|
289
|
+
},
|
|
290
|
+
'--help': {
|
|
291
|
+
type: 'boolean',
|
|
292
|
+
description: 'Show help information'
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
mcpConfig: {
|
|
296
|
+
description: 'Add to your MCP client configuration to use mpx-api as an AI tool',
|
|
297
|
+
config: {
|
|
298
|
+
mcpServers: {
|
|
299
|
+
'mpx-api': {
|
|
300
|
+
command: 'npx',
|
|
301
|
+
args: ['mpx-api', 'mcp']
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|