mcpdrift-cli 1.0.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +428 -0
  3. package/dist/approver.d.ts +8 -0
  4. package/dist/approver.d.ts.map +1 -0
  5. package/dist/approver.js +90 -0
  6. package/dist/approver.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +103 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/client.d.ts +23 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +148 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/config.d.ts +12 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +123 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/differ.d.ts +11 -0
  20. package/dist/differ.d.ts.map +1 -0
  21. package/dist/differ.js +57 -0
  22. package/dist/differ.js.map +1 -0
  23. package/dist/fsUtil.d.ts +15 -0
  24. package/dist/fsUtil.d.ts.map +1 -0
  25. package/dist/fsUtil.js +70 -0
  26. package/dist/fsUtil.js.map +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +10 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/initializer.d.ts +10 -0
  32. package/dist/initializer.d.ts.map +1 -0
  33. package/dist/initializer.js +150 -0
  34. package/dist/initializer.js.map +1 -0
  35. package/dist/jsonPointer.d.ts +31 -0
  36. package/dist/jsonPointer.d.ts.map +1 -0
  37. package/dist/jsonPointer.js +116 -0
  38. package/dist/jsonPointer.js.map +1 -0
  39. package/dist/runner.d.ts +13 -0
  40. package/dist/runner.d.ts.map +1 -0
  41. package/dist/runner.js +283 -0
  42. package/dist/runner.js.map +1 -0
  43. package/dist/types.d.ts +66 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +2 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +58 -0
@@ -0,0 +1,150 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { createMcpSession } from './client.js';
5
+ import { DEFAULT_CONFIG_FILE, DEFAULT_SNAPSHOT_DIR } from './config.js';
6
+ import { writeAtomic } from './fsUtil.js';
7
+ function generateSampleInput(inputSchema) {
8
+ if (!inputSchema || typeof inputSchema !== 'object') {
9
+ return {};
10
+ }
11
+ const properties = inputSchema.properties;
12
+ if (!properties || typeof properties !== 'object') {
13
+ return {};
14
+ }
15
+ const sample = {};
16
+ for (const [propName, propDef] of Object.entries(properties)) {
17
+ if (!propDef || typeof propDef !== 'object') {
18
+ sample[propName] = 'sample';
19
+ continue;
20
+ }
21
+ if (propDef.default !== undefined) {
22
+ sample[propName] = propDef.default;
23
+ continue;
24
+ }
25
+ if (propDef.example !== undefined) {
26
+ sample[propName] = propDef.example;
27
+ continue;
28
+ }
29
+ if (Array.isArray(propDef.examples) && propDef.examples.length > 0) {
30
+ sample[propName] = propDef.examples[0];
31
+ continue;
32
+ }
33
+ switch (propDef.type) {
34
+ case 'string':
35
+ if (propDef.enum && Array.isArray(propDef.enum) && propDef.enum.length > 0) {
36
+ sample[propName] = propDef.enum[0];
37
+ }
38
+ else {
39
+ sample[propName] = `<string:${propName}>`;
40
+ }
41
+ break;
42
+ case 'number':
43
+ case 'integer':
44
+ sample[propName] = 0;
45
+ break;
46
+ case 'boolean':
47
+ sample[propName] = true;
48
+ break;
49
+ case 'array':
50
+ sample[propName] = [];
51
+ break;
52
+ case 'object':
53
+ sample[propName] = {};
54
+ break;
55
+ default:
56
+ sample[propName] = `<${propName}>`;
57
+ break;
58
+ }
59
+ }
60
+ return sample;
61
+ }
62
+ export async function initConfig(options = {}) {
63
+ const targetFile = path.resolve(process.cwd(), options.output || DEFAULT_CONFIG_FILE);
64
+ const serverCommand = options.server || 'node dist/server.js';
65
+ if (fs.existsSync(targetFile) && !options.force) {
66
+ throw new Error(`Configuration file already exists at: ${targetFile}\nUse --force to overwrite it.`);
67
+ }
68
+ console.log(chalk.bold(`\nšŸš€ Initializing mcpdrift configuration...`));
69
+ console.log(chalk.dim(` Server command: ${serverCommand}`));
70
+ console.log(chalk.dim(` Target path: ${targetFile}`));
71
+ let testCases = [];
72
+ if (options.template) {
73
+ console.log(chalk.dim(`Generating standalone starter template without server connection...`));
74
+ testCases = [
75
+ {
76
+ id: 'example_get_status',
77
+ tool: 'getStatus',
78
+ input: {},
79
+ },
80
+ {
81
+ id: 'example_search',
82
+ tool: 'search',
83
+ input: { query: 'documentation' },
84
+ ignore: ['/timestamp'],
85
+ },
86
+ ];
87
+ }
88
+ else {
89
+ console.log(chalk.dim(`Connecting to MCP server to inspect tools (tools/list)...`));
90
+ // Discover tools without ever calling tools/call
91
+ const session = await createMcpSession(serverCommand, undefined, 10000);
92
+ try {
93
+ const response = await session.client.listTools();
94
+ const discoveredTools = response.tools || [];
95
+ if (discoveredTools.length === 0) {
96
+ throw new Error(`Connected to MCP server, but server reported 0 available tools.\n` +
97
+ `Verify that your server registers tools before initializing mcpdrift.`);
98
+ }
99
+ if (!options.tools && !options.allTools) {
100
+ console.log(chalk.yellow(`\nāš ļø Explicit tool selection required to prevent unintended tool executions.`));
101
+ for (const tool of discoveredTools) {
102
+ console.log(chalk.dim(` - ${tool.name} ${tool.description ? `— ${tool.description}` : ''}`));
103
+ }
104
+ throw new Error(`Discovered ${discoveredTools.length} tool(s), but neither --tools nor --all-tools was specified.\n` +
105
+ `To avoid unintended tool side-effects, explicitly select tools:\n` +
106
+ ` • To configure specific tools: npx mcpdrift-cli init --tools <tool1,tool2,...>\n` +
107
+ ` • To include all discovered tools: npx mcpdrift-cli init --all-tools`);
108
+ }
109
+ const selectedToolNames = options.tools && options.tools.length > 0 ? options.tools : null;
110
+ for (const tool of discoveredTools) {
111
+ if (selectedToolNames && !selectedToolNames.includes(tool.name)) {
112
+ continue;
113
+ }
114
+ const sampleInput = generateSampleInput(tool.inputSchema);
115
+ console.log(chalk.dim(` - ${tool.name} ${tool.description ? `— ${tool.description}` : ''}`));
116
+ testCases.push({
117
+ id: tool.name,
118
+ tool: tool.name,
119
+ input: sampleInput,
120
+ });
121
+ }
122
+ if (testCases.length === 0 && selectedToolNames) {
123
+ throw new Error(`None of the requested tools [${selectedToolNames.join(', ')}] were found on the server. Available tools: ${discoveredTools.map((t) => t.name).join(', ')}`);
124
+ }
125
+ }
126
+ finally {
127
+ await session.close();
128
+ }
129
+ }
130
+ const configContent = {
131
+ serverCommand,
132
+ testCases,
133
+ };
134
+ writeAtomic(targetFile, JSON.stringify(configContent, null, 2) + '\n');
135
+ const snapshotDir = path.resolve(process.cwd(), DEFAULT_SNAPSHOT_DIR);
136
+ if (!fs.existsSync(snapshotDir)) {
137
+ fs.mkdirSync(snapshotDir, { recursive: true });
138
+ }
139
+ console.log(chalk.green.bold(`\nāœ… Generated ${path.basename(targetFile)} successfully!`));
140
+ console.log(chalk.cyan(` Configured ${testCases.length} test case(s).`));
141
+ console.log(chalk.yellow.bold(`\nāš ļø IMPORTANT NOTICE:`));
142
+ console.log(chalk.yellow(` The generated input values in ${path.basename(targetFile)} are inferred placeholders.\n` +
143
+ ` You MUST review and provide realistic, safe test inputs before executing 'mcpdrift run'.\n` +
144
+ ` Do NOT assume MCP tool names or annotations imply safety — tools may perform real side effects.`));
145
+ console.log(chalk.dim(`\nNext steps:`));
146
+ console.log(chalk.dim(` 1. Review and edit test inputs in ${path.basename(targetFile)}`));
147
+ console.log(chalk.dim(` 2. Run 'npx mcpdrift-cli run' locally to create baseline snapshots`));
148
+ console.log(chalk.dim(` 3. Commit .mcpdrift/snapshots/ to version control\n`));
149
+ }
150
+ //# sourceMappingURL=initializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initializer.js","sourceRoot":"","sources":["../src/initializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * RFC 6901 JSON Pointer implementation for targeted field ignoring.
3
+ */
4
+ /**
5
+ * Validates that a string is a valid JSON Pointer according to RFC 6901.
6
+ * Must start with '/' or be explicitly empty (referencing whole document, though not recommended for ignore).
7
+ */
8
+ export declare function validatePointer(pointer: string): void;
9
+ /**
10
+ * Unescapes an RFC 6901 JSON Pointer token:
11
+ * ~1 becomes /
12
+ * ~0 becomes ~
13
+ */
14
+ export declare function unescapeToken(token: string): string;
15
+ /**
16
+ * Parses an RFC 6901 JSON Pointer into an array of unescaped path tokens.
17
+ */
18
+ export declare function parseJsonPointer(pointer: string): string[];
19
+ /**
20
+ * Deep clones an object or array to ensure original data is never mutated.
21
+ */
22
+ export declare function deepClone<T>(value: T): T;
23
+ /**
24
+ * Applies an array of JSON Pointer ignore rules to a data object, returning a normalized copy
25
+ * with target fields removed or masked, preserving array order and length.
26
+ *
27
+ * Missing paths are gracefully ignored (no-op).
28
+ * Original input is never mutated.
29
+ */
30
+ export declare function applyIgnorePointers<T>(data: T, pointers?: string[]): T;
31
+ //# sourceMappingURL=jsonPointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonPointer.d.ts","sourceRoot":"","sources":["../src/jsonPointer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAqBrD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAK1D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAYxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,CAAC,CAa1E"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * RFC 6901 JSON Pointer implementation for targeted field ignoring.
3
+ */
4
+ /**
5
+ * Validates that a string is a valid JSON Pointer according to RFC 6901.
6
+ * Must start with '/' or be explicitly empty (referencing whole document, though not recommended for ignore).
7
+ */
8
+ export function validatePointer(pointer) {
9
+ if (typeof pointer !== 'string') {
10
+ throw new Error(`Invalid JSON Pointer: expected string, received ${typeof pointer}`);
11
+ }
12
+ if (pointer.trim() === '') {
13
+ throw new Error(`Invalid JSON Pointer: pointer cannot be empty. Must start with '/'`);
14
+ }
15
+ if (!pointer.startsWith('/')) {
16
+ throw new Error(`Invalid JSON Pointer "${pointer}": JSON Pointers must start with '/'. Example: "/timestamp" or "/data/0/id"`);
17
+ }
18
+ // Check for malformed escape sequences: a '~' not followed by '0' or '1'
19
+ if (/~(?![01])/.test(pointer)) {
20
+ throw new Error(`Invalid JSON Pointer "${pointer}": Malformed escape sequence. In RFC 6901, '~' must be followed by '0' (~0 for ~) or '1' (~1 for /).`);
21
+ }
22
+ }
23
+ /**
24
+ * Unescapes an RFC 6901 JSON Pointer token:
25
+ * ~1 becomes /
26
+ * ~0 becomes ~
27
+ */
28
+ export function unescapeToken(token) {
29
+ return token.replace(/~1/g, '/').replace(/~0/g, '~');
30
+ }
31
+ /**
32
+ * Parses an RFC 6901 JSON Pointer into an array of unescaped path tokens.
33
+ */
34
+ export function parseJsonPointer(pointer) {
35
+ validatePointer(pointer);
36
+ // Split on '/', ignoring the initial empty string before the leading '/'
37
+ const rawTokens = pointer.slice(1).split('/');
38
+ return rawTokens.map(unescapeToken);
39
+ }
40
+ /**
41
+ * Deep clones an object or array to ensure original data is never mutated.
42
+ */
43
+ export function deepClone(value) {
44
+ if (value === null || typeof value !== 'object') {
45
+ return value;
46
+ }
47
+ if (Array.isArray(value)) {
48
+ return value.map((item) => deepClone(item));
49
+ }
50
+ const clonedObj = {};
51
+ for (const [key, val] of Object.entries(value)) {
52
+ clonedObj[key] = deepClone(val);
53
+ }
54
+ return clonedObj;
55
+ }
56
+ /**
57
+ * Applies an array of JSON Pointer ignore rules to a data object, returning a normalized copy
58
+ * with target fields removed or masked, preserving array order and length.
59
+ *
60
+ * Missing paths are gracefully ignored (no-op).
61
+ * Original input is never mutated.
62
+ */
63
+ export function applyIgnorePointers(data, pointers = []) {
64
+ if (!pointers || pointers.length === 0 || data === null || typeof data !== 'object') {
65
+ return deepClone(data);
66
+ }
67
+ const clone = deepClone(data);
68
+ for (const pointer of pointers) {
69
+ const tokens = parseJsonPointer(pointer);
70
+ removePointerFromTarget(clone, tokens);
71
+ }
72
+ return clone;
73
+ }
74
+ function removePointerFromTarget(target, tokens) {
75
+ if (target === null || typeof target !== 'object' || tokens.length === 0) {
76
+ return;
77
+ }
78
+ let current = target;
79
+ for (let i = 0; i < tokens.length - 1; i++) {
80
+ const token = tokens[i];
81
+ if (Array.isArray(current)) {
82
+ const index = parseInt(token, 10);
83
+ if (isNaN(index) || index < 0 || index >= current.length) {
84
+ // Missing array path, safely no-op
85
+ return;
86
+ }
87
+ current = current[index];
88
+ }
89
+ else if (typeof current === 'object' && current !== null) {
90
+ if (!(token in current)) {
91
+ // Missing object key, safely no-op
92
+ return;
93
+ }
94
+ current = current[token];
95
+ }
96
+ else {
97
+ // Primitive reached before end of pointer, safely no-op
98
+ return;
99
+ }
100
+ }
101
+ const lastToken = tokens[tokens.length - 1];
102
+ if (Array.isArray(current)) {
103
+ const index = parseInt(lastToken, 10);
104
+ if (!isNaN(index) && index >= 0 && index < current.length) {
105
+ // To preserve array length and element order without shifting indices,
106
+ // replace the ignored element with a deterministic placeholder
107
+ current[index] = '<mcpdrift-ignored>';
108
+ }
109
+ }
110
+ else if (typeof current === 'object' && current !== null) {
111
+ if (lastToken in current) {
112
+ delete current[lastToken];
113
+ }
114
+ }
115
+ }
116
+ //# sourceMappingURL=jsonPointer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonPointer.js","sourceRoot":"","sources":["../src/jsonPointer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,mDAAmD,OAAO,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,yBAAyB,OAAO,6EAA6E,CAC9G,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,yBAAyB,OAAO,sGAAsG,CACvI,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,yEAAyE;IACzE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9C,OAAO,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAI,KAAQ;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAiB,CAAC;IAC9D,CAAC;IACD,MAAM,SAAS,GAAwB,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,SAAc,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAI,IAAO,EAAE,WAAqB,EAAE;IACrE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpF,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAE9B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACzC,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,uBAAuB,CAAC,MAAW,EAAE,MAAgB;IAC5D,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzE,OAAO;IACT,CAAC;IAED,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAExB,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACzD,mCAAmC;gBACnC,OAAO;YACT,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3D,IAAI,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,EAAE,CAAC;gBACxB,mCAAmC;gBACnC,OAAO;YACT,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,wDAAwD;YACxD,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE5C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1D,uEAAuE;YACvE,+DAA+D;YAC/D,OAAO,CAAC,KAAK,CAAC,GAAG,oBAAoB,CAAC;QACxC,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3D,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { RunSummary } from './types.js';
2
+ export interface RunOptions {
3
+ config?: string;
4
+ timeout?: number;
5
+ tool?: string;
6
+ caseId?: string;
7
+ ci?: boolean;
8
+ report?: string;
9
+ verbose?: boolean;
10
+ }
11
+ export declare function isCiEnvironment(explicitCi?: boolean): boolean;
12
+ export declare function runTests(options?: RunOptions): Promise<RunSummary>;
13
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,UAAU,EAIX,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAM7D;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CA0T5E"}
package/dist/runner.js ADDED
@@ -0,0 +1,283 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { loadConfig, getSnapshotPath } from './config.js';
5
+ import { createMcpSession, executeToolWithTimeout, McpClientError } from './client.js';
6
+ import { areResponsesEqual, formatJson, generateDiff } from './differ.js';
7
+ import { applyIgnorePointers } from './jsonPointer.js';
8
+ import { writeAtomic } from './fsUtil.js';
9
+ export function isCiEnvironment(explicitCi) {
10
+ if (explicitCi !== undefined) {
11
+ return explicitCi;
12
+ }
13
+ const envCi = process.env.CI;
14
+ return envCi === 'true' || envCi === '1';
15
+ }
16
+ export async function runTests(options = {}) {
17
+ const { config, resolvedPath } = loadConfig(options.config);
18
+ const isCi = isCiEnvironment(options.ci);
19
+ const snapshotDir = path.resolve(path.dirname(resolvedPath), config.snapshotDir || path.join('.mcpdrift', 'snapshots'));
20
+ if (!fs.existsSync(snapshotDir)) {
21
+ fs.mkdirSync(snapshotDir, { recursive: true });
22
+ }
23
+ console.log(chalk.bold(`\n🧪 mcpdrift: Starting MCP regression tests`));
24
+ console.log(chalk.dim(` Config: ${resolvedPath}`));
25
+ console.log(chalk.dim(` Server: ${config.serverCommand}`));
26
+ console.log(chalk.dim(` Snapshots: ${snapshotDir}`));
27
+ console.log(chalk.dim(` Mode: ${isCi ? chalk.yellow('CI (strict)') : 'Local development'}\n`));
28
+ let filterCases = config.testCases;
29
+ if (options.caseId) {
30
+ filterCases = filterCases.filter((tc) => tc.id === options.caseId);
31
+ if (filterCases.length === 0) {
32
+ throw new Error(`No test case found with ID "${options.caseId}" in ${resolvedPath}`);
33
+ }
34
+ }
35
+ if (options.tool) {
36
+ filterCases = filterCases.filter((tc) => tc.tool === options.tool);
37
+ if (filterCases.length === 0) {
38
+ throw new Error(`No test case found for tool "${options.tool}" in ${resolvedPath}`);
39
+ }
40
+ }
41
+ const results = [];
42
+ let session;
43
+ try {
44
+ session = await createMcpSession(config.serverCommand, config.env, options.timeout || config.timeout || 15000);
45
+ }
46
+ catch (err) {
47
+ console.error(chalk.red.bold(`\nāŒ Failed to initialize MCP server:`));
48
+ console.error(chalk.red(` ${err.message}\n`));
49
+ const failureCategory = err instanceof McpClientError ? err.category : 'STARTUP_FAILURE';
50
+ const failResults = filterCases.map((tc) => ({
51
+ id: tc.id || tc.tool,
52
+ tool: tc.tool,
53
+ status: 'FAILED',
54
+ failureCategory,
55
+ durationMs: 0,
56
+ error: `Server initialization failed: ${err.message}`,
57
+ }));
58
+ const summary = {
59
+ total: filterCases.length,
60
+ passed: 0,
61
+ changed: 0,
62
+ failed: filterCases.length,
63
+ baselineCreated: 0,
64
+ missingBaseline: 0,
65
+ results: failResults,
66
+ success: false,
67
+ };
68
+ if (options.report) {
69
+ writeJsonReport(options.report, summary, config.serverCommand, isCi);
70
+ }
71
+ return summary;
72
+ }
73
+ try {
74
+ for (const testCase of filterCases) {
75
+ const caseId = testCase.id || testCase.tool;
76
+ const toolName = testCase.tool;
77
+ const snapshotPath = getSnapshotPath(snapshotDir, caseId);
78
+ const testTimeout = testCase.timeout || options.timeout || config.timeout || 15000;
79
+ const startTime = performance.now();
80
+ let actualResponse;
81
+ let errorMsg;
82
+ let failureCategory;
83
+ try {
84
+ actualResponse = await executeToolWithTimeout(session.client, toolName, testCase.input || {}, testTimeout);
85
+ // Verify if tool returned isError: true
86
+ if (actualResponse !== null &&
87
+ typeof actualResponse === 'object' &&
88
+ actualResponse.isError === true) {
89
+ errorMsg = `Tool returned error result (isError: true):\n${formatJson(actualResponse)}`;
90
+ failureCategory = 'TOOL_ERROR';
91
+ }
92
+ }
93
+ catch (err) {
94
+ errorMsg = err.message || String(err);
95
+ failureCategory = err instanceof McpClientError ? err.category : 'TOOL_ERROR';
96
+ }
97
+ const durationMs = Math.round(performance.now() - startTime);
98
+ // Check if tool call threw or failed
99
+ if (errorMsg !== undefined) {
100
+ console.log(`${chalk.red.bold('āŒ FAILED:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
101
+ console.log(chalk.red(` Error: ${errorMsg}\n`));
102
+ results.push({
103
+ id: caseId,
104
+ tool: toolName,
105
+ status: 'FAILED',
106
+ failureCategory: failureCategory || 'TOOL_ERROR',
107
+ durationMs,
108
+ error: errorMsg,
109
+ snapshotPath,
110
+ ignoredPointers: testCase.ignore,
111
+ });
112
+ continue;
113
+ }
114
+ // Check baseline existence
115
+ const snapshotExists = fs.existsSync(snapshotPath);
116
+ if (!snapshotExists) {
117
+ if (isCi) {
118
+ // In CI mode, missing baselines are strictly forbidden and not created
119
+ console.log(`${chalk.red.bold('āŒ MISSING BASELINE:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
120
+ console.log(chalk.red(` Baseline snapshot missing: ${path.relative(process.cwd(), snapshotPath)}\n` +
121
+ ` In CI mode, missing baselines fail tests. Run mcpdrift locally to create and commit baselines.\n`));
122
+ results.push({
123
+ id: caseId,
124
+ tool: toolName,
125
+ status: 'MISSING_BASELINE',
126
+ failureCategory: 'MISSING_BASELINE',
127
+ durationMs,
128
+ actual: actualResponse,
129
+ snapshotPath,
130
+ ignoredPointers: testCase.ignore,
131
+ });
132
+ continue;
133
+ }
134
+ // In local mode, save current response as baseline atomically
135
+ writeAtomic(snapshotPath, formatJson(actualResponse));
136
+ console.log(`${chalk.blue.bold('šŸ†• BASELINE CREATED:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
137
+ console.log(chalk.dim(` Saved baseline to: ${path.relative(process.cwd(), snapshotPath)}\n`));
138
+ results.push({
139
+ id: caseId,
140
+ tool: toolName,
141
+ status: 'BASELINE_CREATED',
142
+ durationMs,
143
+ actual: actualResponse,
144
+ snapshotPath,
145
+ ignoredPointers: testCase.ignore,
146
+ });
147
+ continue;
148
+ }
149
+ // Snapshot exists: deep-compare with ignore rules
150
+ let baselineResponse;
151
+ try {
152
+ const rawBaseline = fs.readFileSync(snapshotPath, 'utf-8');
153
+ baselineResponse = JSON.parse(rawBaseline);
154
+ }
155
+ catch (err) {
156
+ console.log(`${chalk.red.bold('āŒ FAILED:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
157
+ console.log(chalk.red(` Corrupt or unreadable snapshot at ${snapshotPath}: ${err.message}\n`));
158
+ results.push({
159
+ id: caseId,
160
+ tool: toolName,
161
+ status: 'FAILED',
162
+ failureCategory: 'SNAPSHOT_READ_ERROR',
163
+ durationMs,
164
+ error: `Corrupt snapshot file: ${err.message}`,
165
+ snapshotPath,
166
+ ignoredPointers: testCase.ignore,
167
+ });
168
+ continue;
169
+ }
170
+ // Apply ignore rules to both baseline and actual
171
+ const cleanBaseline = applyIgnorePointers(baselineResponse, testCase.ignore || []);
172
+ const cleanActual = applyIgnorePointers(actualResponse, testCase.ignore || []);
173
+ const isMatch = areResponsesEqual(cleanBaseline, cleanActual);
174
+ if (isMatch) {
175
+ console.log(`${chalk.green.bold('āœ… PASS:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
176
+ results.push({
177
+ id: caseId,
178
+ tool: toolName,
179
+ status: 'PASS',
180
+ durationMs,
181
+ baseline: baselineResponse,
182
+ actual: actualResponse,
183
+ snapshotPath,
184
+ ignoredPointers: testCase.ignore,
185
+ });
186
+ }
187
+ else {
188
+ console.log(`${chalk.yellow.bold('āš ļø CHANGED:')} ${chalk.bold(caseId)} ${chalk.dim(`(tool: ${toolName}, ${durationMs}ms)`)}`);
189
+ const diffOutput = generateDiff(cleanBaseline, cleanActual, caseId, testCase.ignore);
190
+ console.log(diffOutput);
191
+ console.log(chalk.dim(` Run 'mcpdrift approve ${caseId}' to accept this change.\n`));
192
+ results.push({
193
+ id: caseId,
194
+ tool: toolName,
195
+ status: 'CHANGED',
196
+ failureCategory: 'SNAPSHOT_MISMATCH',
197
+ durationMs,
198
+ baseline: baselineResponse,
199
+ actual: actualResponse,
200
+ diff: diffOutput,
201
+ snapshotPath,
202
+ ignoredPointers: testCase.ignore,
203
+ });
204
+ }
205
+ }
206
+ }
207
+ finally {
208
+ await session.close();
209
+ }
210
+ // Summary statistics
211
+ const passed = results.filter((r) => r.status === 'PASS').length;
212
+ const changed = results.filter((r) => r.status === 'CHANGED').length;
213
+ const failed = results.filter((r) => r.status === 'FAILED').length;
214
+ const missingBaseline = results.filter((r) => r.status === 'MISSING_BASELINE').length;
215
+ const baselineCreated = results.filter((r) => r.status === 'BASELINE_CREATED').length;
216
+ const total = results.length;
217
+ const isSuccess = changed === 0 && failed === 0 && missingBaseline === 0;
218
+ console.log(chalk.bold('──────────────────────────────────────────────────'));
219
+ console.log(chalk.bold('Results: ') +
220
+ [
221
+ passed > 0 ? chalk.green.bold(`${passed} passed`) : null,
222
+ baselineCreated > 0 ? chalk.blue.bold(`${baselineCreated} baseline created`) : null,
223
+ changed > 0 ? chalk.yellow.bold(`${changed} changed`) : null,
224
+ failed > 0 ? chalk.red.bold(`${failed} failed`) : null,
225
+ missingBaseline > 0 ? chalk.red.bold(`${missingBaseline} missing baseline`) : null,
226
+ ]
227
+ .filter(Boolean)
228
+ .join(', ') +
229
+ chalk.dim(` (total: ${total})`));
230
+ if (!isSuccess) {
231
+ console.log(chalk.red.bold(`\nāš ļø Regression, missing baseline, or failure detected! CI check failed.`));
232
+ if (changed > 0) {
233
+ console.log(chalk.yellow(`To accept intentional snapshot changes, run:\n npx mcpdrift-cli approve <case-id>`));
234
+ }
235
+ }
236
+ else {
237
+ console.log(chalk.green.bold('\n✨ All MCP regression checks passed!'));
238
+ }
239
+ const summary = {
240
+ total,
241
+ passed,
242
+ changed,
243
+ failed,
244
+ baselineCreated,
245
+ missingBaseline,
246
+ results,
247
+ success: isSuccess,
248
+ };
249
+ if (options.report) {
250
+ writeJsonReport(options.report, summary, config.serverCommand, isCi);
251
+ }
252
+ return summary;
253
+ }
254
+ function writeJsonReport(reportPath, summary, serverCommand, ci) {
255
+ const report = {
256
+ timestamp: new Date().toISOString(),
257
+ serverCommand,
258
+ ci,
259
+ summary: {
260
+ total: summary.total,
261
+ passed: summary.passed,
262
+ changed: summary.changed,
263
+ failed: summary.failed,
264
+ baselineCreated: summary.baselineCreated,
265
+ missingBaseline: summary.missingBaseline,
266
+ success: summary.success,
267
+ },
268
+ results: summary.results.map((r) => ({
269
+ id: r.id,
270
+ tool: r.tool,
271
+ status: r.status,
272
+ failureCategory: r.failureCategory,
273
+ durationMs: r.durationMs,
274
+ error: r.error,
275
+ snapshotPath: r.snapshotPath,
276
+ ignoredPointers: r.ignoredPointers,
277
+ })),
278
+ };
279
+ const resolved = path.resolve(process.cwd(), reportPath);
280
+ writeAtomic(resolved, JSON.stringify(report, null, 2) + '\n');
281
+ console.log(chalk.dim(`šŸ“Š Machine-readable JSON report written to: ${resolved}`));
282
+ }
283
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}