@k08200/mcp-probe 1.5.0 → 1.8.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 (50) hide show
  1. package/README.md +86 -517
  2. package/dist/checker.d.ts.map +1 -1
  3. package/dist/checker.js +54 -56
  4. package/dist/checker.js.map +1 -1
  5. package/dist/cli.js +42 -10
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +30 -1
  9. package/dist/config.js.map +1 -1
  10. package/dist/doctor.d.ts +5 -0
  11. package/dist/doctor.d.ts.map +1 -1
  12. package/dist/doctor.js +270 -29
  13. package/dist/doctor.js.map +1 -1
  14. package/dist/exit-code.d.ts +3 -0
  15. package/dist/exit-code.d.ts.map +1 -0
  16. package/dist/exit-code.js +8 -0
  17. package/dist/exit-code.js.map +1 -0
  18. package/dist/init.d.ts.map +1 -1
  19. package/dist/init.js +1 -118
  20. package/dist/init.js.map +1 -1
  21. package/dist/issues.d.ts.map +1 -1
  22. package/dist/issues.js +33 -14
  23. package/dist/issues.js.map +1 -1
  24. package/dist/protocols/mcp-client.d.ts.map +1 -1
  25. package/dist/protocols/mcp-client.js +36 -17
  26. package/dist/protocols/mcp-client.js.map +1 -1
  27. package/dist/scaffold.d.ts +17 -0
  28. package/dist/scaffold.d.ts.map +1 -0
  29. package/dist/scaffold.js +145 -0
  30. package/dist/scaffold.js.map +1 -0
  31. package/dist/sidecar.d.ts +6 -0
  32. package/dist/sidecar.d.ts.map +1 -0
  33. package/dist/sidecar.js +76 -0
  34. package/dist/sidecar.js.map +1 -0
  35. package/dist/types.d.ts +7 -1
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/version.d.ts +2 -0
  38. package/dist/version.d.ts.map +1 -0
  39. package/dist/version.js +5 -0
  40. package/dist/version.js.map +1 -0
  41. package/examples/fixtures/stdio-mcp-server.js +68 -0
  42. package/examples/github-actions/fleet.yml +1 -0
  43. package/examples/github-actions/remote-server.yml +1 -0
  44. package/examples/github-actions/single-server.yml +1 -0
  45. package/examples/self-check.config.json +3 -1
  46. package/examples/self-check.strict.config.json +16 -0
  47. package/examples/self-check.strict.tools.json +29 -0
  48. package/package.json +1 -1
  49. package/schemas/mcp-probe.config.schema.json +15 -0
  50. package/schemas/mcp-probe.sidecar.schema.json +1 -1
package/README.md CHANGED
@@ -3,275 +3,80 @@
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
15
19
 
16
- Scaffold the config, sidecar, and GitHub Actions workflow:
20
+ ## Quick Start
17
21
 
18
22
  ```bash
19
- npx @k08200/mcp-probe@latest init \
20
- --target @your-org/your-mcp-server \
21
- --discover \
22
- --github-actions
23
+ npx @k08200/mcp-probe@latest @modelcontextprotocol/server-memory
23
24
  ```
24
25
 
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]
34
-
35
- jobs:
36
- mcp-probe:
37
- runs-on: ubuntu-latest
38
- timeout-minutes: 5
39
-
40
- steps:
41
- - uses: actions/checkout@v6
42
-
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:
26
+ For CI, scaffold a config, sidecar, and workflow:
51
27
 
52
28
  ```bash
53
- npx @k08200/mcp-probe --config mcp-probe.config.json --github-summary
29
+ npx @k08200/mcp-probe@latest init \
30
+ --target @your-org/your-mcp-server \
31
+ --discover \
32
+ --github-actions
54
33
  ```
55
34
 
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
- ```
35
+ Then run:
76
36
 
77
37
  ```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
38
+ npx @k08200/mcp-probe@latest --config mcp-probe.config.json --github-summary --fail-on-warn
106
39
  ```
107
40
 
108
- ---
109
-
110
- ## Install
111
-
112
- Requires Node.js 20.19 or newer.
41
+ ## Commands
113
42
 
114
43
  ```bash
115
- # No install needed
116
- npx @k08200/mcp-probe <target>
117
-
118
- # Or install globally
119
- npm install -g @k08200/mcp-probe
120
- ```
121
-
122
- ## Usage
123
-
124
- ```bash
125
- # Check an npm package
44
+ # Check one server
126
45
  mcp-probe @modelcontextprotocol/server-memory
127
46
 
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
155
-
156
- # Check a local server file
157
- mcp-probe ./my-server.js
47
+ # Check a local server
48
+ mcp-probe ./server.js
158
49
 
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
50
+ # Check a remote Streamable HTTP server
166
51
  mcp-probe https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
167
52
 
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
53
+ # Batch-check from config
178
54
  mcp-probe --config mcp-probe.config.json
179
55
 
180
- # Write GitHub Actions summary and annotations
181
- mcp-probe --config mcp-probe.config.json --github-summary
182
-
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
56
+ # Call tools, not just tools/list
187
57
  mcp-probe @scope/server --probe-tools
188
58
 
189
- # Call tools with real sample inputs from a sidecar file
59
+ # Use meaningful sidecar inputs
190
60
  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
61
 
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
-
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
62
+ # Preflight local mcp-probe setup
63
+ mcp-probe doctor
238
64
 
239
- If you are starting from scratch, generate the files:
65
+ # Make warnings fail CI too
66
+ mcp-probe --config mcp-probe.config.json --fail-on-warn
240
67
 
241
- ```bash
242
- mcp-probe init --target @your-org/your-mcp-server --discover --github-actions
68
+ # Create missing config/sidecar/workflow files
69
+ mcp-probe doctor --fix --target @scope/server
243
70
  ```
244
71
 
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`.
254
-
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.
72
+ ## Config
263
73
 
264
- Use `--config` when a project depends on several MCP servers and you want one CI command to validate all of them:
74
+ Use `mcp-probe.config.json` when a repository depends on one or more MCP servers:
265
75
 
266
76
  ```json
267
77
  {
268
78
  "timeoutMs": 10000,
269
79
  "servers": [
270
- {
271
- "name": "memory",
272
- "target": "@modelcontextprotocol/server-memory",
273
- "probeTools": true
274
- },
275
80
  {
276
81
  "name": "datadog",
277
82
  "target": "https://mcp.example.com/mcp",
@@ -279,95 +84,30 @@ Use `--config` when a project depends on several MCP servers and you want one CI
279
84
  "headers": {
280
85
  "Authorization": "Bearer ${DATADOG_MCP_TOKEN}"
281
86
  },
282
- "stderr": {
283
- "allow": ["^Warning:", "missing optional config"],
284
- "fatal": ["panic", "FATAL"]
285
- },
286
- "toolsFile": "./recipes/datadog.tools.json"
87
+ "expectedTools": ["logs_query"],
88
+ "forbiddenTools": ["delete_dashboard", "rotate_api_key"],
89
+ "toolsFile": "./datadog.tools.json"
287
90
  }
288
91
  ]
289
92
  }
290
93
  ```
291
94
 
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
- ```
322
-
323
- It checks:
95
+ Relative local `target` and `toolsFile` paths are resolved from the config file directory.
324
96
 
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 file mentions `mcp-probe`. |
97
+ 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.
98
+ 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.
331
99
 
332
- For automation, use JSON output:
100
+ Run:
333
101
 
334
102
  ```bash
335
- mcp-probe doctor --config-file ci/mcp-probe.config.json --output json
103
+ mcp-probe --config mcp-probe.config.json --github-summary --fail-on-warn
336
104
  ```
337
105
 
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:
106
+ ## Sidecar Inputs
343
107
 
344
- ```bash
345
- mcp-probe @scope/server \
346
- --stderr-allow "^Warning:" \
347
- --stderr-allow "missing optional config" \
348
- --stderr-fatal "panic|FATAL"
349
- ```
108
+ Auto-generated tool inputs mostly test schema validation. Production CI should use sidecar inputs that reach real read-only paths.
350
109
 
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
- }
362
- ```
363
-
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.
365
-
366
- ## Tool call dry-runs
367
-
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:
110
+ 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
111
 
372
112
  ```json
373
113
  {
@@ -377,58 +117,12 @@ By default, mcp-probe generates minimal inputs from each tool schema. That catch
377
117
  "query": "service:web status:error",
378
118
  "timeframe": "1h"
379
119
  },
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
120
  "expect": {
417
121
  "status": "pass",
418
- "requiredFields": ["rowCount", "limit", "source", "freshness"],
122
+ "not_error_code": [401, 403],
123
+ "requiredFields": ["source", "freshness"],
419
124
  "maxRows": 100
420
125
  }
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"]
431
- }
432
126
  }
433
127
  }
434
128
  }
@@ -437,55 +131,42 @@ Add assertions to `.mcp-probe.json` to validate that contract:
437
131
  Supported assertions:
438
132
 
439
133
  | 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. |
134
+ |---|---|
135
+ | `status` | Expected call status: `pass`, `fail`, or `warn`. |
136
+ | `requiredFields` | Fields that must appear somewhere in the result payload. |
137
+ | `maxRows` | Maximum allowed row count from metadata or row arrays. |
444
138
  | `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. |
448
-
449
- If an assertion fails, mcp-probe returns `CONTRACT_ASSERTION_FAILED` and includes per-assertion details in JSON and GitHub Actions summaries.
139
+ | `contains` | Text snippets that must appear. |
140
+ | `notContains` | Text snippets that must not appear, useful for leak checks. |
141
+ | `not_error_code` | HTTP/status codes treated as warnings, usually auth handoff codes. |
450
142
 
451
- ## Status badges
143
+ ## Doctor
452
144
 
453
- Use `--badge-file` to write a [shields.io endpoint](https://shields.io/badges/endpoint-badge) JSON file:
145
+ `doctor` checks whether the repository is ready to run mcp-probe in CI:
454
146
 
455
147
  ```bash
456
- mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
148
+ mcp-probe doctor
457
149
  ```
458
150
 
459
- Example output:
151
+ It validates:
460
152
 
461
- ```json
462
- {
463
- "schemaVersion": 1,
464
- "label": "mcp fleet",
465
- "message": "2 pass, 1 warn",
466
- "color": "yellow"
467
- }
468
- ```
153
+ - Node.js version
154
+ - config file shape
155
+ - sidecar file shape
156
+ - `expectedTools` sidecar sample coverage
157
+ - GitHub Actions workflow presence and recommended flags (`--github-summary`, `--fail-on-warn`)
158
+ - whether mcp-probe is actually executed from a workflow `run:` step
469
159
 
470
- Host that JSON file anywhere public and reference it from your README:
160
+ `doctor --fix` creates missing files. It does **not** rewrite existing workflows unless `--force` is explicitly passed.
161
+ When a config already declares `expectedTools`, missing sidecar files are scaffolded with those tool names instead of a generic placeholder.
471
162
 
472
- ```markdown
473
- ![MCP readiness](https://img.shields.io/endpoint?url=https://example.com/mcp-probe-badge.json)
163
+ ```bash
164
+ mcp-probe doctor --fix --target @your-org/your-mcp-server
474
165
  ```
475
166
 
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:
167
+ ## GitHub Actions
486
168
 
487
169
  ```yaml
488
- # .github/workflows/mcp-probe.yml
489
170
  name: MCP Probe
490
171
 
491
172
  on:
@@ -496,145 +177,33 @@ on:
496
177
  jobs:
497
178
  mcp-probe:
498
179
  runs-on: ubuntu-latest
499
- timeout-minutes: 5
500
-
501
180
  steps:
502
181
  - 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
182
+ - uses: actions/setup-node@v6
183
+ with:
184
+ node-version: 20
185
+ - run: npx @k08200/mcp-probe@latest --config mcp-probe.config.json --github-summary --fail-on-warn
510
186
  ```
511
187
 
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
- steps:
531
- - uses: actions/checkout@v6
532
-
533
- - name: Validate MCP fleet
534
- run: |
535
- npx @k08200/mcp-probe \
536
- --config mcp-probe.config.json \
537
- --github-summary \
538
- --badge-file mcp-probe-badge.json
539
- ```
540
-
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.
542
-
543
- Copy-ready examples live in [`examples/github-actions`](examples/github-actions):
188
+ ## Exit Codes
544
189
 
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. |
190
+ | Code | Meaning |
191
+ |---|---|
192
+ | `0` | Passed, or warnings only unless `--fail-on-warn` is set |
193
+ | `1` | One or more checks failed |
564
194
 
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.
195
+ 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.
196
+ Use `--fail-on-warn` for production readiness gates where auth handoff, permission warnings, or incomplete receipts should block the workflow.
566
197
 
567
- ## JSON output
198
+ ## Development
568
199
 
569
200
  ```bash
570
- mcp-probe @your-org/datadog-mcp --tools-file .mcp-probe.json --output json
571
- ```
572
-
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
- }
201
+ npm install
202
+ npm run typecheck
203
+ npm test
204
+ npm run build
610
205
  ```
611
206
 
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 |
619
-
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
631
-
632
- Issues and PRs are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
633
-
634
- ## Changelog
635
-
636
- See [CHANGELOG.md](CHANGELOG.md).
637
-
638
207
  ## License
639
208
 
640
- [MIT](LICENSE)
209
+ MIT