@k08200/mcp-probe 1.6.0 → 1.10.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 (57) hide show
  1. package/README.md +120 -507
  2. package/dist/assertions.d.ts.map +1 -1
  3. package/dist/assertions.js +93 -0
  4. package/dist/assertions.js.map +1 -1
  5. package/dist/checker.d.ts.map +1 -1
  6. package/dist/checker.js +54 -56
  7. package/dist/checker.js.map +1 -1
  8. package/dist/cli.js +52 -10
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +30 -1
  12. package/dist/config.js.map +1 -1
  13. package/dist/doctor.d.ts +5 -0
  14. package/dist/doctor.d.ts.map +1 -1
  15. package/dist/doctor.js +258 -31
  16. package/dist/doctor.js.map +1 -1
  17. package/dist/exit-code.d.ts +3 -0
  18. package/dist/exit-code.d.ts.map +1 -0
  19. package/dist/exit-code.js +8 -0
  20. package/dist/exit-code.js.map +1 -0
  21. package/dist/init.d.ts.map +1 -1
  22. package/dist/init.js +1 -118
  23. package/dist/init.js.map +1 -1
  24. package/dist/issues.d.ts.map +1 -1
  25. package/dist/issues.js +33 -14
  26. package/dist/issues.js.map +1 -1
  27. package/dist/protocols/mcp-client.d.ts.map +1 -1
  28. package/dist/protocols/mcp-client.js +36 -17
  29. package/dist/protocols/mcp-client.js.map +1 -1
  30. package/dist/reporters/receipt.d.ts +16 -0
  31. package/dist/reporters/receipt.d.ts.map +1 -0
  32. package/dist/reporters/receipt.js +21 -0
  33. package/dist/reporters/receipt.js.map +1 -0
  34. package/dist/scaffold.d.ts +17 -0
  35. package/dist/scaffold.d.ts.map +1 -0
  36. package/dist/scaffold.js +152 -0
  37. package/dist/scaffold.js.map +1 -0
  38. package/dist/sidecar.d.ts +6 -0
  39. package/dist/sidecar.d.ts.map +1 -0
  40. package/dist/sidecar.js +79 -0
  41. package/dist/sidecar.js.map +1 -0
  42. package/dist/types.d.ts +8 -1
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/version.d.ts +2 -0
  45. package/dist/version.d.ts.map +1 -0
  46. package/dist/version.js +5 -0
  47. package/dist/version.js.map +1 -0
  48. package/examples/fixtures/stdio-mcp-server.js +68 -0
  49. package/examples/github-actions/fleet.yml +9 -1
  50. package/examples/github-actions/remote-server.yml +9 -1
  51. package/examples/github-actions/single-server.yml +9 -1
  52. package/examples/self-check.config.json +3 -1
  53. package/examples/self-check.strict.config.json +16 -0
  54. package/examples/self-check.strict.tools.json +49 -0
  55. package/package.json +1 -1
  56. package/schemas/mcp-probe.config.schema.json +15 -0
  57. package/schemas/mcp-probe.sidecar.schema.json +5 -1
package/README.md CHANGED
@@ -3,275 +3,96 @@
3
3
  [![CI](https://github.com/k08200/mcp-probe/actions/workflows/ci.yml/badge.svg)](https://github.com/k08200/mcp-probe/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/@k08200/mcp-probe)](https://www.npmjs.com/package/@k08200/mcp-probe)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
- [![Node.js](https://img.shields.io/node/v/@k08200/mcp-probe)](package.json)
7
6
 
8
- **CI readiness gate for MCP servers.** Validates protocol handshake, discovery, optional tool-call dry-runs, stderr noise, and response latency in one command.
7
+ **CI readiness gate for MCP servers.**
9
8
 
10
- The `npm audit` for the [MCP](https://modelcontextprotocol.io) ecosystem — because an MCP server can start, pass `tools/list`, and still fail every real tool call when auth handoff, browser OAuth, or downstream permissions are broken.
9
+ `tools/list` is not enough. An MCP server can start, advertise a clean schema, and still fail every real tool call because auth, scopes, downstream permissions, or environment setup are broken.
11
10
 
12
- Read the v1 launch post: [mcp-probe v1.0.0: A CI readiness gate for MCP servers](https://dev.to/k08200/mcp-probe-v100-a-ci-readiness-gate-for-mcp-servers-4ch0)
11
+ `mcp-probe` checks the path an agent actually depends on:
13
12
 
14
- ## Quick Start for CI
13
+ - MCP `initialize` handshake
14
+ - `tools/list` discovery
15
+ - optional real `tools/call` dry-runs
16
+ - sidecar sample inputs for meaningful calls
17
+ - contract assertions for result shape, row limits, stable error codes, and leak checks
18
+ - GitHub Actions summaries and machine-readable JSON output
19
+ - optional JSON receipt artifacts for independent CI evidence
15
20
 
16
- Scaffold the config, sidecar, and GitHub Actions workflow:
21
+ ## Looking For Real-World Recipes
17
22
 
18
- ```bash
19
- npx @k08200/mcp-probe@latest init \
20
- --target @your-org/your-mcp-server \
21
- --discover \
22
- --github-actions
23
- ```
24
-
25
- Add this workflow to any project that depends on MCP servers:
26
-
27
- ```yaml
28
- name: MCP Probe
29
-
30
- on:
31
- pull_request:
32
- push:
33
- branches: [main]
23
+ The core tool is useful only if it reflects real MCP failure modes. If you run MCP servers in agent workflows, recipe contributions are especially useful for:
34
24
 
35
- jobs:
36
- mcp-probe:
37
- runs-on: ubuntu-latest
38
- timeout-minutes: 5
25
+ | Server | What to validate | Issue |
26
+ |---|---|---|
27
+ | Datadog | OAuth/scopes, logs/metrics read paths, auth handoff failures | [#1](https://github.com/k08200/mcp-probe/issues/1) |
28
+ | Supabase | read-only roles, row limits, tenant/project scope, denied writes | [#2](https://github.com/k08200/mcp-probe/issues/2) |
29
+ | Gmail | OAuth browser handoff, stable auth errors, no private email leaks | [#3](https://github.com/k08200/mcp-probe/issues/3) |
39
30
 
40
- steps:
41
- - uses: actions/checkout@v6
31
+ Do not paste secrets. Recipes should use placeholders such as `${DATADOG_MCP_TOKEN}` and read-only sample calls.
42
32
 
43
- - name: Validate MCP server
44
- run: |
45
- npx @k08200/mcp-probe @your-org/your-mcp-server \
46
- --probe-tools \
47
- --github-summary
48
- ```
49
-
50
- For teams running several MCP servers, use a config file:
33
+ ## Quick Start
51
34
 
52
35
  ```bash
53
- npx @k08200/mcp-probe --config mcp-probe.config.json --github-summary
36
+ npx @k08200/mcp-probe@latest @modelcontextprotocol/server-memory
54
37
  ```
55
38
 
56
- For production CI, add sidecar inputs so dry-runs call real read-only paths instead of schema-minimum placeholders:
57
-
58
- ```json
59
- {
60
- "tools": {
61
- "logs_query": {
62
- "input": {
63
- "query": "service:web status:error",
64
- "timeframe": "1h"
65
- },
66
- "expect": {
67
- "status": "pass",
68
- "not_error_code": [401, 403],
69
- "requiredFields": ["source", "freshness"],
70
- "maxRows": 100
71
- }
72
- }
73
- }
74
- }
75
- ```
39
+ For CI, scaffold a config, sidecar, and workflow:
76
40
 
77
41
  ```bash
78
- npx @k08200/mcp-probe @modelcontextprotocol/server-memory
79
- ```
80
-
81
- ```
82
- mcp-probe @modelcontextprotocol/server-memory
83
- ────────────────────────────────────────────────────
84
- ✓ Target resolution
85
- npx --yes @modelcontextprotocol/server-memory
86
- ✓ MCP protocol handshake 1392ms
87
- memory-server v0.6.3
88
- ✓ Tools discovery 33ms
89
- Found 9 tools
90
- ✓ Tool schema validation
91
- All tool schemas are valid
92
- ────────────────────────────────────────────────────
93
- Server memory-server v0.6.3
94
- Caps tools
95
-
96
- Tools
97
- ▸ create_entities Create multiple new entities in the knowledge graph
98
- ▸ create_relations Create multiple new relations between entities
99
- ▸ add_observations Add new observations to existing entities
100
- ▸ delete_entities Delete entities and their associated relations
101
- ▸ read_graph Read the entire knowledge graph
102
- ▸ search_nodes Search for nodes in the knowledge graph
103
- ▸ ...and 3 more
104
-
105
- ✓ PASS 1455ms total
42
+ npx @k08200/mcp-probe@latest init \
43
+ --target @your-org/your-mcp-server \
44
+ --discover \
45
+ --github-actions
106
46
  ```
107
47
 
108
- ---
109
-
110
- ## Install
111
-
112
- Requires Node.js 20.19 or newer.
48
+ Then run:
113
49
 
114
50
  ```bash
115
- # No install needed
116
- npx @k08200/mcp-probe <target>
117
-
118
- # Or install globally
119
- npm install -g @k08200/mcp-probe
51
+ npx @k08200/mcp-probe@latest --config mcp-probe.config.json --github-summary --fail-on-warn
120
52
  ```
121
53
 
122
- ## Usage
54
+ ## Commands
123
55
 
124
56
  ```bash
125
- # Check an npm package
57
+ # Check one server
126
58
  mcp-probe @modelcontextprotocol/server-memory
127
59
 
128
- # Scaffold config + .mcp-probe.json + optional GitHub Actions workflow
129
- mcp-probe init --target @modelcontextprotocol/server-memory --github-actions
130
-
131
- # Discover tool names first and scaffold sidecar entries automatically
132
- mcp-probe init --target @modelcontextprotocol/server-memory --discover --github-actions
133
-
134
- # Check whether this project is ready to run mcp-probe in CI
135
- mcp-probe doctor
136
-
137
- # JSON output for scripting or internal CI preflight checks
138
- mcp-probe doctor --config-file mcp-probe.config.json --output json
139
-
140
- # Scaffold a remote server config with auth from an env var
141
- mcp-probe init \
142
- --target https://mcp.example.com/mcp \
143
- --transport http \
144
- --header-env MCP_TOKEN \
145
- --github-actions
146
-
147
- # Choose custom scaffold paths
148
- mcp-probe init \
149
- --target @your-org/your-mcp-server \
150
- --config-file ci/mcp-probe.config.json \
151
- --sidecar-file ci/mcp-tools.json
152
-
153
- # Check a server that requires arguments (e.g. directories to serve)
154
- mcp-probe @modelcontextprotocol/server-filesystem /tmp /Users/me/projects
60
+ # Check a local server
61
+ mcp-probe ./server.js
155
62
 
156
- # Check a local server file
157
- mcp-probe ./my-server.js
158
-
159
- # Check a remote Streamable HTTP MCP server
160
- mcp-probe https://mcp.example.com/mcp
161
-
162
- # Check a legacy HTTP+SSE MCP server
163
- mcp-probe https://mcp.example.com/sse --transport sse
164
-
165
- # Pass headers to remote servers
63
+ # Check a remote Streamable HTTP server
166
64
  mcp-probe https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
167
65
 
168
- # Ignore known noisy stderr lines when classifying startup failures
169
- mcp-probe @scope/server --stderr-allow "^Warning:" --stderr-fatal "panic|FATAL"
170
-
171
- # JSON output for CI / scripting
172
- mcp-probe @scope/server --output json
173
-
174
- # Custom timeout (default: 10000ms)
175
- mcp-probe @scope/server --timeout 30000
176
-
177
- # Batch-check several servers from a config file
66
+ # Batch-check from config
178
67
  mcp-probe --config mcp-probe.config.json
179
68
 
180
- # Write GitHub Actions summary and annotations
181
- mcp-probe --config mcp-probe.config.json --github-summary
69
+ # Persist an independent readiness receipt artifact
70
+ mcp-probe --config mcp-probe.config.json --receipt-file mcp-probe.receipt.json
182
71
 
183
- # Write shields.io endpoint JSON for a status badge
184
- mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
185
-
186
- # Call tools with generated minimal inputs
72
+ # Call tools, not just tools/list
187
73
  mcp-probe @scope/server --probe-tools
188
74
 
189
- # Call tools with real sample inputs from a sidecar file
75
+ # Use meaningful sidecar inputs
190
76
  mcp-probe @scope/server --tools-file .mcp-probe.json
191
- ```
192
-
193
- ## What it checks
194
-
195
- | Check | Description |
196
- |-------|-------------|
197
- | **Target resolution** | Can the package be located and spawned? |
198
- | **MCP protocol handshake** | Does the server respond to `initialize`? Measures connect latency. |
199
- | **Tools discovery** | Does `tools/list` return results? Measures list latency. |
200
- | **Tool schema validation** | Are all tool schemas well-formed? |
201
- | **Resources discovery** | Runs `resources/list` when the server advertises resources. |
202
- | **Prompts discovery** | Runs `prompts/list` when the server advertises prompts. |
203
- | **Tool call dry-run** | Optional `tools/call` checks via `--probe-tools` or `--tools-file`. |
204
-
205
- ## Issue codes and remediation hints
206
-
207
- When a check warns or fails, mcp-probe attaches stable issue metadata:
208
-
209
- ```json
210
- {
211
- "name": "Tool call dry-run",
212
- "status": "warn",
213
- "message": "1 auth/permission errors (1 sidecar, 0 auto)",
214
- "issue": {
215
- "code": "TOOL_CALL_AUTH",
216
- "hint": "At least one tool call hit auth or permission handling. This often means CI needs tokens or the server needs non-browser auth."
217
- }
218
- }
219
- ```
220
-
221
- These hints appear in terminal output, JSON output, GitHub Actions summaries, and workflow annotations so PR failures point at the likely fix instead of only showing raw MCP errors.
222
77
 
223
- Common issue codes:
224
-
225
- | Code | Meaning |
226
- |------|---------|
227
- | `TARGET_NOT_FOUND` | The npm package, local file, or executable could not be started. |
228
- | `HANDSHAKE_TIMEOUT` | The server did not complete MCP `initialize` before the timeout. |
229
- | `HANDSHAKE_AUTH` | Initialization failed with an auth-like error. |
230
- | `NO_TOOLS` | The server responded but did not expose tools. |
231
- | `TOOL_SCHEMA_INVALID` | A discovered tool has an invalid schema. |
232
- | `TOOL_CALL_AUTH` | A real tool call reached auth or permission handling. |
233
- | `CONTRACT_ASSERTION_FAILED` | A tool call completed but failed one or more sidecar assertions. |
234
- | `AUTO_DRY_RUN_INPUT` | Auto-generated schema-minimum input failed; add sidecar inputs. |
235
- | `TOOL_CALL_FAILED` | A sidecar tool call returned a non-auth error. |
236
-
237
- ## Batch CI gate
78
+ # Preflight local mcp-probe setup
79
+ mcp-probe doctor
238
80
 
239
- If you are starting from scratch, generate the files:
81
+ # Make warnings fail CI too
82
+ mcp-probe --config mcp-probe.config.json --fail-on-warn
240
83
 
241
- ```bash
242
- mcp-probe init --target @your-org/your-mcp-server --discover --github-actions
84
+ # Create missing config/sidecar/workflow files
85
+ mcp-probe doctor --fix --target @scope/server
243
86
  ```
244
87
 
245
- This creates:
246
-
247
- | File | Purpose |
248
- |------|---------|
249
- | `mcp-probe.config.json` | Batch config with one server and `probeTools: true`. |
250
- | `.mcp-probe.json` | Sidecar template for real tool-call sample inputs. |
251
- | `.github/workflows/mcp-probe.yml` | GitHub Actions readiness gate. |
252
-
253
- Existing files are skipped unless you pass `--force`.
88
+ ## Config
254
89
 
255
- Generated config and sidecar files include JSON Schema references:
256
-
257
- | Schema | File |
258
- |--------|------|
259
- | [`mcp-probe.config.schema.json`](schemas/mcp-probe.config.schema.json) | `mcp-probe.config.json` |
260
- | [`mcp-probe.sidecar.schema.json`](schemas/mcp-probe.sidecar.schema.json) | `.mcp-probe.json` |
261
-
262
- When `--discover` is enabled, mcp-probe connects to the target server, runs discovery, and pre-populates `.mcp-probe.json` with the discovered tool names and schema-minimum sample inputs. Review those values before using them as a production CI gate.
263
-
264
- Use `--config` when a project depends on several MCP servers and you want one CI command to validate all of them:
90
+ Use `mcp-probe.config.json` when a repository depends on one or more MCP servers:
265
91
 
266
92
  ```json
267
93
  {
268
94
  "timeoutMs": 10000,
269
95
  "servers": [
270
- {
271
- "name": "memory",
272
- "target": "@modelcontextprotocol/server-memory",
273
- "probeTools": true
274
- },
275
96
  {
276
97
  "name": "datadog",
277
98
  "target": "https://mcp.example.com/mcp",
@@ -279,95 +100,30 @@ Use `--config` when a project depends on several MCP servers and you want one CI
279
100
  "headers": {
280
101
  "Authorization": "Bearer ${DATADOG_MCP_TOKEN}"
281
102
  },
282
- "stderr": {
283
- "allow": ["^Warning:", "missing optional config"],
284
- "fatal": ["panic", "FATAL"]
285
- },
286
- "toolsFile": "./recipes/datadog.tools.json"
103
+ "expectedTools": ["logs_query"],
104
+ "forbiddenTools": ["delete_dashboard", "rotate_api_key"],
105
+ "toolsFile": "./datadog.tools.json"
287
106
  }
288
107
  ]
289
108
  }
290
109
  ```
291
110
 
292
- Run:
293
-
294
- ```bash
295
- mcp-probe --config mcp-probe.config.json
296
- ```
297
-
298
- The process exits with `1` if any configured server fails. Warnings such as auth handoff failures still exit `0`, so CI can flag degraded MCP readiness without blocking deploys unless a server is truly broken.
299
-
300
- Config fields:
301
-
302
- | Field | Description |
303
- |-------|-------------|
304
- | `timeoutMs` | Optional global timeout in milliseconds. CLI `--timeout` is used when omitted. |
305
- | `servers[].name` | Human-readable name shown in batch output. |
306
- | `servers[].target` | npm package, local server path, or remote MCP URL. |
307
- | `servers[].serverArgs` | Optional arguments passed to the MCP server. |
308
- | `servers[].transport` | Optional transport override: `stdio`, `http`, or `sse`. URL targets default to `http`; package/path targets default to `stdio`. |
309
- | `servers[].headers` | Optional HTTP headers for remote MCP servers. `${ENV_VAR}` placeholders are expanded at runtime. |
310
- | `servers[].stderr.allow` | Optional regex patterns for stderr lines that should be ignored when startup fails. |
311
- | `servers[].stderr.fatal` | Optional regex patterns for stderr lines that should always be treated as the startup failure reason. |
312
- | `servers[].probeTools` | Enables dry-run tool calls for that server. |
313
- | `servers[].toolsFile` | Sidecar input file for meaningful `tools/call` samples. Relative paths resolve from the config file directory. |
314
-
315
- ## Project doctor
316
-
317
- Use `mcp-probe doctor` before wiring mcp-probe into CI or after changing config files:
318
-
319
- ```bash
320
- mcp-probe doctor
321
- ```
111
+ Relative local `target` and `toolsFile` paths are resolved from the config file directory.
322
112
 
323
- It checks:
113
+ Use `expectedTools` for tools that must be advertised, `allowedTools` for an exact allow-list, and `forbiddenTools` for dangerous tools that must not appear in low-trust configs.
114
+ When `expectedTools` and a `toolsFile` are both set, every expected tool must also have a sidecar sample input so CI proves the tool is actually dry-run.
324
115
 
325
- | Check | Description |
326
- |-------|-------------|
327
- | **Node.js version** | Confirms the current runtime satisfies mcp-probe's required Node.js version. |
328
- | **Config file** | Validates that `mcp-probe.config.json` exists and can be parsed. |
329
- | **Sidecar files** | Validates each configured `toolsFile`, resolving relative paths from the config file directory. |
330
- | **GitHub Actions workflow** | Warns when no workflow mentions `mcp-probe`, or when workflows miss `actions/checkout@v6`, `--config <file>`, or `--github-summary`. |
331
-
332
- For automation, use JSON output:
333
-
334
- ```bash
335
- mcp-probe doctor --config-file ci/mcp-probe.config.json --output json
336
- ```
337
-
338
- ## Stderr classification
339
-
340
- Many MCP servers write harmless warnings to stderr during startup: optional config notices, update checks, deprecation warnings, and similar noise. If the server later fails to initialize, raw stderr can make those warnings look like the root cause.
341
-
342
- mcp-probe has built-in warning filters and also lets you declare server-specific regexes:
116
+ Run:
343
117
 
344
118
  ```bash
345
- mcp-probe @scope/server \
346
- --stderr-allow "^Warning:" \
347
- --stderr-allow "missing optional config" \
348
- --stderr-fatal "panic|FATAL"
349
- ```
350
-
351
- For batch checks, put the rules in `mcp-probe.config.json`:
352
-
353
- ```json
354
- {
355
- "name": "datadog",
356
- "target": "https://mcp.example.com/mcp",
357
- "stderr": {
358
- "allow": ["^Warning:", "missing optional config"],
359
- "fatal": ["panic", "FATAL"]
360
- }
361
- }
119
+ mcp-probe --config mcp-probe.config.json --github-summary --fail-on-warn
362
120
  ```
363
121
 
364
- `fatal` patterns win over `allow` patterns. If every stderr line is allowed noise, mcp-probe reports the actual connection/init error instead of the warning text.
122
+ ## Sidecar Inputs
365
123
 
366
- ## Tool call dry-runs
124
+ Auto-generated tool inputs mostly test schema validation. Production CI should use sidecar inputs that reach real read-only paths.
367
125
 
368
- Discovery proves that a server starts and registers tools. It does **not** prove that the tools actually work in an agent loop. Use `--probe-tools` to call every discovered tool.
369
-
370
- By default, mcp-probe generates minimal inputs from each tool schema. That catches broken call paths, but real CI gates should prefer a sidecar file with meaningful sample inputs:
126
+ When a sidecar is provided, mcp-probe calls only the tools listed in that file. Tools that are discovered but not listed are not called.
371
127
 
372
128
  ```json
373
129
  {
@@ -377,57 +133,19 @@ By default, mcp-probe generates minimal inputs from each tool schema. That catch
377
133
  "query": "service:web status:error",
378
134
  "timeframe": "1h"
379
135
  },
380
- "expect": {
381
- "not_error_code": [401, 403]
382
- }
383
- }
384
- }
385
- }
386
- ```
387
-
388
- Save this as `.mcp-probe.json` in your project root and run:
389
-
390
- ```bash
391
- mcp-probe @your-org/datadog-mcp --probe-tools
392
- ```
393
-
394
- Or pass an explicit path:
395
-
396
- ```bash
397
- mcp-probe @your-org/datadog-mcp --tools-file ./ci/mcp-tools.json
398
- ```
399
-
400
- Sidecar inputs are used first; generated minimal inputs are fallback only. Auth and permission failures such as 401/403 are surfaced as warnings so CI can distinguish "OAuth handoff needed" from transport or runtime failure.
401
-
402
- ## Tool call contract assertions
403
-
404
- For production MCP servers, especially database-backed servers, a successful `tools/call` is still not enough. Agents depend on a contract: read-only roles, scoped data, stable error codes, safe limits, and no leaked internals.
405
-
406
- Add assertions to `.mcp-probe.json` to validate that contract:
407
-
408
- ```json
409
- {
410
- "tools": {
411
- "execute_sql": {
412
- "input": {
413
- "project_id": "YOUR_PROJECT_ID",
414
- "query": "select 1 as health_check"
415
- },
416
136
  "expect": {
417
137
  "status": "pass",
418
- "requiredFields": ["rowCount", "limit", "source", "freshness"],
419
- "maxRows": 100
420
- }
421
- },
422
- "execute_sql_write_denied": {
423
- "input": {
424
- "project_id": "YOUR_PROJECT_ID",
425
- "query": "delete from users where id = 1"
426
- },
427
- "expect": {
428
- "status": "fail",
429
- "errorCode": "WRITE_NOT_ALLOWED",
430
- "notContains": ["DATABASE_URL", "password", "stack"]
138
+ "not_error_code": [401, 403],
139
+ "requiredFields": ["source", "freshness"],
140
+ "maxRows": 100,
141
+ "jsonSchema": {
142
+ "type": "object",
143
+ "required": ["source", "freshness"],
144
+ "properties": {
145
+ "source": { "type": "string" },
146
+ "freshness": { "type": "string" }
147
+ }
148
+ }
431
149
  }
432
150
  }
433
151
  }
@@ -437,55 +155,43 @@ Add assertions to `.mcp-probe.json` to validate that contract:
437
155
  Supported assertions:
438
156
 
439
157
  | Assertion | Purpose |
440
- |-----------|---------|
441
- | `status` | Expected call status: `pass`, `fail`, or `warn`. Use `fail` for denied-write probes. |
442
- | `requiredFields` | Field names that must appear anywhere in the tool result payload. |
443
- | `maxRows` | Maximum allowed row count, using `rowCount`, `rowsReturned`, or common row arrays. |
158
+ |---|---|
159
+ | `status` | Expected call status: `pass`, `fail`, or `warn`. |
160
+ | `requiredFields` | Fields that must appear somewhere in the result payload. |
161
+ | `maxRows` | Maximum allowed row count from metadata or row arrays. |
444
162
  | `errorCode` | Stable error code expected in an error response. |
445
- | `contains` | Text snippets that must appear in the result or error payload. |
446
- | `notContains` | Text snippets that must not appear; useful for stack traces, secrets, and raw internals. |
447
- | `not_error_code` | HTTP/status codes that should be warnings instead of failures, usually auth handoff codes. |
163
+ | `contains` | Text snippets that must appear. |
164
+ | `notContains` | Text snippets that must not appear, useful for leak checks. |
165
+ | `not_error_code` | HTTP/status codes treated as warnings, usually auth handoff codes. |
166
+ | `jsonSchema` | JSON Schema subset for validating the observed tool result shape. |
448
167
 
449
- If an assertion fails, mcp-probe returns `CONTRACT_ASSERTION_FAILED` and includes per-assertion details in JSON and GitHub Actions summaries.
168
+ ## Doctor
450
169
 
451
- ## Status badges
452
-
453
- Use `--badge-file` to write a [shields.io endpoint](https://shields.io/badges/endpoint-badge) JSON file:
170
+ `doctor` checks whether the repository is ready to run mcp-probe in CI:
454
171
 
455
172
  ```bash
456
- mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
173
+ mcp-probe doctor
457
174
  ```
458
175
 
459
- Example output:
176
+ It validates:
460
177
 
461
- ```json
462
- {
463
- "schemaVersion": 1,
464
- "label": "mcp fleet",
465
- "message": "2 pass, 1 warn",
466
- "color": "yellow"
467
- }
468
- ```
178
+ - Node.js version
179
+ - config file shape
180
+ - sidecar file shape
181
+ - `expectedTools` sidecar sample coverage
182
+ - GitHub Actions workflow presence, strict CI flags, receipt generation, and artifact upload
183
+ - whether mcp-probe is actually executed from a workflow `run:` step
469
184
 
470
- Host that JSON file anywhere public and reference it from your README:
185
+ `doctor --fix` creates missing files. It does **not** rewrite existing workflows unless `--force` is explicitly passed.
186
+ When a config already declares `expectedTools`, missing sidecar files are scaffolded with those tool names instead of a generic placeholder.
471
187
 
472
- ```markdown
473
- ![MCP readiness](https://img.shields.io/endpoint?url=https://example.com/mcp-probe-badge.json)
188
+ ```bash
189
+ mcp-probe doctor --fix --target @your-org/your-mcp-server
474
190
  ```
475
191
 
476
- ## Exit codes
477
-
478
- | Code | Meaning |
479
- |------|---------|
480
- | `0` | All checks passed (or warnings only) |
481
- | `1` | One or more checks failed |
482
-
483
- ## CI integration
484
-
485
- Single server workflow:
192
+ ## GitHub Actions
486
193
 
487
194
  ```yaml
488
- # .github/workflows/mcp-probe.yml
489
195
  name: MCP Probe
490
196
 
491
197
  on:
@@ -496,145 +202,52 @@ on:
496
202
  jobs:
497
203
  mcp-probe:
498
204
  runs-on: ubuntu-latest
499
- timeout-minutes: 5
500
-
501
- steps:
502
- - uses: actions/checkout@v6
503
-
504
- - name: Validate MCP server
505
- run: |
506
- npx @k08200/mcp-probe @your-org/your-mcp-server \
507
- --probe-tools \
508
- --github-summary \
509
- --badge-file mcp-probe-badge.json
510
- ```
511
-
512
- Fleet workflow:
513
-
514
- ```yaml
515
- # .github/workflows/mcp-fleet.yml
516
- name: MCP Fleet Probe
517
-
518
- on:
519
- pull_request:
520
- push:
521
- branches: [main]
522
- schedule:
523
- - cron: "0 * * * *"
524
-
525
- jobs:
526
- mcp-probe:
527
- runs-on: ubuntu-latest
528
- timeout-minutes: 10
529
-
530
205
  steps:
531
206
  - uses: actions/checkout@v6
532
-
533
- - name: Validate MCP fleet
534
- run: |
535
- npx @k08200/mcp-probe \
207
+ - uses: actions/setup-node@v6
208
+ with:
209
+ node-version: 20
210
+ - run: |
211
+ npx @k08200/mcp-probe@latest \
536
212
  --config mcp-probe.config.json \
537
213
  --github-summary \
538
- --badge-file mcp-probe-badge.json
214
+ --fail-on-warn \
215
+ --receipt-file mcp-probe.receipt.json
216
+ - uses: actions/upload-artifact@v4
217
+ with:
218
+ name: mcp-probe-receipt
219
+ path: mcp-probe.receipt.json
539
220
  ```
540
221
 
541
- When `--github-summary` is enabled in GitHub Actions, mcp-probe appends a Markdown report to `$GITHUB_STEP_SUMMARY` and emits workflow annotations for failed checks, warnings, and tool-call dry-run errors. This makes PR failures point directly at the broken MCP server or tool call instead of burying the signal in raw logs.
222
+ ## Receipt Artifacts
542
223
 
543
- Copy-ready examples live in [`examples/github-actions`](examples/github-actions):
224
+ `--receipt-file` writes a redacted JSON artifact containing the observed handshake, tool catalog, dry-run calls, contract assertions, and final status.
544
225
 
545
- | Example | Use case |
546
- |---------|----------|
547
- | [`single-server.yml`](examples/github-actions/single-server.yml) | Validate one stdio MCP package. |
548
- | [`fleet.yml`](examples/github-actions/fleet.yml) | Validate several MCP servers from `mcp-probe.config.json` on PRs and hourly schedules. |
549
- | [`remote-server.yml`](examples/github-actions/remote-server.yml) | Validate a remote Streamable HTTP MCP server with auth headers. |
550
-
551
- mcp-probe also dogfoods itself in CI with [`examples/self-check.config.json`](examples/self-check.config.json), which validates batch mode, sidecar inputs, GitHub summaries, and badge output against a local fixture MCP server.
552
-
553
- It also includes [`examples/contract-failure.tools.json`](examples/contract-failure.tools.json), an intentionally broken sidecar used by CI to prove contract failures surface as `CONTRACT_ASSERTION_FAILED`. That fixture checks the negative path: missing metadata, row-limit violations, and denied writes that must fail safely.
554
-
555
- ## Recipes
556
-
557
- Production MCP checks work best with sidecar inputs that exercise real call paths instead of generated empty values. Copy-ready starting points live in [`examples/recipes`](examples/recipes):
558
-
559
- | Recipe | Focus |
560
- |--------|-------|
561
- | [`datadog.tools.json`](examples/recipes/datadog.tools.json) | Logs/metrics queries that reveal auth handoff and downstream API failures. |
562
- | [`supabase.tools.json`](examples/recipes/supabase.tools.json) | Project visibility and a harmless `select 1` SQL path. |
563
- | [`gmail.tools.json`](examples/recipes/gmail.tools.json) | OAuth/token handoff and read-only mailbox access. |
564
-
565
- Tool names vary by MCP server implementation. Run your server once with `--output json`, inspect the discovered tool names and schemas, then adjust the recipe file to match.
566
-
567
- ## JSON output
226
+ Use it when CI needs durable evidence of what actually happened, not just terminal output:
568
227
 
569
228
  ```bash
570
- mcp-probe @your-org/datadog-mcp --tools-file .mcp-probe.json --output json
229
+ mcp-probe --config mcp-probe.config.json --receipt-file mcp-probe.receipt.json
571
230
  ```
572
231
 
573
- ```json
574
- {
575
- "target": "@your-org/datadog-mcp",
576
- "timestamp": "2026-05-17T12:00:00.000Z",
577
- "overallStatus": "warn",
578
- "checks": [
579
- { "name": "Target resolution", "status": "pass", "message": "npx --yes @your-org/datadog-mcp" },
580
- { "name": "MCP protocol handshake", "status": "pass", "message": "datadog-mcp v1.0.0", "latencyMs": 1392 },
581
- { "name": "Tools discovery", "status": "pass", "message": "Found 12 tools", "latencyMs": 33 },
582
- { "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" },
583
- {
584
- "name": "Tool call dry-run",
585
- "status": "warn",
586
- "message": "1 auth/permission errors (1 sidecar, 0 auto)",
587
- "issue": {
588
- "code": "TOOL_CALL_AUTH",
589
- "hint": "At least one tool call hit auth or permission handling. This often means CI needs tokens or the server needs non-browser auth."
590
- }
591
- }
592
- ],
593
- "serverInfo": { "name": "datadog-mcp", "version": "1.0.0", "capabilities": ["tools"] },
594
- "tools": [{ "name": "logs_query", "description": "Query Datadog logs" }],
595
- "toolCallResults": [
596
- {
597
- "tool": "logs_query",
598
- "status": "warn",
599
- "latencyMs": 41,
600
- "source": "sidecar",
601
- "error": "401 Unauthorized",
602
- "issue": {
603
- "code": "TOOL_CALL_AUTH",
604
- "hint": "The server registered this tool, but the call path hit auth or permission handling. Check OAuth/browser handoff, service tokens, and CI secrets."
605
- }
606
- }
607
- ],
608
- "totalLatencyMs": 1455
609
- }
610
- ```
611
-
612
- ## Status values
613
-
614
- | Status | Icon | Meaning |
615
- |--------|------|---------|
616
- | `pass` | ✓ | Check succeeded |
617
- | `warn` | ⚠ | Non-fatal issue (e.g. no tools registered) |
618
- | `fail` | ✗ | Check failed — exits with code 1 |
232
+ ## Exit Codes
619
233
 
620
- ## Roadmap
621
-
622
- - [x] Self-check batch workflow for mcp-probe itself
623
- - [x] HTTP/SSE transport support
624
- - [x] Batch checking from a config file (`mcp-probe --config mcp-probe.config.json`)
625
- - [x] GitHub Actions summary and annotations
626
- - [x] Badge generation (`mcp-probe --badge-file mcp-probe-badge.json`)
627
- - [x] Structured stderr classification rules
628
- - [x] Server-specific recipe examples for Datadog, Supabase, and Gmail MCP servers
629
-
630
- ## Contributing
234
+ | Code | Meaning |
235
+ |---|---|
236
+ | `0` | Passed, or warnings only unless `--fail-on-warn` is set |
237
+ | `1` | One or more checks failed |
631
238
 
632
- Issues and PRs are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
239
+ Warnings do not fail CI by default. They are intended for degraded states such as OAuth handoff or permission issues that should be visible but may not block every deploy.
240
+ Use `--fail-on-warn` for production readiness gates where auth handoff, permission warnings, or incomplete receipts should block the workflow.
633
241
 
634
- ## Changelog
242
+ ## Development
635
243
 
636
- See [CHANGELOG.md](CHANGELOG.md).
244
+ ```bash
245
+ npm install
246
+ npm run typecheck
247
+ npm test
248
+ npm run build
249
+ ```
637
250
 
638
251
  ## License
639
252
 
640
- [MIT](LICENSE)
253
+ MIT