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.
- package/LICENSE +21 -0
- package/README.md +428 -0
- package/dist/approver.d.ts +8 -0
- package/dist/approver.d.ts.map +1 -0
- package/dist/approver.js +90 -0
- package/dist/approver.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +148 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +123 -0
- package/dist/config.js.map +1 -0
- package/dist/differ.d.ts +11 -0
- package/dist/differ.d.ts.map +1 -0
- package/dist/differ.js +57 -0
- package/dist/differ.js.map +1 -0
- package/dist/fsUtil.d.ts +15 -0
- package/dist/fsUtil.d.ts.map +1 -0
- package/dist/fsUtil.js +70 -0
- package/dist/fsUtil.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/initializer.d.ts +10 -0
- package/dist/initializer.d.ts.map +1 -0
- package/dist/initializer.js +150 -0
- package/dist/initializer.js.map +1 -0
- package/dist/jsonPointer.d.ts +31 -0
- package/dist/jsonPointer.d.ts.map +1 -0
- package/dist/jsonPointer.js +116 -0
- package/dist/jsonPointer.js.map +1 -0
- package/dist/runner.d.ts +13 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +283 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# mcpdrift ๐งช
|
|
2
|
+
|
|
3
|
+
> Dependable CI/CD regression tester and snapshot assertion tool for Model Context Protocol (MCP) servers.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
|
|
8
|
+
`mcpdrift` prevents unintended breaking changes and response drift in MCP servers. It executes defined tool calls over `stdio` via the official `@modelcontextprotocol/sdk`, captures response snapshots, and fails your CI pipeline with clear, colorized line-by-line diffs when unexpected changes occur.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [What is mcpdrift?](#what-is-mcpdrift)
|
|
15
|
+
- [Important Concepts & Guarantees](#important-concepts--guarantees)
|
|
16
|
+
- [Quickstart (5-Minute Walkthrough)](#quickstart-5-minute-walkthrough)
|
|
17
|
+
- [Configuration Reference (`mcpdrift.config.json`)](#configuration-reference-mcpdriftconfigjson)
|
|
18
|
+
- [Multiple Test Cases Per Tool](#multiple-test-cases-per-tool)
|
|
19
|
+
- [Targeted Field Ignoring (RFC 6901 JSON Pointer)](#targeted-field-ignoring-rfc-6901-json-pointer)
|
|
20
|
+
- [CLI Command Reference](#cli-command-reference)
|
|
21
|
+
- [`mcpdrift run`](#mcpdrift-run)
|
|
22
|
+
- [`mcpdrift approve`](#mcpdrift-approve)
|
|
23
|
+
- [`mcpdrift init`](#mcpdrift-init)
|
|
24
|
+
- [CI/CD Integration](#cicd-integration)
|
|
25
|
+
- [Machine-Readable Reports](#machine-readable-reports)
|
|
26
|
+
- [Migration Guide: Single-Tool to Multi-Case IDs](#migration-guide-single-tool-to-multi-case-ids)
|
|
27
|
+
- [Known Limitations](#known-limitations)
|
|
28
|
+
- [License](#license)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## What is mcpdrift?
|
|
33
|
+
|
|
34
|
+
Model Context Protocol (MCP) servers expose tools to AI agents and LLMs. When developers refactor server code, update dependencies, or adjust prompt/response serialization, tool response schemas can inadvertently drift.
|
|
35
|
+
|
|
36
|
+
`mcpdrift` acts like Jest/Vitest snapshot testing, but specifically tailored for MCP servers:
|
|
37
|
+
1. Spawns your server as a child process via `serverCommand`.
|
|
38
|
+
2. Connects over `stdio` using `@modelcontextprotocol/sdk`.
|
|
39
|
+
3. Calls each configured tool with deterministic test inputs.
|
|
40
|
+
4. Compares responses against committed baseline snapshots under `.mcpdrift/snapshots/<case-id>.json`.
|
|
41
|
+
5. Exits `0` when identical, or exits `1` with a line-by-line diff if unexpected changes or errors occur.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Important Concepts & Guarantees
|
|
46
|
+
|
|
47
|
+
- **Differences do not automatically mean bugs**: A detected drift indicates the server response changed from the saved baseline. If the change was deliberate, review the diff and run `mcpdrift approve <case-id>` to accept it.
|
|
48
|
+
- **Passing snapshots do not prove correctness**: A snapshot only proves that the current output matches the baseline. If your baseline contained a defect, `mcpdrift` will preserve that defect until updated.
|
|
49
|
+
- **Dynamic data requires controlled fixtures or narrow ignore rules**: Fields like timestamps, UUIDs, trace tokens, or ephemeral counters will drift every run. Use targeted `ignore` rules (RFC 6901 JSON Pointers) or configure deterministic server test seeds.
|
|
50
|
+
- **MCP tools may have side effects**: Tools that delete files, invoke external APIs, or charge accounts will trigger those actions during testing. **Always point `mcpdrift` to local, sandboxed test servers or read-only mock environments.**
|
|
51
|
+
- **Approval executes tools again**: When you run `mcpdrift approve`, `mcpdrift` re-runs the tool against your server and overwrites the snapshot file with the newly returned output. If the tool fails or returns `isError: true`, approval is automatically aborted and your existing baseline is preserved.
|
|
52
|
+
- **CI checks block PRs by policy**: `mcpdrift run --ci` returns exit code `1` on drift, missing baselines, or tool failures. Configure your repository branch protection rules to require this check before merging.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
### A. Consumer Quickstart (For Your MCP Server Project)
|
|
59
|
+
|
|
60
|
+
In your MCP project directory:
|
|
61
|
+
|
|
62
|
+
#### 1. Installation
|
|
63
|
+
|
|
64
|
+
Run directly without installation, or add it to your project:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Run directly:
|
|
68
|
+
npx mcpdrift-cli --help
|
|
69
|
+
|
|
70
|
+
# Or install as a project devDependency:
|
|
71
|
+
npm install --save-dev mcpdrift-cli
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### 2. Initialize Configuration
|
|
75
|
+
Connect to your running MCP server to inspect its available tools. To prevent unintended execution of tools with side effects, explicit tool selection is required:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Explicitly select tools by name:
|
|
79
|
+
npx mcpdrift-cli init --server "node dist/server.js" --tools getWeather,searchDocs
|
|
80
|
+
|
|
81
|
+
# Or explicitly include all discovered tools:
|
|
82
|
+
npx mcpdrift-cli init --server "node dist/server.js" --all-tools
|
|
83
|
+
|
|
84
|
+
# Or generate a standalone starter template without connecting to a server:
|
|
85
|
+
npx mcpdrift-cli init --template
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This creates `mcpdrift.config.json` in your project root with inferred parameter placeholders.
|
|
89
|
+
|
|
90
|
+
#### 3. Review and Customize Inputs
|
|
91
|
+
Open `mcpdrift.config.json` and adjust inputs to provide deterministic, safe test values:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"serverCommand": "node dist/server.js",
|
|
96
|
+
"testCases": [
|
|
97
|
+
{
|
|
98
|
+
"id": "weather_addis",
|
|
99
|
+
"tool": "getWeather",
|
|
100
|
+
"input": { "city": "Addis Ababa" }
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "docs_pricing",
|
|
104
|
+
"tool": "searchDocs",
|
|
105
|
+
"input": { "query": "pricing" }
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### 4. Create Local Baseline Snapshots
|
|
112
|
+
Run tests in local development mode:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx mcpdrift-cli run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This creates baseline response snapshots under `.mcpdrift/snapshots/<case-id>.json`. Commit `.mcpdrift/snapshots/` to Git.
|
|
119
|
+
|
|
120
|
+
#### 5. Run Strict CI Checks
|
|
121
|
+
In CI pipelines (or with `--ci`), `mcpdrift` asserts that all snapshots exist and match:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx mcpdrift-cli run --ci
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### B. Contributor Quickstart (Within the mcpdrift Repository)
|
|
131
|
+
|
|
132
|
+
If you are developing or testing `mcpdrift` directly from source:
|
|
133
|
+
|
|
134
|
+
1. Clone the repository and build:
|
|
135
|
+
```bash
|
|
136
|
+
git clone <repo-url>
|
|
137
|
+
cd mcpdrift
|
|
138
|
+
npm ci
|
|
139
|
+
npm run build
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
2. Test against the bundled fixture server:
|
|
143
|
+
```bash
|
|
144
|
+
node dist/cli.js init --server "node examples/sample-server/server.js" --all-tools --output my-test.config.json
|
|
145
|
+
node dist/cli.js run -c my-test.config.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
3. Run the automated offline test suite:
|
|
149
|
+
```bash
|
|
150
|
+
npm test
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> **Network Access Note**:
|
|
154
|
+
> The automated test suite (`npm test`, covering diffing, pointer rules, config validation, safety, and CLI execution) is **100% offline and deterministic**; it never calls external APIs or npm registries. Installing `mcpdrift` as a consumer via `npm install` requires normal npm registry network access to fetch declared dependencies.
|
|
155
|
+
|
|
156
|
+
โจ All MCP regression checks passed!
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Configuration Reference (`mcpdrift.config.json`)
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
166
|
+
"serverCommand": "node dist/server.js",
|
|
167
|
+
"timeout": 15000,
|
|
168
|
+
"snapshotDir": ".mcpdrift/snapshots",
|
|
169
|
+
"env": {
|
|
170
|
+
"NODE_ENV": "test"
|
|
171
|
+
},
|
|
172
|
+
"testCases": [
|
|
173
|
+
{
|
|
174
|
+
"id": "weather_cold",
|
|
175
|
+
"tool": "getWeather",
|
|
176
|
+
"input": { "city": "Oslo" },
|
|
177
|
+
"timeout": 5000,
|
|
178
|
+
"ignore": [
|
|
179
|
+
"/meta/timestamp",
|
|
180
|
+
"/meta/traceId"
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"id": "weather_warm",
|
|
185
|
+
"tool": "getWeather",
|
|
186
|
+
"input": { "city": "Nairobi" }
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Multiple Test Cases Per Tool
|
|
193
|
+
|
|
194
|
+
You can test the same tool with varying inputs, edge cases, or error parameters. To avoid snapshot collisions, each test case must specify a unique, filesystem-safe `id`.
|
|
195
|
+
|
|
196
|
+
- Valid characters for `id`: alphanumeric, `_`, `-`, `.`.
|
|
197
|
+
- Path traversal sequences (`..`, `/`, `\`) and special characters (`< > : " | ? *`) are strictly prohibited and rejected by config validation.
|
|
198
|
+
|
|
199
|
+
### Targeted Field Ignoring (RFC 6901 JSON Pointer)
|
|
200
|
+
|
|
201
|
+
When responses contain dynamic values (e.g., timestamps, session IDs, tokens), specify an array of RFC 6901 JSON Pointers in `ignore`:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"id": "user_profile",
|
|
206
|
+
"tool": "getUser",
|
|
207
|
+
"input": { "userId": 42 },
|
|
208
|
+
"ignore": [
|
|
209
|
+
"/lastLoginAt",
|
|
210
|
+
"/session/token",
|
|
211
|
+
"/tags/0/createdAt"
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- **Standards-compliant**: Uses standard JSON Pointer syntax starting with `/`. Tokens with `/` are escaped as `~1`, and `~` as `~0`.
|
|
217
|
+
- **Immutable normalization**: `mcpdrift` normalizes copies of both baseline and actual responses without mutating the original output.
|
|
218
|
+
- **Array order preserved**: Array lengths and index positions are preserved.
|
|
219
|
+
- **Missing path safety**: If an ignored path does not exist in a given response, it is safely skipped without failing.
|
|
220
|
+
- **Meaningful changes still caught**: Any modification to unignored fields will immediately trigger a drift failure.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## CLI Command Reference
|
|
225
|
+
|
|
226
|
+
### `mcpdrift run`
|
|
227
|
+
|
|
228
|
+
Runs all configured test cases against the MCP server.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
mcpdrift run [options]
|
|
232
|
+
|
|
233
|
+
Options:
|
|
234
|
+
-c, --config <path> Path to config file (default: "mcpdrift.config.json")
|
|
235
|
+
--ci Strict CI mode: fails if any baseline is missing, never writes snapshots
|
|
236
|
+
--case <id> Run only a specific test case by ID
|
|
237
|
+
-t, --tool <name> Run all test cases configured for a specific tool
|
|
238
|
+
--report <path> Write a machine-readable JSON report
|
|
239
|
+
--json-report <path> Alias for --report
|
|
240
|
+
--timeout <ms> Override tool execution timeout in milliseconds
|
|
241
|
+
-h, --help Display command help
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### `mcpdrift approve`
|
|
245
|
+
|
|
246
|
+
Overwrites baseline snapshot(s) with the latest server response.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Approve a specific test case by ID:
|
|
250
|
+
mcpdrift approve weather_addis
|
|
251
|
+
|
|
252
|
+
# Approve only a specific test case explicitly:
|
|
253
|
+
mcpdrift approve --case weather_addis
|
|
254
|
+
|
|
255
|
+
# Approve all test cases for a specific tool:
|
|
256
|
+
mcpdrift approve --tool getWeather
|
|
257
|
+
|
|
258
|
+
# Approve all test cases in the entire suite:
|
|
259
|
+
mcpdrift approve --all
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
> **Safety Guarantee**: If the tool call throws an exception, times out, or returns `isError: true`, the approval operation is aborted and your existing baseline snapshot remains untouched.
|
|
263
|
+
|
|
264
|
+
### `mcpdrift init`
|
|
265
|
+
|
|
266
|
+
Inspects a running MCP server and generates a starter `mcpdrift.config.json`.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Inspect running server:
|
|
270
|
+
mcpdrift init --server "node dist/server.js"
|
|
271
|
+
|
|
272
|
+
# Filter to specific tools:
|
|
273
|
+
mcpdrift init --server "node dist/server.js" --tools ping,getWeather
|
|
274
|
+
|
|
275
|
+
# Overwrite existing config:
|
|
276
|
+
mcpdrift init --server "node dist/server.js" --force
|
|
277
|
+
|
|
278
|
+
# Generate standalone template without connecting to a server:
|
|
279
|
+
mcpdrift init --template
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
> **Discovery Safety**: `mcpdrift init` issues only `tools/list` over stdio and **never** calls `tools/call`. Inferred inputs are placeholders and must be reviewed.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## CI/CD Integration
|
|
287
|
+
|
|
288
|
+
`mcpdrift` is designed from the ground up for automated pipelines.
|
|
289
|
+
|
|
290
|
+
### Automatic CI Detection
|
|
291
|
+
|
|
292
|
+
`mcpdrift` automatically activates strict CI mode when:
|
|
293
|
+
- Passed the `--ci` CLI flag, OR
|
|
294
|
+
- The `CI=true` or `CI=1` environment variable is detected (standard in GitHub Actions, GitLab CI, CircleCI, Jenkins).
|
|
295
|
+
|
|
296
|
+
In strict CI mode:
|
|
297
|
+
- Any missing baseline snapshot causes an immediate failure (`exit code 1`).
|
|
298
|
+
- No snapshots are written to disk during the CI run.
|
|
299
|
+
- Changed responses or tool errors fail the build with line-by-line diffs.
|
|
300
|
+
|
|
301
|
+
### Example GitHub Actions Workflow
|
|
302
|
+
|
|
303
|
+
Create `.github/workflows/mcpdrift.yml`:
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
name: MCP Server Regression Tests
|
|
307
|
+
|
|
308
|
+
on:
|
|
309
|
+
pull_request:
|
|
310
|
+
branches: [main, master]
|
|
311
|
+
push:
|
|
312
|
+
branches: [main, master]
|
|
313
|
+
|
|
314
|
+
jobs:
|
|
315
|
+
test:
|
|
316
|
+
name: MCP Regression (${{ matrix.os }})
|
|
317
|
+
runs-on: ${{ matrix.os }}
|
|
318
|
+
strategy:
|
|
319
|
+
matrix:
|
|
320
|
+
os: [ubuntu-latest, windows-latest]
|
|
321
|
+
|
|
322
|
+
steps:
|
|
323
|
+
- name: Checkout repository
|
|
324
|
+
uses: actions/checkout@v4
|
|
325
|
+
|
|
326
|
+
- name: Setup Node.js
|
|
327
|
+
uses: actions/setup-node@v4
|
|
328
|
+
with:
|
|
329
|
+
node-version: '20'
|
|
330
|
+
cache: 'npm'
|
|
331
|
+
|
|
332
|
+
- name: Install dependencies
|
|
333
|
+
run: npm ci
|
|
334
|
+
|
|
335
|
+
- name: Build server
|
|
336
|
+
run: npm run build --if-present
|
|
337
|
+
|
|
338
|
+
- name: Run strict regression check
|
|
339
|
+
run: npx mcpdrift-cli run --ci
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Machine-Readable Reports
|
|
345
|
+
|
|
346
|
+
Generate machine-readable JSON reports for CI dashboard integration, test summarizers, or compliance logs:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
npx mcpdrift-cli run --report .mcpdrift/report.json
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
Sample JSON report output (clean JSON, no ANSI escape codes, no leaked secrets):
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"timestamp": "2026-09-12T18:35:00.000Z",
|
|
358
|
+
"serverCommand": "node dist/server.js",
|
|
359
|
+
"ci": true,
|
|
360
|
+
"summary": {
|
|
361
|
+
"total": 2,
|
|
362
|
+
"passed": 1,
|
|
363
|
+
"changed": 1,
|
|
364
|
+
"failed": 0,
|
|
365
|
+
"baselineCreated": 0,
|
|
366
|
+
"missingBaseline": 0,
|
|
367
|
+
"success": false
|
|
368
|
+
},
|
|
369
|
+
"results": [
|
|
370
|
+
{
|
|
371
|
+
"id": "weather_addis",
|
|
372
|
+
"tool": "getWeather",
|
|
373
|
+
"status": "PASS",
|
|
374
|
+
"durationMs": 12,
|
|
375
|
+
"snapshotPath": "C:\\repo\\.mcpdrift\\snapshots\\weather_addis.json"
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"id": "docs_pricing",
|
|
379
|
+
"tool": "searchDocs",
|
|
380
|
+
"status": "CHANGED",
|
|
381
|
+
"failureCategory": "SNAPSHOT_MISMATCH",
|
|
382
|
+
"durationMs": 15,
|
|
383
|
+
"snapshotPath": "C:\\repo\\.mcpdrift\\snapshots\\docs_pricing.json"
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Migration Guide: Single-Tool to Multi-Case IDs
|
|
392
|
+
|
|
393
|
+
In earlier versions of `mcpdrift`, test cases did not require an `id` property, and snapshots were saved as `.mcpdrift/snapshots/<tool-name>.json`.
|
|
394
|
+
|
|
395
|
+
### Backward Compatibility
|
|
396
|
+
If your configuration contains only **one** test case per tool name, `mcpdrift` maintains 100% backward compatibility:
|
|
397
|
+
- The test case automatically inherits `id: "<tool-name>"`.
|
|
398
|
+
- Existing snapshots at `.mcpdrift/snapshots/<tool-name>.json` will load and pass without change.
|
|
399
|
+
|
|
400
|
+
### Upgrading to Multiple Cases
|
|
401
|
+
When you want multiple test cases for the same tool:
|
|
402
|
+
1. Add an explicit, unique `id` to each test case in `mcpdrift.config.json`:
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"testCases": [
|
|
406
|
+
{ "id": "getWeather_city_1", "tool": "getWeather", "input": { "city": "Addis Ababa" } },
|
|
407
|
+
{ "id": "getWeather_city_2", "tool": "getWeather", "input": { "city": "Tokyo" } }
|
|
408
|
+
]
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
2. Rename your existing snapshot file to match the new `id`:
|
|
412
|
+
- e.g., rename `.mcpdrift/snapshots/getWeather.json` to `.mcpdrift/snapshots/getWeather_city_1.json`.
|
|
413
|
+
3. Run `npx mcpdrift-cli run` to generate the new baseline for `getWeather_city_2.json`.
|
|
414
|
+
4. Commit the new snapshots. `mcpdrift` never silently deletes or ambiguously migrates old snapshot files.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Known Limitations
|
|
419
|
+
|
|
420
|
+
1. **Stdio Only**: `mcpdrift` currently connects to MCP servers over standard I/O (`stdio`). SSE (Server-Sent Events) and custom HTTP transports are planned for future releases.
|
|
421
|
+
2. **Deterministic Inputs Required**: If your MCP server relies on external third-party live APIs that return uncontrolled live data, responses will drift unless mocked or covered by `ignore` rules.
|
|
422
|
+
3. **Re-execution on Approval**: `mcpdrift approve` re-executes the tool against the active server to capture the latest response rather than re-using the cached run failure.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## License
|
|
427
|
+
|
|
428
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approver.d.ts","sourceRoot":"","sources":["../src/approver.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwG9F"}
|
package/dist/approver.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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 } from './client.js';
|
|
6
|
+
import { formatJson } from './differ.js';
|
|
7
|
+
import { writeAtomic } from './fsUtil.js';
|
|
8
|
+
export async function approveTool(target, options = {}) {
|
|
9
|
+
const { config, resolvedPath } = loadConfig(options.config);
|
|
10
|
+
const snapshotDir = path.resolve(path.dirname(resolvedPath), config.snapshotDir || path.join('.mcpdrift', 'snapshots'));
|
|
11
|
+
if (!fs.existsSync(snapshotDir)) {
|
|
12
|
+
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
let casesToApprove = [];
|
|
15
|
+
if (options.all) {
|
|
16
|
+
casesToApprove = config.testCases;
|
|
17
|
+
}
|
|
18
|
+
else if (options.caseId) {
|
|
19
|
+
casesToApprove = config.testCases.filter((tc) => (tc.id || tc.tool) === options.caseId);
|
|
20
|
+
if (casesToApprove.length === 0) {
|
|
21
|
+
throw new Error(`Test case with ID "${options.caseId}" was not found in ${resolvedPath}.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (options.tool) {
|
|
25
|
+
casesToApprove = config.testCases.filter((tc) => tc.tool === options.tool);
|
|
26
|
+
if (casesToApprove.length === 0) {
|
|
27
|
+
throw new Error(`No test cases found for tool "${options.tool}" in ${resolvedPath}.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (target) {
|
|
31
|
+
// Target can match either case ID or tool name
|
|
32
|
+
const matchById = config.testCases.filter((tc) => (tc.id || tc.tool) === target);
|
|
33
|
+
if (matchById.length > 0) {
|
|
34
|
+
casesToApprove = matchById;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const matchByTool = config.testCases.filter((tc) => tc.tool === target);
|
|
38
|
+
if (matchByTool.length > 0) {
|
|
39
|
+
casesToApprove = matchByTool;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error(`Target "${target}" matched no test case ID or tool name in ${resolvedPath}.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error('Please specify a case ID or tool name to approve (e.g. `mcpdrift approve <case-id>`) or pass `--all`.');
|
|
48
|
+
}
|
|
49
|
+
console.log(chalk.bold(`\n๐ mcpdrift approve: Updating snapshots`));
|
|
50
|
+
console.log(chalk.dim(` Config: ${resolvedPath}`));
|
|
51
|
+
console.log(chalk.dim(` Server: ${config.serverCommand}`));
|
|
52
|
+
console.log(chalk.dim(` Cases to approve: ${casesToApprove.length}\n`));
|
|
53
|
+
const session = await createMcpSession(config.serverCommand, config.env, config.timeout || 15000);
|
|
54
|
+
try {
|
|
55
|
+
for (const testCase of casesToApprove) {
|
|
56
|
+
const caseId = testCase.id || testCase.tool;
|
|
57
|
+
const tool = testCase.tool;
|
|
58
|
+
const snapshotPath = getSnapshotPath(snapshotDir, caseId);
|
|
59
|
+
const testTimeout = testCase.timeout || config.timeout || 15000;
|
|
60
|
+
console.log(chalk.dim(`Calling tool "${tool}" for case "${caseId}" to capture latest response...`));
|
|
61
|
+
let latestResponse;
|
|
62
|
+
try {
|
|
63
|
+
latestResponse = await executeToolWithTimeout(session.client, tool, testCase.input || {}, testTimeout);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.error(chalk.red.bold(`\nโ Failed to approve "${caseId}": Tool threw an execution error.`));
|
|
67
|
+
console.error(chalk.red(` Error: ${err.message}`));
|
|
68
|
+
console.error(chalk.yellow(` Existing baseline snapshot was preserved: ${snapshotPath}\n`));
|
|
69
|
+
throw new Error(`Approval aborted: Tool "${tool}" threw an execution error.`);
|
|
70
|
+
}
|
|
71
|
+
// Check if tool returned error result (isError: true)
|
|
72
|
+
if (latestResponse !== null &&
|
|
73
|
+
typeof latestResponse === 'object' &&
|
|
74
|
+
latestResponse.isError === true) {
|
|
75
|
+
console.error(chalk.red.bold(`\nโ Failed to approve "${caseId}": Tool returned an error result (isError: true).`));
|
|
76
|
+
console.error(chalk.red(` Response:\n${formatJson(latestResponse)}`));
|
|
77
|
+
console.error(chalk.yellow(` Existing baseline snapshot was preserved: ${snapshotPath}\n`));
|
|
78
|
+
throw new Error(`Approval aborted: Tool "${tool}" returned an error result (isError: true).`);
|
|
79
|
+
}
|
|
80
|
+
// Write snapshot atomically
|
|
81
|
+
writeAtomic(snapshotPath, formatJson(latestResponse));
|
|
82
|
+
console.log(`${chalk.green.bold('โ
APPROVED:')} Snapshot for "${chalk.bold(caseId)}" updated at ${chalk.cyan(path.relative(process.cwd(), snapshotPath))}\n`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
await session.close();
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.green.bold('๐ All requested snapshots have been approved!'));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=approver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approver.js","sourceRoot":"","sources":["../src/approver.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { runTests } from './runner.js';
|
|
5
|
+
import { approveTool } from './approver.js';
|
|
6
|
+
import { initConfig } from './initializer.js';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('mcpdrift')
|
|
10
|
+
.description('CI/CD regression and snapshot tester for Model Context Protocol (MCP) servers')
|
|
11
|
+
.version('1.0.0');
|
|
12
|
+
program
|
|
13
|
+
.command('run')
|
|
14
|
+
.description('Run regression tests against the MCP server tools and compare with baseline snapshots')
|
|
15
|
+
.option('-c, --config <path>', 'path to mcpdrift.config.json')
|
|
16
|
+
.option('-t, --tool <name>', 'run tests only for a specific tool')
|
|
17
|
+
.option('--case <id>', 'run tests only for a specific test case ID')
|
|
18
|
+
.option('--ci', 'strict CI mode: fail if any baseline snapshot is missing, never write baselines')
|
|
19
|
+
.option('--report <path>', 'output machine-readable JSON report to the specified file path')
|
|
20
|
+
.option('--json-report <path>', 'alias for --report')
|
|
21
|
+
.option('--timeout <ms>', 'timeout in milliseconds per tool call', (val) => parseInt(val, 10))
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
const summary = await runTests({
|
|
25
|
+
config: options.config,
|
|
26
|
+
tool: options.tool,
|
|
27
|
+
caseId: options.case,
|
|
28
|
+
ci: options.ci,
|
|
29
|
+
report: options.report || options.jsonReport,
|
|
30
|
+
timeout: options.timeout,
|
|
31
|
+
});
|
|
32
|
+
if (!summary.success) {
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.error(chalk.red.bold(`\nโ Error: ${err.message}\n`));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
program
|
|
43
|
+
.command('approve [target]')
|
|
44
|
+
.description('Overwrite baseline snapshot(s) with the latest server response')
|
|
45
|
+
.option('-c, --config <path>', 'path to mcpdrift.config.json')
|
|
46
|
+
.option('--case <id>', 'approve only a specific test case ID')
|
|
47
|
+
.option('-t, --tool <name>', 'approve all test cases for a specific tool')
|
|
48
|
+
.option('-a, --all', 'approve all tools configured in mcpdrift.config.json')
|
|
49
|
+
.action(async (target, options) => {
|
|
50
|
+
try {
|
|
51
|
+
await approveTool(target, {
|
|
52
|
+
config: options.config,
|
|
53
|
+
caseId: options.case,
|
|
54
|
+
tool: options.tool,
|
|
55
|
+
all: options.all,
|
|
56
|
+
});
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error(chalk.red.bold(`\nโ Approve failed: ${err.message}\n`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
program
|
|
65
|
+
.command('init')
|
|
66
|
+
.description('Inspect a running MCP server and generate a starter mcpdrift.config.json')
|
|
67
|
+
.option('-s, --server <command>', 'server command to start MCP server (default: "node dist/server.js")')
|
|
68
|
+
.option('--tools <list>', 'comma-separated list of tools to include in config')
|
|
69
|
+
.option('--all-tools', 'explicitly include all discovered tools in generated test cases')
|
|
70
|
+
.option('--template', 'generate a starter template config without connecting to a server')
|
|
71
|
+
.option('-o, --output <path>', 'output config file path (default: "mcpdrift.config.json")')
|
|
72
|
+
.option('-f, --force', 'overwrite existing config file if present')
|
|
73
|
+
.action(async (options) => {
|
|
74
|
+
try {
|
|
75
|
+
const toolList = options.tools
|
|
76
|
+
? options.tools.split(',').map((t) => t.trim()).filter(Boolean)
|
|
77
|
+
: undefined;
|
|
78
|
+
await initConfig({
|
|
79
|
+
server: options.server,
|
|
80
|
+
tools: toolList,
|
|
81
|
+
allTools: options.allTools,
|
|
82
|
+
template: options.template,
|
|
83
|
+
output: options.output,
|
|
84
|
+
force: options.force,
|
|
85
|
+
});
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error(chalk.red.bold(`\nโ Init failed: ${err.message}\n`));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// If no subcommands provided, default to help
|
|
94
|
+
if (process.argv.length <= 2) {
|
|
95
|
+
program.help();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
99
|
+
console.error(chalk.red.bold(`Unexpected error: ${err.message}`));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=cli.js.map
|