mcp-stdio-guard 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -14
- package/package.json +1 -1
- package/src/index.js +2210 -103
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://github.com/1Utkarsh1/mcp-stdio-guard/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/1Utkarsh1/mcp-stdio-guard/actions/workflows/ci.yml/badge.svg" /></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/mcp-stdio-guard"><img alt="npm" src="https://img.shields.io/npm/v/mcp-stdio-guard?color=0b6bcb" /></a>
|
|
14
|
-
<a href="https://badge.socket.dev/npm/package/mcp-stdio-guard/0.
|
|
14
|
+
<a href="https://badge.socket.dev/npm/package/mcp-stdio-guard/0.5.0"><img alt="Socket" src="https://badge.socket.dev/npm/package/mcp-stdio-guard/0.5.0" /></a>
|
|
15
15
|
<img alt="runtime dependencies" src="https://img.shields.io/badge/runtime%20deps-0-1f8f4c" />
|
|
16
16
|
<img alt="node" src="https://img.shields.io/badge/node-%3E%3D18-2f855a" />
|
|
17
17
|
<a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-111827" /></a>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
MCP stdio servers use stdout as their protocol channel. Debug text, banners, progress logs, `console.log`, Python `print`, or any other stray stdout output can corrupt the stream and make clients fail in confusing ways.
|
|
25
25
|
|
|
26
|
-
`mcp-stdio-guard` starts your server, performs a real MCP initialize handshake, optionally sends a real post-initialize MCP request such as `tools/list`, validates every stdout frame, and scans source for risky stdout calls.
|
|
26
|
+
`mcp-stdio-guard` starts your server, performs a real MCP initialize handshake, probes advertised `tools`, `resources`, and `prompts` list capabilities, optionally sends a real post-initialize MCP request such as `tools/list`, validates every stdout frame, checks returned tool metadata, and scans source for risky stdout calls.
|
|
27
27
|
|
|
28
28
|
## Why This Exists
|
|
29
29
|
|
|
@@ -60,13 +60,25 @@ Run your MCP server behind the guard:
|
|
|
60
60
|
mcp-stdio-guard -- node ./server.js
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
Use a deterministic profile for common workflows:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mcp-stdio-guard --profile registry --json -- node ./server.js
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use a config file for registry runs that need environment names, request lists, or explicitly safe tool calls:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
mcp-stdio-guard --config mcp-stdio-guard.config.json
|
|
73
|
+
```
|
|
74
|
+
|
|
63
75
|
Exercise a real MCP operation after initialization:
|
|
64
76
|
|
|
65
77
|
```bash
|
|
66
78
|
mcp-stdio-guard --request tools/list -- node ./server.js
|
|
67
79
|
```
|
|
68
80
|
|
|
69
|
-
Scan source for obvious stdout writes too:
|
|
81
|
+
Scan source for obvious stdout writes too. Findings are warnings unless `--fail-on-static` is set:
|
|
70
82
|
|
|
71
83
|
```bash
|
|
72
84
|
mcp-stdio-guard --scan src --fail-on-static --request tools/list -- node ./server.js
|
|
@@ -99,6 +111,8 @@ mcp-stdio-guard --repeat 2 --request tools/list -- node ./server.js
|
|
|
99
111
|
| Invalid JSON-RPC frames | Yes | No |
|
|
100
112
|
| Server crash after `notifications/initialized` | Yes | No |
|
|
101
113
|
| Missing `initialize` or operation response | Yes | No |
|
|
114
|
+
| Duplicate tool names or invalid `inputSchema.required` | Yes with `--request tools/list` | No |
|
|
115
|
+
| Cold/warm protocol, capability, or tool-list drift | Warning with `--repeat` | No |
|
|
102
116
|
| stderr diagnostics | Allowed | Allowed |
|
|
103
117
|
|
|
104
118
|
## Live MCP Coverage
|
|
@@ -122,17 +136,83 @@ mcp-stdio-guard [options] -- <command> [args...]
|
|
|
122
136
|
|
|
123
137
|
| Option | Description |
|
|
124
138
|
| --- | --- |
|
|
139
|
+
| `--config <path>` | read a JSON config file for registry runs and explicitly safe tool calls |
|
|
140
|
+
| `--profile <name>` | apply a deterministic guard profile: `custom`, `smoke`, `registry`, `ci`, or `strict` |
|
|
125
141
|
| `--protocol <version>` | MCP protocol version to send, default `2025-11-25` |
|
|
126
142
|
| `--timeout <ms>` | initialize and request timeout, default `5000` |
|
|
127
143
|
| `--repeat <count>` | run the same guard multiple times to catch cold/warm startup behavior |
|
|
128
144
|
| `--request <method>` | send one MCP request after initialization, for example `tools/list` |
|
|
129
145
|
| `--params <json>` | JSON params for `--request` |
|
|
130
|
-
| `--
|
|
146
|
+
| `--adversarial-probe <name>` / `--adversarial-probes <list>` | opt into strict protocol probes: `invalid-method`, `invalid-params`, `notification`, `malformed-json`, `all`, or `none` |
|
|
147
|
+
| `--scan <path>` | scan source for risky stdout writes and visible startup-output risks |
|
|
131
148
|
| `--fail-on-static` | make static scan findings fail the command |
|
|
132
149
|
| `--json` | print machine-readable output |
|
|
133
150
|
| `--cwd <path>` | run the server command from a specific directory |
|
|
134
151
|
| `--help` | show help |
|
|
135
152
|
|
|
153
|
+
## Profiles
|
|
154
|
+
|
|
155
|
+
Profiles are deterministic presets for common workflows. Existing CLI behavior remains the default `custom` profile, so current commands keep working unless `--profile` is provided.
|
|
156
|
+
|
|
157
|
+
| Profile | Behavior |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `custom` | preserve explicit CLI flags and legacy defaults |
|
|
160
|
+
| `smoke` | initialize only unless `--request` is provided; skip advertised `tools/list`, `resources/list`, and `prompts/list` probes |
|
|
161
|
+
| `registry` | run advertised list probes and repeat twice by default for cold/warm consistency |
|
|
162
|
+
| `ci` | emit JSON output and make static scan findings fail when `--scan` is used |
|
|
163
|
+
| `strict` | combine CI-style output/static failures, registry-style repeat depth, and built-in adversarial protocol probes |
|
|
164
|
+
|
|
165
|
+
Explicit flags can still narrow or deepen a profile. For example, `--profile registry --repeat 1` keeps registry capability probing but disables the repeat preset. Use `--profile strict --adversarial-probes none` if you want strict JSON/static behavior without adversarial inputs.
|
|
166
|
+
|
|
167
|
+
## Config Files
|
|
168
|
+
|
|
169
|
+
Config files let registries run repeatable checks without hiding what was executed. The file is JSON, and CLI flags still override matching config defaults. Parsing happens before the server process starts, so invalid config does not launch the target command.
|
|
170
|
+
|
|
171
|
+
Supported fields:
|
|
172
|
+
|
|
173
|
+
| Field | Meaning |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| `command` | command as either `["node", "./server.js"]` or `"node"` with `args` |
|
|
176
|
+
| `args` | arguments used only when `command` is a string |
|
|
177
|
+
| `cwd` | working directory, resolved relative to the config file |
|
|
178
|
+
| `env` | environment variables to pass; values are redacted in JSON output |
|
|
179
|
+
| `profile`, `protocol`, `timeoutMs`, `repeat`, `json` | same meaning as CLI options |
|
|
180
|
+
| `scan` or `scanPath` | source scan path, resolved relative to the config file |
|
|
181
|
+
| `failOnStatic` | make static scan findings fail |
|
|
182
|
+
| `request` | one explicit post-initialize request: `{ "method": "tools/list" }` |
|
|
183
|
+
| `requests` | list of explicit post-initialize requests |
|
|
184
|
+
| `safeToolCalls` | opt-in `tools/call` recipes; no tool is called unless listed here or explicitly requested |
|
|
185
|
+
| `adversarialProbes` | opt-in built-in probes as `true`, `"all"`, `"none"`, or a list of probe names |
|
|
186
|
+
| `adversarialToolCalls` | opt-in invalid-argument `tools/call` probes for configured safe tools |
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"profile": "registry",
|
|
193
|
+
"command": ["node", "./server.js"],
|
|
194
|
+
"cwd": ".",
|
|
195
|
+
"json": true,
|
|
196
|
+
"env": {
|
|
197
|
+
"API_TOKEN": "set-in-runner"
|
|
198
|
+
},
|
|
199
|
+
"requests": [
|
|
200
|
+
{ "method": "tools/list" }
|
|
201
|
+
],
|
|
202
|
+
"adversarialProbes": ["invalid-method", "notification"],
|
|
203
|
+
"safeToolCalls": [
|
|
204
|
+
{ "name": "echo", "arguments": { "text": "hello" } }
|
|
205
|
+
],
|
|
206
|
+
"adversarialToolCalls": [
|
|
207
|
+
{ "name": "echo", "arguments": { "unexpected": true } }
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The guard does not discover and call arbitrary tools from `tools/list`. Tool execution only happens through an explicit `safeToolCalls` entry or an explicit `tools/call` request you provide.
|
|
213
|
+
|
|
214
|
+
Adversarial probes are off by default because they intentionally send unusual inputs. Built-in probes check that unknown methods return structured errors, invalid params return structured errors, notifications do not receive responses, and malformed JSON does not crash the process. `adversarialToolCalls` is separate because it calls a named tool with intentionally invalid arguments; only use it for tools you control and consider safe/idempotent.
|
|
215
|
+
|
|
136
216
|
## JSON Contract
|
|
137
217
|
|
|
138
218
|
`--json` is intended for CI, registries, and badge ingestion. The current contract is `schemaVersion: 1`; new fields may be added, but these fields are stable for consumers:
|
|
@@ -141,21 +221,30 @@ mcp-stdio-guard [options] -- <command> [args...]
|
|
|
141
221
|
| --- | --- |
|
|
142
222
|
| `schemaVersion` | JSON contract version, currently `1` |
|
|
143
223
|
| `ok` | `true` when no error-severity issue was found |
|
|
224
|
+
| `config` | config file metadata and checks used, or `{ "enabled": false, ... }` |
|
|
225
|
+
| `profile` | selected guard profile, for example `custom`, `smoke`, `registry`, `ci`, or `strict` |
|
|
144
226
|
| `command` | command and arguments that were validated |
|
|
145
227
|
| `protocol` | MCP protocol version sent by the guard |
|
|
146
228
|
| `negotiatedProtocol` | protocol version returned by the server, when available |
|
|
147
229
|
| `initialized` | whether the server completed the initialize handshake |
|
|
148
230
|
| `operation` | post-initialize request result, or `null` when `--request` was not used |
|
|
231
|
+
| `operations` | all explicit post-initialize requests, including config requests and safe tool calls |
|
|
232
|
+
| `adversarial` | opt-in adversarial probe results, including status, risk text, and per-probe issue codes |
|
|
233
|
+
| `toolSchema` | summary of `tools/list` metadata validation when that operation was requested or probed from an advertised tools capability |
|
|
234
|
+
| `capabilityProbes` | whether advertised capability list probes were enabled for this run |
|
|
235
|
+
| `capabilityKeys` | sorted capability keys returned by `initialize` for a single run; repeat mode exposes this inside each `runs` entry |
|
|
236
|
+
| `capabilityChecks` | advertised capability probes observed during a single run; repeat mode exposes this inside each `runs` entry |
|
|
237
|
+
| `drift` | repeat-run comparison summary for negotiated protocol, advertised capabilities, tool names/counts, and resource/prompt list counts |
|
|
149
238
|
| `process` | startup, timeout, exit code, signal, and guard-termination metadata for a single run; repeat mode exposes this inside each `runs` entry |
|
|
150
239
|
| `checks` | badge-friendly per-class statuses |
|
|
151
240
|
| `issueClasses` | registry-friendly summary grouped by `installRuntime`, `stdioTransport`, and `mcpProtocol` |
|
|
152
241
|
| `fingerprint` | redacted reproducibility metadata for debugging registry and CI runs |
|
|
153
242
|
| `issues` | machine-readable diagnostics with `class`, `severity`, `code`, and `message`; repeat mode also adds `run` |
|
|
154
243
|
| `staticScan` | whether source scanning was enabled and whether findings fail the command |
|
|
155
|
-
| `staticFindings` | source scan findings with file, line, and message |
|
|
244
|
+
| `staticFindings` | source scan findings with language, file, line, reason, and message |
|
|
156
245
|
| `runs` | per-run results when `--repeat` is used |
|
|
157
246
|
|
|
158
|
-
Check statuses are `pass`, `fail`, `warning`, or `skipped`. The `checks` object separates the signal into `initialize`, `stdout`, `jsonRpc`, `operation`, `process`, `pythonBuffering`, `staticScan`, and `repeat`, each with stable `status` and `issueCodes` fields. When `--repeat` is used, `checks.repeat` also includes `runs`, `passedRuns`, and `failedRuns`; each entry in `runs` is a normal schema-versioned result for that individual guard run.
|
|
247
|
+
Check statuses are `pass`, `fail`, `warning`, or `skipped`. The `checks` object separates the signal into `initialize`, `stdout`, `jsonRpc`, `operation`, `capabilities`, `toolSchema`, `adversarial`, `process`, `pythonBuffering`, `staticScan`, and `repeat`, each with stable `status` and `issueCodes` fields. When `--repeat` is used, `checks.repeat` also includes `runs`, `passedRuns`, and `failedRuns`; each entry in `runs` is a normal schema-versioned result for that individual guard run.
|
|
159
248
|
|
|
160
249
|
`issueClasses` is additive to `checks`. It groups issue codes by the kind of problem a registry or client should display:
|
|
161
250
|
|
|
@@ -171,12 +260,22 @@ Current issue-code mapping:
|
|
|
171
260
|
| --- | --- |
|
|
172
261
|
| `installRuntime` | `initialize-timeout`, `operation-missing-response`, `operation-timeout`, `python-buffered-stdio`, `server-crashed`, `server-exited`, `spawn-failed` |
|
|
173
262
|
| `stdioTransport` | `static-stdout-write`, `stdout-content-length-framing`, `stdout-empty-line`, `stdout-non-json`, `stdout-without-newline` |
|
|
174
|
-
| `mcpProtocol` | `initialize-error`, `initialize-invalid-capabilities`, `initialize-invalid-protocol-version`, `initialize-invalid-result`, `initialize-invalid-server-info`, `initialize-missing-capabilities`, `initialize-missing-protocol-version`, `initialize-missing-server-info`, `notification-response`, `operation-error`, `response-id-mismatch`, `response-id-type-mismatch`, `stdout-invalid-json-rpc`, `stdout-unexpected-request-id` |
|
|
263
|
+
| `mcpProtocol` | `adversarial-invalid-method-result`, `adversarial-invalid-params-result`, `adversarial-malformed-json-result`, `adversarial-notification-response`, `adversarial-probe-crash`, `adversarial-probe-invalid-stdout`, `adversarial-probe-timeout`, `adversarial-tool-call-result`, `capability-list-error`, `capability-list-missing-response`, `capability-list-timeout`, `capability-list-unsupported`, `initialize-error`, `initialize-invalid-capabilities`, `initialize-invalid-protocol-version`, `initialize-invalid-result`, `initialize-invalid-server-info`, `initialize-missing-capabilities`, `initialize-missing-protocol-version`, `initialize-missing-server-info`, `notification-response`, `operation-error`, `repeat-capability-drift`, `repeat-list-shape-drift`, `repeat-protocol-drift`, `repeat-tool-drift`, `response-id-mismatch`, `response-id-type-mismatch`, `stdout-invalid-json-rpc`, `stdout-unexpected-request-id`, `tool-description-missing`, `tool-input-schema-invalid`, `tool-input-schema-required-missing`, `tool-name-duplicate`, `tool-name-invalid`, `tools-list-invalid-result` |
|
|
175
264
|
|
|
176
265
|
Initialize lifecycle checks are part of the MCP protocol class. Missing or invalid `protocolVersion` and `capabilities` fail the run before the guard sends `notifications/initialized` or any normal request. Missing or invalid `serverInfo` is warning-level so registries can surface incomplete metadata without confusing it with a broken transport.
|
|
177
266
|
|
|
178
267
|
JSON-RPC invariant checks distinguish wrong response ids from id type round-trip problems and fail servers that respond to `notifications/initialized`. JSON-RPC error frames must be structured with numeric `code` and string `message` fields.
|
|
179
268
|
|
|
269
|
+
Tool schema checks run when `tools/list` receives a successful result, either from `--request tools/list` or from the advertised tools capability probe. Duplicate or invalid tool names, missing `inputSchema`, invalid schema shapes, and `required` entries that are absent from `properties` are MCP protocol failures. Missing tool descriptions are warning-level so registries can show quality guidance without marking the server broken.
|
|
270
|
+
|
|
271
|
+
Capability honesty checks are additive. If `initialize` advertises `capabilities.tools`, `capabilities.resources`, or `capabilities.prompts`, the guard probes the matching `tools/list`, `resources/list`, or `prompts/list` method after `notifications/initialized`. Unadvertised capabilities are `skipped`, not failed. `capability-list-unsupported` means an advertised list method returned method-not-found; `capability-list-error`, `capability-list-timeout`, and `capability-list-missing-response` mean the advertised list method existed in the contract but failed at runtime.
|
|
272
|
+
|
|
273
|
+
Adversarial probes are additive and opt-in. Their failures are classified as `mcpProtocol`, not install/runtime failures, so registries can distinguish "the package cannot start" from "the server started but mishandled strict JSON-RPC/MCP inputs." `malformed-json` accepts either a structured parse error or silence after a short observation window; a crash is a protocol failure. `notification` expects no response.
|
|
274
|
+
|
|
275
|
+
Repeat drift checks compare successful initialized runs against the first initialized run. Negotiated protocol changes, advertised capability key changes, added or removed tool names, tool count changes, and resource/prompt list count changes are warning-level `repeat-*` issues. Tool order is normalized before comparison, so order-only changes do not warn.
|
|
276
|
+
|
|
277
|
+
The repeat `drift` object has stable `status`, `issueCodes`, `baselineRun`, and `comparedRuns` fields. Its nested `negotiatedProtocol`, `capabilities`, `tools`, `lists.resources`, and `lists.prompts` sections include `changedRuns` so registries can show exactly what changed between cold and warm starts.
|
|
278
|
+
|
|
180
279
|
Runtime issue codes remain backward-compatible. For finer registry display, runtime issues may also include a stable `detailCode`:
|
|
181
280
|
|
|
182
281
|
| Existing issue code | Detail codes |
|
|
@@ -188,7 +287,7 @@ Runtime issue codes remain backward-compatible. For finer registry display, runt
|
|
|
188
287
|
| `operation-missing-response` | `clean-exit-during-operation`, `nonzero-exit-during-operation`, `signal-exit-during-operation` |
|
|
189
288
|
| `server-crashed` | `nonzero-exit-after-initialize`, `signal-exit-after-initialize` |
|
|
190
289
|
|
|
191
|
-
`process` records the observed lifecycle even when the run passes. `outcome` is one of `starting`, `running`, `exited`, `timeout`, `spawn-failed`, or `guard-terminated`; `starting` is the transient initial value while the child is being created, not an expected terminal outcome. `phase` is `startup`, `initialize`, `operation`, or `post-initialize`. `exitCode` and `signal` are included when the process exits before the guard finishes; timeout runs include `timedOut`, `timeoutCode`, `timeoutMs`, and guard kill metadata. `spawnError` is either `null` or an object with `code` and `message`; the matching `spawn-failed` issue also exposes `spawnErrorCode`.
|
|
290
|
+
`process` records the observed lifecycle even when the run passes. `outcome` is one of `starting`, `running`, `exited`, `timeout`, `spawn-failed`, or `guard-terminated`; `starting` is the transient initial value while the child is being created, not an expected terminal outcome. `phase` is `startup`, `initialize`, `operation`, `adversarial`, or `post-initialize`. `exitCode` and `signal` are included when the process exits before the guard finishes; timeout runs include `timedOut`, `timeoutCode`, `timeoutMs`, and guard kill metadata. `spawnError` is either `null` or an object with `code` and `message`; the matching `spawn-failed` issue also exposes `spawnErrorCode`.
|
|
192
291
|
|
|
193
292
|
Spawn failure shape:
|
|
194
293
|
|
|
@@ -205,8 +304,11 @@ Registry display flow:
|
|
|
205
304
|
| --- | --- |
|
|
206
305
|
| 1 | Show `issueClasses` first so install/runtime, stdio transport, and MCP protocol failures stay distinct |
|
|
207
306
|
| 2 | Use `fingerprint.command`, `fingerprint.cwd`, and `fingerprint.package` to show what was actually run |
|
|
208
|
-
| 3 |
|
|
209
|
-
| 4 | Show `
|
|
307
|
+
| 3 | Show `checks.capabilities` as advertised MCP surface honesty |
|
|
308
|
+
| 4 | Show `checks.toolSchema` as tool metadata quality, separate from startup and stdio transport health |
|
|
309
|
+
| 5 | Show `drift` warnings as stability advisories, not hard failures, unless another check failed |
|
|
310
|
+
| 6 | Compare `fingerprint.system`, `fingerprint.runtimes`, and `fingerprint.timings` before marking a package broken |
|
|
311
|
+
| 7 | Show `fingerprint.env.names` only when debugging; never ask users to paste secret values |
|
|
210
312
|
|
|
211
313
|
Example:
|
|
212
314
|
|
|
@@ -214,8 +316,23 @@ Example:
|
|
|
214
316
|
{
|
|
215
317
|
"schemaVersion": 1,
|
|
216
318
|
"ok": true,
|
|
319
|
+
"config": {
|
|
320
|
+
"enabled": false,
|
|
321
|
+
"path": "",
|
|
322
|
+
"resolvedPath": "",
|
|
323
|
+
"checks": {
|
|
324
|
+
"command": false,
|
|
325
|
+
"cwd": false,
|
|
326
|
+
"envNames": [],
|
|
327
|
+
"requests": [],
|
|
328
|
+
"safeToolCalls": [],
|
|
329
|
+
"adversarialProbes": [],
|
|
330
|
+
"adversarialToolCalls": []
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
"profile": "custom",
|
|
217
334
|
"fingerprint": {
|
|
218
|
-
"guard": { "name": "mcp-stdio-guard", "version": "0.
|
|
335
|
+
"guard": { "name": "mcp-stdio-guard", "version": "0.5.0" },
|
|
219
336
|
"command": {
|
|
220
337
|
"executable": "node",
|
|
221
338
|
"args": ["./server.js"],
|
|
@@ -227,9 +344,27 @@ Example:
|
|
|
227
344
|
"exists": true
|
|
228
345
|
},
|
|
229
346
|
"protocol": "2025-11-25",
|
|
347
|
+
"config": {
|
|
348
|
+
"enabled": false,
|
|
349
|
+
"path": "",
|
|
350
|
+
"resolvedPath": "",
|
|
351
|
+
"checks": {
|
|
352
|
+
"command": false,
|
|
353
|
+
"cwd": false,
|
|
354
|
+
"envNames": [],
|
|
355
|
+
"requests": [],
|
|
356
|
+
"safeToolCalls": [],
|
|
357
|
+
"adversarialProbes": [],
|
|
358
|
+
"adversarialToolCalls": []
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"profile": "custom",
|
|
230
362
|
"timeoutMs": 5000,
|
|
231
363
|
"repeat": 1,
|
|
232
|
-
"
|
|
364
|
+
"capabilityProbes": true,
|
|
365
|
+
"adversarialProbes": [],
|
|
366
|
+
"operation": { "method": "tools/list", "hasParams": false, "source": "cli-request", "safeToolCallName": "" },
|
|
367
|
+
"operations": [{ "method": "tools/list", "hasParams": false, "source": "cli-request", "safeToolCallName": "" }],
|
|
233
368
|
"system": { "platform": "darwin", "arch": "arm64", "osRelease": "25.0.0" },
|
|
234
369
|
"runtimes": {
|
|
235
370
|
"node": { "version": "v24.0.0", "role": "guard-and-target" }
|
|
@@ -258,16 +393,42 @@ Example:
|
|
|
258
393
|
"killReason": "guard-finished",
|
|
259
394
|
"spawnError": null
|
|
260
395
|
},
|
|
396
|
+
"capabilityProbes": true,
|
|
397
|
+
"adversarial": { "enabled": false, "probes": [] },
|
|
398
|
+
"capabilityKeys": ["tools"],
|
|
399
|
+
"capabilityChecks": {
|
|
400
|
+
"tools": { "advertised": true, "method": "tools/list", "responded": true, "itemCount": 2, "error": null },
|
|
401
|
+
"resources": { "advertised": false, "method": "resources/list", "responded": false, "itemCount": null, "error": null },
|
|
402
|
+
"prompts": { "advertised": false, "method": "prompts/list", "responded": false, "itemCount": null, "error": null }
|
|
403
|
+
},
|
|
261
404
|
"issueClasses": {
|
|
262
405
|
"installRuntime": { "status": "pass", "issueCodes": [] },
|
|
263
406
|
"stdioTransport": { "status": "pass", "issueCodes": [] },
|
|
264
407
|
"mcpProtocol": { "status": "pass", "issueCodes": [] }
|
|
265
408
|
},
|
|
409
|
+
"toolSchema": {
|
|
410
|
+
"checked": true,
|
|
411
|
+
"toolCount": 2,
|
|
412
|
+
"toolNames": ["read_file", "search"],
|
|
413
|
+
"validToolCount": 2,
|
|
414
|
+
"warningCount": 0,
|
|
415
|
+
"errorCount": 0,
|
|
416
|
+
"duplicateNames": []
|
|
417
|
+
},
|
|
266
418
|
"checks": {
|
|
267
419
|
"initialize": { "status": "pass", "issueCodes": [] },
|
|
268
420
|
"stdout": { "status": "pass", "issueCodes": [] },
|
|
269
421
|
"jsonRpc": { "status": "pass", "issueCodes": [] },
|
|
270
422
|
"operation": { "status": "pass", "issueCodes": [] },
|
|
423
|
+
"capabilities": {
|
|
424
|
+
"status": "pass",
|
|
425
|
+
"issueCodes": [],
|
|
426
|
+
"tools": { "status": "pass", "issueCodes": [], "advertised": true, "method": "tools/list", "responded": true, "itemCount": 2 },
|
|
427
|
+
"resources": { "status": "skipped", "issueCodes": [], "advertised": false, "method": "resources/list", "responded": false, "itemCount": null },
|
|
428
|
+
"prompts": { "status": "skipped", "issueCodes": [], "advertised": false, "method": "prompts/list", "responded": false, "itemCount": null }
|
|
429
|
+
},
|
|
430
|
+
"toolSchema": { "status": "pass", "issueCodes": [] },
|
|
431
|
+
"adversarial": { "status": "skipped", "issueCodes": [] },
|
|
271
432
|
"process": { "status": "pass", "issueCodes": [] },
|
|
272
433
|
"pythonBuffering": { "status": "pass", "issueCodes": [] },
|
|
273
434
|
"staticScan": { "status": "skipped", "issueCodes": [] },
|
|
@@ -282,7 +443,7 @@ The guard is registry-agnostic. It does not care whether an install command came
|
|
|
282
443
|
|
|
283
444
|
```yaml
|
|
284
445
|
- run: npm ci
|
|
285
|
-
- run: npx mcp-stdio-guard --scan src --
|
|
446
|
+
- run: npx mcp-stdio-guard --profile ci --scan src --request tools/list -- node ./server.js
|
|
286
447
|
```
|
|
287
448
|
|
|
288
449
|
## Output
|
|
@@ -296,6 +457,7 @@ frames: 2 stdout / 0 invalid
|
|
|
296
457
|
stderr: 0 lines
|
|
297
458
|
protocol: 2025-11-25
|
|
298
459
|
request: tools/list responded
|
|
460
|
+
tool schemas: 2/2 valid
|
|
299
461
|
```
|
|
300
462
|
|
|
301
463
|
Polluted stdout:
|
|
@@ -314,7 +476,7 @@ request: tools/list responded
|
|
|
314
476
|
|
|
315
477
|
- Runtime dependencies: zero.
|
|
316
478
|
- Default behavior: validate the real process boundary.
|
|
317
|
-
- Optional static scan: intentionally simple and conservative.
|
|
479
|
+
- Optional static scan: intentionally simple and conservative; catches common JavaScript and Python stdout writes, stdout logging handlers, and visible startup-output risks.
|
|
318
480
|
- CI posture: fail on protocol corruption, crashes, and missing responses.
|
|
319
481
|
- Promotion promise: no fake stars, no spam, just a tool that catches a real MCP failure mode.
|
|
320
482
|
|