mpx-api 1.0.2 → 1.2.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 +28 -1
- package/package.json +9 -4
- package/src/commands/collection.js +14 -7
- package/src/commands/request.js +26 -4
- package/src/lib/assertion.js +3 -21
- package/src/lib/http-client.js +25 -3
- package/src/lib/output.js +27 -1
- package/src/lib/template.js +3 -22
- package/src/lib/utils.js +25 -0
- package/src/mcp.js +180 -0
- package/src/schema.js +294 -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,20 @@ 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
|
+
.enablePositionalOptions()
|
|
40
|
+
.passThroughOptions()
|
|
41
|
+
.option('-o, --output <format>', 'Output format: json for structured JSON (machine-readable)')
|
|
42
|
+
.option('-q, --quiet', 'Suppress non-essential output');
|
|
29
43
|
|
|
30
44
|
// Register HTTP method commands (get, post, put, patch, delete, head, options)
|
|
31
45
|
registerRequestCommands(program);
|
|
@@ -49,4 +63,17 @@ registerHistoryCommand(program);
|
|
|
49
63
|
registerLoadCommand(program);
|
|
50
64
|
registerDocsCommand(program);
|
|
51
65
|
|
|
66
|
+
// MCP subcommand
|
|
67
|
+
program
|
|
68
|
+
.command('mcp')
|
|
69
|
+
.description('Start MCP (Model Context Protocol) stdio server')
|
|
70
|
+
.action(async () => {
|
|
71
|
+
try {
|
|
72
|
+
await startMCPServer();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error(JSON.stringify({ error: err.message, code: 'ERR_MCP_START' }));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
52
79
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mpx-api",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Developer-first API testing, mocking, and documentation CLI",
|
|
3
|
+
"version": "1.2.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
7
|
"mpx-api": "bin/mpx-api.js"
|
|
@@ -16,7 +16,11 @@
|
|
|
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",
|
|
@@ -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",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import { parse, stringify } from 'yaml';
|
|
3
3
|
import { ensureLocalDir } from '../lib/config.js';
|
|
4
|
-
import { formatSuccess, formatError, formatInfo } from '../lib/output.js';
|
|
4
|
+
import { formatSuccess, formatError, formatInfo, formatWarning } from '../lib/output.js';
|
|
5
5
|
import { runCollection } from '../lib/collection-runner.js';
|
|
6
6
|
import { formatTestResults } from '../lib/output.js';
|
|
7
7
|
import { join } from 'path';
|
|
@@ -95,7 +95,8 @@ export function registerCollectionCommands(program) {
|
|
|
95
95
|
.description('Run a collection')
|
|
96
96
|
.option('-e, --env <name>', 'Environment to use')
|
|
97
97
|
.option('--base-url <url>', 'Override base URL')
|
|
98
|
-
.
|
|
98
|
+
.option('--json', 'Output results as JSON')
|
|
99
|
+
.action(async (file, options, command) => {
|
|
99
100
|
try {
|
|
100
101
|
const collectionPath = file || join('.mpx-api', 'collection.yaml');
|
|
101
102
|
|
|
@@ -123,9 +124,17 @@ export function registerCollectionCommands(program) {
|
|
|
123
124
|
|
|
124
125
|
const results = await runCollection(collection, { env, baseUrl });
|
|
125
126
|
|
|
126
|
-
const
|
|
127
|
+
const globalOpts = command.optsWithGlobals();
|
|
128
|
+
const jsonOutput = options.json || globalOpts.output === 'json';
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
if (jsonOutput) {
|
|
131
|
+
console.log(JSON.stringify(results, null, 2));
|
|
132
|
+
const allPassed = results.every(r => r.passed);
|
|
133
|
+
process.exit(allPassed ? 0 : 1);
|
|
134
|
+
} else {
|
|
135
|
+
const allPassed = formatTestResults(results);
|
|
136
|
+
process.exit(allPassed ? 0 : 1);
|
|
137
|
+
}
|
|
129
138
|
} catch (err) {
|
|
130
139
|
formatError(err);
|
|
131
140
|
process.exit(1);
|
|
@@ -170,6 +179,4 @@ export function registerCollectionCommands(program) {
|
|
|
170
179
|
});
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
|
|
174
|
-
console.log(chalk.yellow('⚠'), message);
|
|
175
|
-
}
|
|
182
|
+
// formatWarning imported from output.js
|
package/src/commands/request.js
CHANGED
|
@@ -17,8 +17,14 @@ 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
|
-
.
|
|
20
|
+
.option('-o, --output <format>', 'Output format (json)')
|
|
21
|
+
.action(async (url, options, command) => {
|
|
21
22
|
try {
|
|
23
|
+
// Get global options
|
|
24
|
+
const globalOpts = command.optsWithGlobals();
|
|
25
|
+
const jsonOutput = options.output === 'json' || globalOpts.output === 'json';
|
|
26
|
+
const quiet = options.quiet || globalOpts.quiet || false;
|
|
27
|
+
|
|
22
28
|
const client = new HttpClient({
|
|
23
29
|
followRedirects: options.follow,
|
|
24
30
|
verifySsl: options.verify,
|
|
@@ -34,7 +40,11 @@ export function registerRequestCommands(program) {
|
|
|
34
40
|
try {
|
|
35
41
|
requestOptions.json = JSON.parse(options.json);
|
|
36
42
|
} catch (err) {
|
|
37
|
-
|
|
43
|
+
if (jsonOutput) {
|
|
44
|
+
console.log(JSON.stringify({ error: `Invalid JSON: ${err.message}` }));
|
|
45
|
+
} else {
|
|
46
|
+
formatError(new Error(`Invalid JSON: ${err.message}`));
|
|
47
|
+
}
|
|
38
48
|
process.exit(1);
|
|
39
49
|
}
|
|
40
50
|
} else if (options.data) {
|
|
@@ -57,7 +67,14 @@ export function registerRequestCommands(program) {
|
|
|
57
67
|
// Format and display response
|
|
58
68
|
formatResponse(response, {
|
|
59
69
|
verbose: options.verbose,
|
|
60
|
-
quiet:
|
|
70
|
+
quiet: quiet,
|
|
71
|
+
jsonOutput: jsonOutput,
|
|
72
|
+
request: {
|
|
73
|
+
method: method.toUpperCase(),
|
|
74
|
+
url,
|
|
75
|
+
headers: requestOptions.headers,
|
|
76
|
+
body: requestOptions.json || requestOptions.body,
|
|
77
|
+
}
|
|
61
78
|
});
|
|
62
79
|
|
|
63
80
|
// Exit with non-zero code for 4xx/5xx errors
|
|
@@ -65,7 +82,12 @@ export function registerRequestCommands(program) {
|
|
|
65
82
|
process.exit(1);
|
|
66
83
|
}
|
|
67
84
|
} catch (err) {
|
|
68
|
-
|
|
85
|
+
const globalOpts = command.optsWithGlobals();
|
|
86
|
+
if (options.output === 'json' || globalOpts.output === 'json') {
|
|
87
|
+
console.log(JSON.stringify({ error: err.message, code: err.code || 'ERR_REQUEST' }));
|
|
88
|
+
} else {
|
|
89
|
+
formatError(err);
|
|
90
|
+
}
|
|
69
91
|
process.exit(1);
|
|
70
92
|
}
|
|
71
93
|
});
|
package/src/lib/assertion.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getNestedValue } from './utils.js';
|
|
2
|
+
|
|
1
3
|
// Assertion engine for testing
|
|
2
4
|
|
|
3
5
|
export function runAssertions(response, assertions) {
|
|
@@ -75,27 +77,7 @@ export function runAssertions(response, assertions) {
|
|
|
75
77
|
return results;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
const parts = path.split('.');
|
|
80
|
-
let current = obj;
|
|
81
|
-
|
|
82
|
-
for (const part of parts) {
|
|
83
|
-
if (current === null || current === undefined) return undefined;
|
|
84
|
-
|
|
85
|
-
// Handle array indexing
|
|
86
|
-
const arrayMatch = part.match(/^(.+?)\[(\d+)\]$/);
|
|
87
|
-
if (arrayMatch) {
|
|
88
|
-
const [, key, index] = arrayMatch;
|
|
89
|
-
current = current[key];
|
|
90
|
-
if (!Array.isArray(current)) return undefined;
|
|
91
|
-
current = current[parseInt(index)];
|
|
92
|
-
} else {
|
|
93
|
-
current = current[part];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return current;
|
|
98
|
-
}
|
|
80
|
+
// getNestedValue imported from utils.js
|
|
99
81
|
|
|
100
82
|
function evaluateOperators(actual, expected) {
|
|
101
83
|
for (const [op, value] of Object.entries(expected)) {
|
package/src/lib/http-client.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { request } from 'undici';
|
|
2
2
|
import { CookieJar } from 'tough-cookie';
|
|
3
3
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
4
6
|
import { getCookieJarPath } from './config.js';
|
|
5
7
|
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const pkgVersion = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')).version;
|
|
11
|
+
|
|
6
12
|
export class HttpClient {
|
|
7
13
|
constructor(options = {}) {
|
|
8
14
|
this.followRedirects = options.followRedirects !== false;
|
|
@@ -40,7 +46,7 @@ export class HttpClient {
|
|
|
40
46
|
|
|
41
47
|
// Set default User-Agent, allow override via options.headers
|
|
42
48
|
const defaultHeaders = {
|
|
43
|
-
'user-agent':
|
|
49
|
+
'user-agent': `mpx-api/${pkgVersion}`,
|
|
44
50
|
};
|
|
45
51
|
|
|
46
52
|
// Merge headers, with user headers taking precedence
|
|
@@ -52,7 +58,18 @@ export class HttpClient {
|
|
|
52
58
|
body: options.body,
|
|
53
59
|
};
|
|
54
60
|
|
|
55
|
-
//
|
|
61
|
+
// Handle redirects
|
|
62
|
+
if (!this.followRedirects) {
|
|
63
|
+
requestOptions.maxRedirections = 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle timeout
|
|
67
|
+
if (this.timeout) {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
70
|
+
requestOptions.signal = controller.signal;
|
|
71
|
+
requestOptions._timeoutId = timeoutId;
|
|
72
|
+
}
|
|
56
73
|
|
|
57
74
|
// Add cookies to request
|
|
58
75
|
const cookies = await this.cookieJar.getCookies(url);
|
|
@@ -72,7 +89,10 @@ export class HttpClient {
|
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
try {
|
|
92
|
+
const timeoutId = requestOptions._timeoutId;
|
|
93
|
+
delete requestOptions._timeoutId;
|
|
75
94
|
const response = await request(url, requestOptions);
|
|
95
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
76
96
|
|
|
77
97
|
// Store cookies from response
|
|
78
98
|
const setCookieHeaders = response.headers['set-cookie'];
|
|
@@ -119,7 +139,9 @@ export class HttpClient {
|
|
|
119
139
|
};
|
|
120
140
|
} catch (err) {
|
|
121
141
|
// Handle network errors gracefully
|
|
122
|
-
if (err.code === '
|
|
142
|
+
if (err.name === 'AbortError' || err.code === 'UND_ERR_ABORTED') {
|
|
143
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
144
|
+
} else if (err.code === 'ENOTFOUND') {
|
|
123
145
|
throw new Error(`DNS lookup failed for ${url}`);
|
|
124
146
|
} else if (err.code === 'ECONNREFUSED') {
|
|
125
147
|
throw new Error(`Connection refused to ${url}`);
|
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/lib/template.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getNestedValue } from './utils.js';
|
|
2
|
+
|
|
1
3
|
// Template variable interpolation
|
|
2
4
|
// Supports: {{varName}}, {{env.VAR}}, {{request-name.response.body.field}}
|
|
3
5
|
|
|
@@ -39,25 +41,4 @@ export function interpolateObject(obj, context = {}) {
|
|
|
39
41
|
return obj;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
// Handle array indexing: users[0].name
|
|
44
|
-
const parts = path.split('.');
|
|
45
|
-
let current = obj;
|
|
46
|
-
|
|
47
|
-
for (const part of parts) {
|
|
48
|
-
if (!current) return undefined;
|
|
49
|
-
|
|
50
|
-
// Check for array indexing
|
|
51
|
-
const arrayMatch = part.match(/^(.+?)\[(\d+)\]$/);
|
|
52
|
-
if (arrayMatch) {
|
|
53
|
-
const [, key, index] = arrayMatch;
|
|
54
|
-
current = current[key];
|
|
55
|
-
if (!Array.isArray(current)) return undefined;
|
|
56
|
-
current = current[parseInt(index)];
|
|
57
|
-
} else {
|
|
58
|
-
current = current[part];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return current;
|
|
63
|
-
}
|
|
44
|
+
// getNestedValue imported from utils.js
|
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a nested value from an object using dot notation.
|
|
3
|
+
* Supports array indexing: users[0].name
|
|
4
|
+
*/
|
|
5
|
+
export function getNestedValue(obj, path) {
|
|
6
|
+
const parts = path.split('.');
|
|
7
|
+
let current = obj;
|
|
8
|
+
|
|
9
|
+
for (const part of parts) {
|
|
10
|
+
if (current === null || current === undefined) return undefined;
|
|
11
|
+
|
|
12
|
+
// Handle array indexing
|
|
13
|
+
const arrayMatch = part.match(/^(.+?)\[(\d+)\]$/);
|
|
14
|
+
if (arrayMatch) {
|
|
15
|
+
const [, key, index] = arrayMatch;
|
|
16
|
+
current = current[key];
|
|
17
|
+
if (!Array.isArray(current)) return undefined;
|
|
18
|
+
current = current[parseInt(index)];
|
|
19
|
+
} else {
|
|
20
|
+
current = current[part];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return current;
|
|
25
|
+
}
|
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,294 @@
|
|
|
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
|
+
},
|
|
74
|
+
output: {
|
|
75
|
+
json: {
|
|
76
|
+
description: 'Structured response data when --output json is used',
|
|
77
|
+
schema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
request: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
method: { type: 'string' },
|
|
84
|
+
url: { type: 'string' },
|
|
85
|
+
headers: { type: 'object' },
|
|
86
|
+
body: { type: ['string', 'object', 'null'] }
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
response: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
status: { type: 'number' },
|
|
93
|
+
statusText: { type: 'string' },
|
|
94
|
+
headers: { type: 'object' },
|
|
95
|
+
body: { type: ['string', 'object', 'null'] },
|
|
96
|
+
rawBody: { type: 'string' },
|
|
97
|
+
responseTime: { type: 'number', description: 'Response time in milliseconds' },
|
|
98
|
+
size: { type: 'number', description: 'Response size in bytes' }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
error: {
|
|
105
|
+
description: 'Error response when request fails',
|
|
106
|
+
schema: {
|
|
107
|
+
type: 'object',
|
|
108
|
+
properties: {
|
|
109
|
+
error: { type: 'string' },
|
|
110
|
+
message: { type: 'string' },
|
|
111
|
+
code: { type: 'string' }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
exitCodes: {
|
|
117
|
+
0: 'Success (2xx or 3xx status)',
|
|
118
|
+
1: 'Request error or 4xx/5xx status'
|
|
119
|
+
},
|
|
120
|
+
examples: [
|
|
121
|
+
{ command: 'mpx-api get https://api.example.com/users --output json', description: 'GET request with JSON output' },
|
|
122
|
+
{ command: 'mpx-api post https://api.example.com/users -j \'{"name":"John"}\' --output json', description: 'POST JSON data with structured output' },
|
|
123
|
+
{ command: 'mpx-api get https://api.example.com/data -H "Authorization: Bearer token"', description: 'GET with custom headers' }
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
collection: {
|
|
127
|
+
description: 'Manage and run request collections',
|
|
128
|
+
subcommands: {
|
|
129
|
+
init: {
|
|
130
|
+
usage: 'mpx-api collection init [options]',
|
|
131
|
+
description: 'Initialize a new collection in current directory',
|
|
132
|
+
flags: {
|
|
133
|
+
'-n, --name': {
|
|
134
|
+
type: 'string',
|
|
135
|
+
default: 'API Collection',
|
|
136
|
+
description: 'Collection name'
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
add: {
|
|
141
|
+
usage: 'mpx-api collection add <name> <method> <url> [options]',
|
|
142
|
+
description: 'Add a request to the collection',
|
|
143
|
+
arguments: {
|
|
144
|
+
name: { type: 'string', required: true, description: 'Request name' },
|
|
145
|
+
method: { type: 'string', required: true, description: 'HTTP method' },
|
|
146
|
+
url: { type: 'string', required: true, description: 'Request URL' }
|
|
147
|
+
},
|
|
148
|
+
flags: {
|
|
149
|
+
'-H, --header': { type: 'array', description: 'Request headers' },
|
|
150
|
+
'-j, --json': { type: 'string', description: 'JSON body' },
|
|
151
|
+
'-d, --data': { type: 'string', description: 'Request body' }
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
run: {
|
|
155
|
+
usage: 'mpx-api collection run [file] [options]',
|
|
156
|
+
description: 'Run a collection',
|
|
157
|
+
arguments: {
|
|
158
|
+
file: { type: 'string', required: false, description: 'Collection file (default: .mpx-api/collection.yaml)' }
|
|
159
|
+
},
|
|
160
|
+
flags: {
|
|
161
|
+
'-e, --env': { type: 'string', description: 'Environment name to use' },
|
|
162
|
+
'--base-url': { type: 'string', description: 'Override base URL' },
|
|
163
|
+
'--json': { type: 'boolean', description: 'Output results as JSON' }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
list: {
|
|
167
|
+
usage: 'mpx-api collection list [file]',
|
|
168
|
+
description: 'List requests in a collection'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
env: {
|
|
173
|
+
description: 'Manage environments',
|
|
174
|
+
subcommands: {
|
|
175
|
+
init: {
|
|
176
|
+
usage: 'mpx-api env init',
|
|
177
|
+
description: 'Initialize environments directory with dev, staging, production'
|
|
178
|
+
},
|
|
179
|
+
set: {
|
|
180
|
+
usage: 'mpx-api env set <environment> <variable>',
|
|
181
|
+
description: 'Set an environment variable (KEY=value format)',
|
|
182
|
+
arguments: {
|
|
183
|
+
environment: { type: 'string', required: true, description: 'Environment name' },
|
|
184
|
+
variable: { type: 'string', required: true, description: 'Variable in KEY=value format' }
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
list: {
|
|
188
|
+
usage: 'mpx-api env list [environment]',
|
|
189
|
+
description: 'List all environments or variables in a specific environment',
|
|
190
|
+
arguments: {
|
|
191
|
+
environment: { type: 'string', required: false, description: 'Environment name (optional)' }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
mock: {
|
|
197
|
+
description: 'Start a mock API server',
|
|
198
|
+
usage: 'mpx-api mock [file] [options]',
|
|
199
|
+
arguments: {
|
|
200
|
+
file: { type: 'string', required: false, description: 'Mock definition file' }
|
|
201
|
+
},
|
|
202
|
+
flags: {
|
|
203
|
+
'-p, --port': { type: 'number', default: 3000, description: 'Server port' },
|
|
204
|
+
'--watch': { type: 'boolean', description: 'Watch file for changes' }
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
test: {
|
|
208
|
+
description: 'Run API tests',
|
|
209
|
+
usage: 'mpx-api test <file> [options]',
|
|
210
|
+
arguments: {
|
|
211
|
+
file: { type: 'string', required: false, description: 'Test file to run (default: .mpx-api/collection.yaml)' }
|
|
212
|
+
},
|
|
213
|
+
flags: {
|
|
214
|
+
'-e, --env': { type: 'string', description: 'Environment name' },
|
|
215
|
+
'--json': { type: 'boolean', description: 'Output results as JSON' }
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
history: {
|
|
219
|
+
description: 'View request history',
|
|
220
|
+
usage: 'mpx-api history [options]',
|
|
221
|
+
flags: {
|
|
222
|
+
'-n, --limit': { type: 'number', default: 10, description: 'Number of entries to show' },
|
|
223
|
+
'--clear': { type: 'boolean', description: 'Clear history' }
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
load: {
|
|
227
|
+
description: 'Load test API endpoint (Pro)',
|
|
228
|
+
usage: 'mpx-api load <url> [options]',
|
|
229
|
+
arguments: {
|
|
230
|
+
url: { type: 'string', required: true, description: 'Target URL' }
|
|
231
|
+
},
|
|
232
|
+
flags: {
|
|
233
|
+
'--rps': { type: 'number', default: 10, description: 'Requests per second' },
|
|
234
|
+
'-d, --duration': { type: 'number', default: 10, description: 'Test duration in seconds' },
|
|
235
|
+
'-H, --header': { type: 'array', description: 'Add request headers' },
|
|
236
|
+
'--method': { type: 'string', default: 'GET', description: 'HTTP method' }
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
docs: {
|
|
240
|
+
description: 'Generate API documentation (Pro)',
|
|
241
|
+
usage: 'mpx-api docs <file> [options]',
|
|
242
|
+
arguments: {
|
|
243
|
+
file: { type: 'string', required: true, description: 'Collection or OpenAPI file' }
|
|
244
|
+
},
|
|
245
|
+
flags: {
|
|
246
|
+
'-o, --output': { type: 'string', description: 'Output directory' },
|
|
247
|
+
'--format': { type: 'string', enum: ['html', 'markdown'], default: 'html', description: 'Output format' }
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
mcp: {
|
|
251
|
+
description: 'Start MCP (Model Context Protocol) stdio server for AI agent integration',
|
|
252
|
+
usage: 'mpx-api mcp',
|
|
253
|
+
examples: [
|
|
254
|
+
{ command: 'mpx-api mcp', description: 'Start MCP stdio server' }
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
globalFlags: {
|
|
259
|
+
'-o, --output': {
|
|
260
|
+
type: 'string',
|
|
261
|
+
description: 'Output format: "json" for structured JSON for machine consumption'
|
|
262
|
+
},
|
|
263
|
+
'-q, --quiet': {
|
|
264
|
+
type: 'boolean',
|
|
265
|
+
default: false,
|
|
266
|
+
description: 'Suppress non-essential output'
|
|
267
|
+
},
|
|
268
|
+
'--schema': {
|
|
269
|
+
type: 'boolean',
|
|
270
|
+
default: false,
|
|
271
|
+
description: 'Output this schema as JSON'
|
|
272
|
+
},
|
|
273
|
+
'--version': {
|
|
274
|
+
type: 'boolean',
|
|
275
|
+
description: 'Show version number'
|
|
276
|
+
},
|
|
277
|
+
'--help': {
|
|
278
|
+
type: 'boolean',
|
|
279
|
+
description: 'Show help information'
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
mcpConfig: {
|
|
283
|
+
description: 'Add to your MCP client configuration to use mpx-api as an AI tool',
|
|
284
|
+
config: {
|
|
285
|
+
mcpServers: {
|
|
286
|
+
'mpx-api': {
|
|
287
|
+
command: 'npx',
|
|
288
|
+
args: ['mpx-api', 'mcp']
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|