mcpspec 1.0.3 → 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 +12 -6
- package/dist/index.js +280 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
```bash
|
|
25
25
|
mcpspec test ./collection.yaml # Run tests
|
|
26
26
|
mcpspec inspect "npx my-server" # Interactive REPL
|
|
27
|
-
mcpspec audit "npx my-server" # Security scan
|
|
27
|
+
mcpspec audit "npx my-server" # Security scan (8 rules)
|
|
28
28
|
mcpspec bench "npx my-server" # Performance benchmark
|
|
29
29
|
mcpspec score "npx my-server" # Quality rating (0-100)
|
|
30
30
|
mcpspec docs "npx my-server" # Auto-generate documentation
|
|
31
|
+
mcpspec record start "npx my-server" # Record & replay sessions
|
|
31
32
|
mcpspec ui # Launch web dashboard
|
|
32
33
|
```
|
|
33
34
|
|
|
@@ -50,9 +51,10 @@ mcpspec test
|
|
|
50
51
|
|---|---|---|
|
|
51
52
|
| **Test Collections** | YAML-based test suites with 10 assertion types, environments, variables, tags, retries, and parallel execution |
|
|
52
53
|
| **Interactive Inspector** | Connect to any MCP server and explore tools, resources, and schemas in a live REPL |
|
|
53
|
-
| **Security Audit** |
|
|
54
|
+
| **Security Audit** | 8 rules: path traversal, injection, auth bypass, resource exhaustion, info disclosure, **tool poisoning** (LLM prompt injection), and **excessive agency** (overly broad tools). Safety filter auto-skips destructive tools; `--dry-run` previews targets |
|
|
55
|
+
| **Recording & Replay** | Record inspector sessions, save them, and replay against the same or different server versions. Diff output highlights regressions — matched, changed, added, removed steps |
|
|
54
56
|
| **Benchmarks** | Measure min/max/mean/median/P95/P99 latency and throughput across hundreds of iterations |
|
|
55
|
-
| **MCP Score** | 0-100 quality rating across documentation, schema quality, error handling, responsiveness, and security |
|
|
57
|
+
| **MCP Score** | 0-100 quality rating across documentation, schema quality (opinionated linting: property types, descriptions, constraints, naming conventions), error handling, responsiveness, and security |
|
|
56
58
|
| **Doc Generator** | Auto-generate Markdown or HTML documentation from server introspection |
|
|
57
59
|
| **Web Dashboard** | Full React UI with server management, test runner, audit viewer, and dark mode |
|
|
58
60
|
| **CI/CD Ready** | JUnit/JSON/TAP reporters, deterministic exit codes, `--ci` mode, GitHub Actions compatible |
|
|
@@ -69,6 +71,10 @@ mcpspec test
|
|
|
69
71
|
| `mcpspec docs <server>` | Generate docs — `--format markdown\|html`, `--output <dir>` |
|
|
70
72
|
| `mcpspec compare` | Compare test runs or `--baseline <name>` |
|
|
71
73
|
| `mcpspec baseline save <name>` | Save/list baselines for regression detection |
|
|
74
|
+
| `mcpspec record start <server>` | Record an inspector session — `.call`, `.save`, `.steps` |
|
|
75
|
+
| `mcpspec record replay <name> <server>` | Replay a recording and diff against original |
|
|
76
|
+
| `mcpspec record list` | List saved recordings |
|
|
77
|
+
| `mcpspec record delete <name>` | Delete a saved recording |
|
|
72
78
|
| `mcpspec init [dir]` | Scaffold project — `--template minimal\|standard\|full` |
|
|
73
79
|
| `mcpspec ui` | Launch web dashboard on `localhost:6274` |
|
|
74
80
|
|
|
@@ -93,8 +99,8 @@ Pre-built test suites for popular MCP servers in [`examples/collections/servers/
|
|
|
93
99
|
| Package | Description |
|
|
94
100
|
|---------|-------------|
|
|
95
101
|
| `@mcpspec/shared` | Types, Zod schemas, constants |
|
|
96
|
-
| `@mcpspec/core` | MCP client, test runner, assertions, security scanner, profiler, doc generator, scorer |
|
|
97
|
-
| `@mcpspec/cli` |
|
|
102
|
+
| `@mcpspec/core` | MCP client, test runner, assertions, security scanner (8 rules), profiler, doc generator, scorer, recording/replay |
|
|
103
|
+
| `@mcpspec/cli` | 11 CLI commands built with Commander.js |
|
|
98
104
|
| `@mcpspec/server` | Hono HTTP server with REST API + WebSocket |
|
|
99
105
|
| `@mcpspec/ui` | React SPA — TanStack Router, TanStack Query, Tailwind, shadcn/ui |
|
|
100
106
|
|
|
@@ -104,7 +110,7 @@ Pre-built test suites for popular MCP servers in [`examples/collections/servers/
|
|
|
104
110
|
git clone https://github.com/light-handle/mcpspec.git
|
|
105
111
|
cd mcpspec
|
|
106
112
|
pnpm install && pnpm build
|
|
107
|
-
pnpm test #
|
|
113
|
+
pnpm test # 294 tests across core + server
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
## License
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12 } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync3 } from "fs";
|
|
6
6
|
import { dirname, join as join2 } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -1116,10 +1116,287 @@ ${COLORS5.bold} MCP Score${COLORS5.reset}`);
|
|
|
1116
1116
|
}
|
|
1117
1117
|
});
|
|
1118
1118
|
|
|
1119
|
+
// src/commands/record.ts
|
|
1120
|
+
import { Command as Command11 } from "commander";
|
|
1121
|
+
import { createInterface as createInterface2 } from "readline";
|
|
1122
|
+
import { randomUUID } from "crypto";
|
|
1123
|
+
import { EXIT_CODES as EXIT_CODES10 } from "@mcpspec/shared";
|
|
1124
|
+
import {
|
|
1125
|
+
MCPClient as MCPClient6,
|
|
1126
|
+
RecordingStore,
|
|
1127
|
+
RecordingReplayer,
|
|
1128
|
+
RecordingDiffer,
|
|
1129
|
+
formatError as formatError7
|
|
1130
|
+
} from "@mcpspec/core";
|
|
1131
|
+
var COLORS6 = {
|
|
1132
|
+
reset: "\x1B[0m",
|
|
1133
|
+
green: "\x1B[32m",
|
|
1134
|
+
red: "\x1B[31m",
|
|
1135
|
+
yellow: "\x1B[33m",
|
|
1136
|
+
gray: "\x1B[90m",
|
|
1137
|
+
bold: "\x1B[1m",
|
|
1138
|
+
cyan: "\x1B[36m",
|
|
1139
|
+
blue: "\x1B[34m"
|
|
1140
|
+
};
|
|
1141
|
+
var recordCommand = new Command11("record").description("Record, replay, and manage inspector session recordings");
|
|
1142
|
+
recordCommand.command("start").description("Start a recording session (interactive REPL)").argument("<server>", 'Server command (e.g., "npx @modelcontextprotocol/server-filesystem /tmp")').action(async (serverCommand) => {
|
|
1143
|
+
let client = null;
|
|
1144
|
+
const store = new RecordingStore();
|
|
1145
|
+
const steps = [];
|
|
1146
|
+
let toolList = [];
|
|
1147
|
+
try {
|
|
1148
|
+
client = new MCPClient6({ serverConfig: serverCommand });
|
|
1149
|
+
console.log(`${COLORS6.cyan}Connecting to: ${COLORS6.reset}${serverCommand}`);
|
|
1150
|
+
await client.connect();
|
|
1151
|
+
const info = client.getServerInfo();
|
|
1152
|
+
const serverName = info?.name ?? "unknown";
|
|
1153
|
+
console.log(`${COLORS6.green}Connected to ${serverName}${COLORS6.reset}`);
|
|
1154
|
+
const tools = await client.listTools();
|
|
1155
|
+
toolList = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
1156
|
+
console.log(`${COLORS6.gray}${tools.length} tools available${COLORS6.reset}`);
|
|
1157
|
+
console.log(`
|
|
1158
|
+
${COLORS6.bold}Recording mode.${COLORS6.reset} Type ${COLORS6.bold}.help${COLORS6.reset} for commands.
|
|
1159
|
+
`);
|
|
1160
|
+
const rl = createInterface2({
|
|
1161
|
+
input: process.stdin,
|
|
1162
|
+
output: process.stdout,
|
|
1163
|
+
prompt: `${COLORS6.red}rec>${COLORS6.reset} `
|
|
1164
|
+
});
|
|
1165
|
+
rl.prompt();
|
|
1166
|
+
rl.on("line", async (line) => {
|
|
1167
|
+
const trimmed = line.trim();
|
|
1168
|
+
if (!trimmed) {
|
|
1169
|
+
rl.prompt();
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
try {
|
|
1173
|
+
if (trimmed === ".exit" || trimmed === ".quit") {
|
|
1174
|
+
if (steps.length > 0) {
|
|
1175
|
+
console.log(`${COLORS6.yellow}Warning: ${steps.length} unsaved step(s). Use .save <name> first, or .exit to discard.${COLORS6.reset}`);
|
|
1176
|
+
if (trimmed === ".exit") {
|
|
1177
|
+
await client?.disconnect();
|
|
1178
|
+
rl.close();
|
|
1179
|
+
process.exit(EXIT_CODES10.SUCCESS);
|
|
1180
|
+
}
|
|
1181
|
+
} else {
|
|
1182
|
+
await client?.disconnect();
|
|
1183
|
+
rl.close();
|
|
1184
|
+
process.exit(EXIT_CODES10.SUCCESS);
|
|
1185
|
+
}
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
if (trimmed === ".help") {
|
|
1189
|
+
console.log(`
|
|
1190
|
+
${COLORS6.bold}Recording commands:${COLORS6.reset}
|
|
1191
|
+
.tools List available tools
|
|
1192
|
+
.call <tool> <json> Call a tool and record the result
|
|
1193
|
+
.steps List recorded steps
|
|
1194
|
+
.save <name> Save recording with given name
|
|
1195
|
+
.exit Disconnect and exit
|
|
1196
|
+
`);
|
|
1197
|
+
rl.prompt();
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (trimmed === ".tools") {
|
|
1201
|
+
if (toolList.length === 0) {
|
|
1202
|
+
console.log(`${COLORS6.gray}No tools available${COLORS6.reset}`);
|
|
1203
|
+
} else {
|
|
1204
|
+
console.log(`
|
|
1205
|
+
${COLORS6.bold}Tools (${toolList.length}):${COLORS6.reset}`);
|
|
1206
|
+
for (const tool of toolList) {
|
|
1207
|
+
console.log(` ${COLORS6.green}${tool.name}${COLORS6.reset}`);
|
|
1208
|
+
if (tool.description) console.log(` ${COLORS6.gray}${tool.description}${COLORS6.reset}`);
|
|
1209
|
+
}
|
|
1210
|
+
console.log("");
|
|
1211
|
+
}
|
|
1212
|
+
rl.prompt();
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
if (trimmed === ".steps") {
|
|
1216
|
+
if (steps.length === 0) {
|
|
1217
|
+
console.log(`${COLORS6.gray}No steps recorded yet${COLORS6.reset}`);
|
|
1218
|
+
} else {
|
|
1219
|
+
console.log(`
|
|
1220
|
+
${COLORS6.bold}Recorded steps (${steps.length}):${COLORS6.reset}`);
|
|
1221
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1222
|
+
const s = steps[i];
|
|
1223
|
+
const status = s.isError ? `${COLORS6.red}ERROR${COLORS6.reset}` : `${COLORS6.green}OK${COLORS6.reset}`;
|
|
1224
|
+
console.log(` ${i + 1}. ${s.tool} ${COLORS6.gray}${JSON.stringify(s.input)}${COLORS6.reset} [${status}] ${COLORS6.gray}${s.durationMs}ms${COLORS6.reset}`);
|
|
1225
|
+
}
|
|
1226
|
+
console.log("");
|
|
1227
|
+
}
|
|
1228
|
+
rl.prompt();
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (trimmed.startsWith(".save ")) {
|
|
1232
|
+
const name = trimmed.slice(6).trim();
|
|
1233
|
+
if (!name) {
|
|
1234
|
+
console.log(`${COLORS6.red}Usage: .save <name>${COLORS6.reset}`);
|
|
1235
|
+
rl.prompt();
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (steps.length === 0) {
|
|
1239
|
+
console.log(`${COLORS6.yellow}No steps to save. Use .call first.${COLORS6.reset}`);
|
|
1240
|
+
rl.prompt();
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
const recording = {
|
|
1244
|
+
id: randomUUID(),
|
|
1245
|
+
name,
|
|
1246
|
+
serverName: info?.name,
|
|
1247
|
+
tools: toolList,
|
|
1248
|
+
steps: [...steps],
|
|
1249
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1250
|
+
};
|
|
1251
|
+
const path = store.save(name, recording);
|
|
1252
|
+
console.log(`${COLORS6.green}Saved recording "${name}" (${steps.length} steps) to ${path}${COLORS6.reset}`);
|
|
1253
|
+
rl.prompt();
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
if (trimmed.startsWith(".call ")) {
|
|
1257
|
+
const rest = trimmed.slice(6).trim();
|
|
1258
|
+
const spaceIdx = rest.indexOf(" ");
|
|
1259
|
+
let toolName;
|
|
1260
|
+
let args = {};
|
|
1261
|
+
if (spaceIdx === -1) {
|
|
1262
|
+
toolName = rest;
|
|
1263
|
+
} else {
|
|
1264
|
+
toolName = rest.slice(0, spaceIdx);
|
|
1265
|
+
const jsonStr = rest.slice(spaceIdx + 1).trim();
|
|
1266
|
+
try {
|
|
1267
|
+
args = JSON.parse(jsonStr);
|
|
1268
|
+
} catch {
|
|
1269
|
+
console.log(`${COLORS6.red}Invalid JSON: ${jsonStr}${COLORS6.reset}`);
|
|
1270
|
+
rl.prompt();
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
console.log(`${COLORS6.gray}Calling ${toolName}...${COLORS6.reset}`);
|
|
1275
|
+
const start = performance.now();
|
|
1276
|
+
let output = [];
|
|
1277
|
+
let isError = false;
|
|
1278
|
+
try {
|
|
1279
|
+
const result = await client.callTool(toolName, args);
|
|
1280
|
+
output = result.content;
|
|
1281
|
+
isError = result.isError === true;
|
|
1282
|
+
} catch (err) {
|
|
1283
|
+
output = [{ type: "text", text: err instanceof Error ? err.message : String(err) }];
|
|
1284
|
+
isError = true;
|
|
1285
|
+
}
|
|
1286
|
+
const durationMs = Math.round(performance.now() - start);
|
|
1287
|
+
steps.push({ tool: toolName, input: args, output, isError, durationMs });
|
|
1288
|
+
const statusLabel = isError ? `${COLORS6.red}ERROR${COLORS6.reset}` : `${COLORS6.green}OK${COLORS6.reset}`;
|
|
1289
|
+
console.log(`[${statusLabel}] ${COLORS6.gray}${durationMs}ms${COLORS6.reset} (step ${steps.length})`);
|
|
1290
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1291
|
+
rl.prompt();
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
console.log(`${COLORS6.yellow}Unknown command. Type .help for available commands.${COLORS6.reset}`);
|
|
1295
|
+
} catch (err) {
|
|
1296
|
+
const formatted = formatError7(err);
|
|
1297
|
+
console.log(`${COLORS6.red}${formatted.title}: ${formatted.description}${COLORS6.reset}`);
|
|
1298
|
+
}
|
|
1299
|
+
rl.prompt();
|
|
1300
|
+
});
|
|
1301
|
+
rl.on("close", async () => {
|
|
1302
|
+
await client?.disconnect();
|
|
1303
|
+
process.exit(EXIT_CODES10.SUCCESS);
|
|
1304
|
+
});
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
const formatted = formatError7(err);
|
|
1307
|
+
console.error(`
|
|
1308
|
+
${formatted.title}: ${formatted.description}`);
|
|
1309
|
+
formatted.suggestions.forEach((s) => console.error(` - ${s}`));
|
|
1310
|
+
await client?.disconnect();
|
|
1311
|
+
process.exit(formatted.exitCode);
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
recordCommand.command("list").description("List saved recordings").action(() => {
|
|
1315
|
+
const store = new RecordingStore();
|
|
1316
|
+
const recordings = store.list();
|
|
1317
|
+
if (recordings.length === 0) {
|
|
1318
|
+
console.log(`${COLORS6.gray}No recordings found.${COLORS6.reset}`);
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
console.log(`
|
|
1322
|
+
${COLORS6.bold}Saved recordings (${recordings.length}):${COLORS6.reset}`);
|
|
1323
|
+
for (const name of recordings) {
|
|
1324
|
+
const recording = store.load(name);
|
|
1325
|
+
if (recording) {
|
|
1326
|
+
console.log(` ${COLORS6.green}${name}${COLORS6.reset} ${COLORS6.gray}(${recording.steps.length} steps, ${recording.createdAt})${COLORS6.reset}`);
|
|
1327
|
+
} else {
|
|
1328
|
+
console.log(` ${COLORS6.green}${name}${COLORS6.reset}`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
console.log("");
|
|
1332
|
+
});
|
|
1333
|
+
recordCommand.command("replay").description("Replay a recording against a server and show diff").argument("<name>", "Recording name").argument("<server>", "Server command").action(async (name, serverCommand) => {
|
|
1334
|
+
const store = new RecordingStore();
|
|
1335
|
+
const recording = store.load(name);
|
|
1336
|
+
if (!recording) {
|
|
1337
|
+
console.error(`${COLORS6.red}Recording "${name}" not found.${COLORS6.reset}`);
|
|
1338
|
+
process.exit(EXIT_CODES10.ERROR);
|
|
1339
|
+
}
|
|
1340
|
+
let client = null;
|
|
1341
|
+
try {
|
|
1342
|
+
client = new MCPClient6({ serverConfig: serverCommand });
|
|
1343
|
+
console.log(`${COLORS6.cyan}Connecting to: ${COLORS6.reset}${serverCommand}`);
|
|
1344
|
+
await client.connect();
|
|
1345
|
+
console.log(`${COLORS6.green}Connected. Replaying ${recording.steps.length} steps...${COLORS6.reset}
|
|
1346
|
+
`);
|
|
1347
|
+
const replayer = new RecordingReplayer();
|
|
1348
|
+
const result = await replayer.replay(recording, client, {
|
|
1349
|
+
onStepStart: (i, step) => {
|
|
1350
|
+
process.stdout.write(` ${i + 1}/${recording.steps.length} ${step.tool}... `);
|
|
1351
|
+
},
|
|
1352
|
+
onStepComplete: (_i, replayed) => {
|
|
1353
|
+
const status = replayed.isError ? `${COLORS6.red}ERROR${COLORS6.reset}` : `${COLORS6.green}OK${COLORS6.reset}`;
|
|
1354
|
+
console.log(`[${status}] ${COLORS6.gray}${replayed.durationMs}ms${COLORS6.reset}`);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
const differ = new RecordingDiffer();
|
|
1358
|
+
const diff = differ.diff(recording, result.replayedSteps, result.replayedAt);
|
|
1359
|
+
console.log(`
|
|
1360
|
+
${COLORS6.bold}Diff Summary:${COLORS6.reset}`);
|
|
1361
|
+
console.log(` ${COLORS6.green}Matched:${COLORS6.reset} ${diff.summary.matched}`);
|
|
1362
|
+
console.log(` ${COLORS6.yellow}Changed:${COLORS6.reset} ${diff.summary.changed}`);
|
|
1363
|
+
console.log(` ${COLORS6.blue}Added:${COLORS6.reset} ${diff.summary.added}`);
|
|
1364
|
+
console.log(` ${COLORS6.red}Removed:${COLORS6.reset} ${diff.summary.removed}`);
|
|
1365
|
+
if (diff.summary.changed > 0) {
|
|
1366
|
+
console.log(`
|
|
1367
|
+
${COLORS6.bold}Changed steps:${COLORS6.reset}`);
|
|
1368
|
+
for (const step of diff.steps) {
|
|
1369
|
+
if (step.type === "changed") {
|
|
1370
|
+
console.log(` Step ${step.index + 1} (${step.tool}): ${COLORS6.yellow}${step.outputDiff}${COLORS6.reset}`);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
await client.disconnect();
|
|
1375
|
+
const exitCode = diff.summary.changed > 0 || diff.summary.removed > 0 ? EXIT_CODES10.TEST_FAILURE : EXIT_CODES10.SUCCESS;
|
|
1376
|
+
process.exit(exitCode);
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
const formatted = formatError7(err);
|
|
1379
|
+
console.error(`
|
|
1380
|
+
${formatted.title}: ${formatted.description}`);
|
|
1381
|
+
formatted.suggestions.forEach((s) => console.error(` - ${s}`));
|
|
1382
|
+
await client?.disconnect();
|
|
1383
|
+
process.exit(formatted.exitCode);
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
recordCommand.command("delete").description("Delete a saved recording").argument("<name>", "Recording name").action((name) => {
|
|
1387
|
+
const store = new RecordingStore();
|
|
1388
|
+
if (store.delete(name)) {
|
|
1389
|
+
console.log(`${COLORS6.green}Deleted recording "${name}".${COLORS6.reset}`);
|
|
1390
|
+
} else {
|
|
1391
|
+
console.error(`${COLORS6.red}Recording "${name}" not found.${COLORS6.reset}`);
|
|
1392
|
+
process.exit(EXIT_CODES10.ERROR);
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1119
1396
|
// src/index.ts
|
|
1120
1397
|
var __cliDir = dirname(fileURLToPath(import.meta.url));
|
|
1121
1398
|
var pkg = JSON.parse(readFileSync3(join2(__cliDir, "..", "package.json"), "utf-8"));
|
|
1122
|
-
var program = new
|
|
1399
|
+
var program = new Command12();
|
|
1123
1400
|
program.name("mcpspec").description("The definitive MCP server testing platform").version(pkg.version);
|
|
1124
1401
|
program.addCommand(testCommand);
|
|
1125
1402
|
program.addCommand(inspectCommand);
|
|
@@ -1131,4 +1408,5 @@ program.addCommand(auditCommand);
|
|
|
1131
1408
|
program.addCommand(benchCommand);
|
|
1132
1409
|
program.addCommand(docsCommand);
|
|
1133
1410
|
program.addCommand(scoreCommand);
|
|
1411
|
+
program.addCommand(recordCommand);
|
|
1134
1412
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpspec",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "The definitive MCP server testing platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"@inquirer/prompts": "^7.0.0",
|
|
30
30
|
"commander": "^12.1.0",
|
|
31
31
|
"open": "^10.1.0",
|
|
32
|
-
"@mcpspec/core": "1.0
|
|
33
|
-
"@mcpspec/shared": "1.0
|
|
34
|
-
"@mcpspec/server": "1.0
|
|
32
|
+
"@mcpspec/core": "1.1.0",
|
|
33
|
+
"@mcpspec/shared": "1.1.0",
|
|
34
|
+
"@mcpspec/server": "1.1.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"tsup": "^8.0.0",
|