@k08200/mcp-probe 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +297 -13
  2. package/dist/checker.d.ts +2 -11
  3. package/dist/checker.d.ts.map +1 -1
  4. package/dist/checker.js +93 -9
  5. package/dist/checker.js.map +1 -1
  6. package/dist/cli.js +132 -5
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts +4 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +143 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/protocols/mcp-client.d.ts.map +1 -1
  13. package/dist/protocols/mcp-client.js +225 -64
  14. package/dist/protocols/mcp-client.js.map +1 -1
  15. package/dist/reporters/badge.d.ts +12 -0
  16. package/dist/reporters/badge.d.ts.map +1 -0
  17. package/dist/reporters/badge.js +34 -0
  18. package/dist/reporters/badge.js.map +1 -0
  19. package/dist/reporters/github.d.ts +7 -0
  20. package/dist/reporters/github.d.ts.map +1 -0
  21. package/dist/reporters/github.js +138 -0
  22. package/dist/reporters/github.js.map +1 -0
  23. package/dist/reporters/json-reporter.d.ts +2 -2
  24. package/dist/reporters/json-reporter.d.ts.map +1 -1
  25. package/dist/reporters/json-reporter.js.map +1 -1
  26. package/dist/reporters/terminal.d.ts +2 -1
  27. package/dist/reporters/terminal.d.ts.map +1 -1
  28. package/dist/reporters/terminal.js +38 -0
  29. package/dist/reporters/terminal.js.map +1 -1
  30. package/dist/types.d.ts +73 -2
  31. package/dist/types.d.ts.map +1 -1
  32. package/examples/datadog.tools.json +13 -0
  33. package/examples/github-actions/fleet.yml +25 -0
  34. package/examples/github-actions/remote-server.yml +24 -0
  35. package/examples/github-actions/single-server.yml +21 -0
  36. package/examples/mcp-probe.config.json +23 -0
  37. package/examples/recipes/README.md +35 -0
  38. package/examples/recipes/datadog.tools.json +23 -0
  39. package/examples/recipes/gmail.tools.json +18 -0
  40. package/examples/recipes/supabase.tools.json +19 -0
  41. package/examples/self-check.config.json +14 -0
  42. package/examples/self-check.tools.json +17 -0
  43. package/package.json +4 -3
package/README.md CHANGED
@@ -1,14 +1,47 @@
1
1
  # mcp-probe
2
2
 
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
- [![npm](https://img.shields.io/npm/v/mcp-probe)](https://www.npmjs.com/package/mcp-probe)
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/mcp-probe)](package.json)
6
+ [![Node.js](https://img.shields.io/node/v/@k08200/mcp-probe)](package.json)
7
7
 
8
- **Quality checker for MCP servers.** Validates protocol handshake, tool discovery, and response latency in one command.
8
+ **Quality checker for MCP servers.** Validates protocol handshake, discovery, optional tool-call dry-runs, and response latency in one command.
9
9
 
10
10
  The `npm audit` for the [MCP](https://modelcontextprotocol.io) ecosystem — because [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) lists 200+ servers and there was no way to know if they actually worked.
11
11
 
12
+ ## Quick Start for CI
13
+
14
+ Add this workflow to any project that depends on MCP servers:
15
+
16
+ ```yaml
17
+ name: MCP Probe
18
+
19
+ on:
20
+ pull_request:
21
+ push:
22
+ branches: [main]
23
+
24
+ jobs:
25
+ mcp-probe:
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 5
28
+
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+
32
+ - name: Validate MCP server
33
+ run: |
34
+ npx @k08200/mcp-probe @your-org/your-mcp-server \
35
+ --probe-tools \
36
+ --github-summary
37
+ ```
38
+
39
+ For teams running several MCP servers, use a config file:
40
+
41
+ ```bash
42
+ npx @k08200/mcp-probe --config mcp-probe.config.json --github-summary
43
+ ```
44
+
12
45
  ```bash
13
46
  npx @k08200/mcp-probe @modelcontextprotocol/server-memory
14
47
  ```
@@ -64,11 +97,38 @@ mcp-probe @modelcontextprotocol/server-filesystem /tmp /Users/me/projects
64
97
  # Check a local server file
65
98
  mcp-probe ./my-server.js
66
99
 
100
+ # Check a remote Streamable HTTP MCP server
101
+ mcp-probe https://mcp.example.com/mcp
102
+
103
+ # Check a legacy HTTP+SSE MCP server
104
+ mcp-probe https://mcp.example.com/sse --transport sse
105
+
106
+ # Pass headers to remote servers
107
+ mcp-probe https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
108
+
109
+ # Ignore known noisy stderr lines when classifying startup failures
110
+ mcp-probe @scope/server --stderr-allow "^Warning:" --stderr-fatal "panic|FATAL"
111
+
67
112
  # JSON output for CI / scripting
68
113
  mcp-probe @scope/server --output json
69
114
 
70
115
  # Custom timeout (default: 10000ms)
71
116
  mcp-probe @scope/server --timeout 30000
117
+
118
+ # Batch-check several servers from a config file
119
+ mcp-probe --config mcp-probe.config.json
120
+
121
+ # Write GitHub Actions summary and annotations
122
+ mcp-probe --config mcp-probe.config.json --github-summary
123
+
124
+ # Write shields.io endpoint JSON for a status badge
125
+ mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
126
+
127
+ # Call tools with generated minimal inputs
128
+ mcp-probe @scope/server --probe-tools
129
+
130
+ # Call tools with real sample inputs from a sidecar file
131
+ mcp-probe @scope/server --tools-file .mcp-probe.json
72
132
  ```
73
133
 
74
134
  ## What it checks
@@ -79,6 +139,151 @@ mcp-probe @scope/server --timeout 30000
79
139
  | **MCP protocol handshake** | Does the server respond to `initialize`? Measures connect latency. |
80
140
  | **Tools discovery** | Does `tools/list` return results? Measures list latency. |
81
141
  | **Tool schema validation** | Are all tool schemas well-formed? |
142
+ | **Resources discovery** | Runs `resources/list` when the server advertises resources. |
143
+ | **Prompts discovery** | Runs `prompts/list` when the server advertises prompts. |
144
+ | **Tool call dry-run** | Optional `tools/call` checks via `--probe-tools` or `--tools-file`. |
145
+
146
+ ## Batch CI gate
147
+
148
+ Use `--config` when a project depends on several MCP servers and you want one CI command to validate all of them:
149
+
150
+ ```json
151
+ {
152
+ "timeoutMs": 10000,
153
+ "servers": [
154
+ {
155
+ "name": "memory",
156
+ "target": "@modelcontextprotocol/server-memory",
157
+ "probeTools": true
158
+ },
159
+ {
160
+ "name": "datadog",
161
+ "target": "https://mcp.example.com/mcp",
162
+ "transport": "http",
163
+ "headers": {
164
+ "Authorization": "Bearer ${DATADOG_MCP_TOKEN}"
165
+ },
166
+ "stderr": {
167
+ "allow": ["^Warning:", "missing optional config"],
168
+ "fatal": ["panic", "FATAL"]
169
+ },
170
+ "toolsFile": "./recipes/datadog.tools.json"
171
+ }
172
+ ]
173
+ }
174
+ ```
175
+
176
+ Run:
177
+
178
+ ```bash
179
+ mcp-probe --config mcp-probe.config.json
180
+ ```
181
+
182
+ 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.
183
+
184
+ Config fields:
185
+
186
+ | Field | Description |
187
+ |-------|-------------|
188
+ | `timeoutMs` | Optional global timeout in milliseconds. CLI `--timeout` is used when omitted. |
189
+ | `servers[].name` | Human-readable name shown in batch output. |
190
+ | `servers[].target` | npm package, local server path, or remote MCP URL. |
191
+ | `servers[].serverArgs` | Optional arguments passed to the MCP server. |
192
+ | `servers[].transport` | Optional transport override: `stdio`, `http`, or `sse`. URL targets default to `http`; package/path targets default to `stdio`. |
193
+ | `servers[].headers` | Optional HTTP headers for remote MCP servers. `${ENV_VAR}` placeholders are expanded at runtime. |
194
+ | `servers[].stderr.allow` | Optional regex patterns for stderr lines that should be ignored when startup fails. |
195
+ | `servers[].stderr.fatal` | Optional regex patterns for stderr lines that should always be treated as the startup failure reason. |
196
+ | `servers[].probeTools` | Enables dry-run tool calls for that server. |
197
+ | `servers[].toolsFile` | Sidecar input file for meaningful `tools/call` samples. Relative paths resolve from the config file directory. |
198
+
199
+ ## Stderr classification
200
+
201
+ 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.
202
+
203
+ mcp-probe has built-in warning filters and also lets you declare server-specific regexes:
204
+
205
+ ```bash
206
+ mcp-probe @scope/server \
207
+ --stderr-allow "^Warning:" \
208
+ --stderr-allow "missing optional config" \
209
+ --stderr-fatal "panic|FATAL"
210
+ ```
211
+
212
+ For batch checks, put the rules in `mcp-probe.config.json`:
213
+
214
+ ```json
215
+ {
216
+ "name": "datadog",
217
+ "target": "https://mcp.example.com/mcp",
218
+ "stderr": {
219
+ "allow": ["^Warning:", "missing optional config"],
220
+ "fatal": ["panic", "FATAL"]
221
+ }
222
+ }
223
+ ```
224
+
225
+ `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.
226
+
227
+ ## Tool call dry-runs
228
+
229
+ 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.
230
+
231
+ 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:
232
+
233
+ ```json
234
+ {
235
+ "tools": {
236
+ "logs_query": {
237
+ "input": {
238
+ "query": "service:web status:error",
239
+ "timeframe": "1h"
240
+ },
241
+ "expect": {
242
+ "not_error_code": [401, 403]
243
+ }
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ Save this as `.mcp-probe.json` in your project root and run:
250
+
251
+ ```bash
252
+ mcp-probe @your-org/datadog-mcp --probe-tools
253
+ ```
254
+
255
+ Or pass an explicit path:
256
+
257
+ ```bash
258
+ mcp-probe @your-org/datadog-mcp --tools-file ./ci/mcp-tools.json
259
+ ```
260
+
261
+ 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.
262
+
263
+ ## Status badges
264
+
265
+ Use `--badge-file` to write a [shields.io endpoint](https://shields.io/badges/endpoint-badge) JSON file:
266
+
267
+ ```bash
268
+ mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
269
+ ```
270
+
271
+ Example output:
272
+
273
+ ```json
274
+ {
275
+ "schemaVersion": 1,
276
+ "label": "mcp fleet",
277
+ "message": "2 pass, 1 warn",
278
+ "color": "yellow"
279
+ }
280
+ ```
281
+
282
+ Host that JSON file anywhere public and reference it from your README:
283
+
284
+ ```markdown
285
+ ![MCP readiness](https://img.shields.io/endpoint?url=https://example.com/mcp-probe-badge.json)
286
+ ```
82
287
 
83
288
  ## Exit codes
84
289
 
@@ -89,17 +294,90 @@ mcp-probe @scope/server --timeout 30000
89
294
 
90
295
  ## CI integration
91
296
 
297
+ Single server workflow:
298
+
92
299
  ```yaml
93
300
  # .github/workflows/mcp-probe.yml
94
- - name: Validate MCP server
95
- run: npx @k08200/mcp-probe @your-org/your-mcp-server
96
- timeout-minutes: 2
301
+ name: MCP Probe
302
+
303
+ on:
304
+ pull_request:
305
+ push:
306
+ branches: [main]
307
+
308
+ jobs:
309
+ mcp-probe:
310
+ runs-on: ubuntu-latest
311
+ timeout-minutes: 5
312
+
313
+ steps:
314
+ - uses: actions/checkout@v4
315
+
316
+ - name: Validate MCP server
317
+ run: |
318
+ npx @k08200/mcp-probe @your-org/your-mcp-server \
319
+ --probe-tools \
320
+ --github-summary \
321
+ --badge-file mcp-probe-badge.json
322
+ ```
323
+
324
+ Fleet workflow:
325
+
326
+ ```yaml
327
+ # .github/workflows/mcp-fleet.yml
328
+ name: MCP Fleet Probe
329
+
330
+ on:
331
+ pull_request:
332
+ push:
333
+ branches: [main]
334
+ schedule:
335
+ - cron: "0 * * * *"
336
+
337
+ jobs:
338
+ mcp-probe:
339
+ runs-on: ubuntu-latest
340
+ timeout-minutes: 10
341
+
342
+ steps:
343
+ - uses: actions/checkout@v4
344
+
345
+ - name: Validate MCP fleet
346
+ run: |
347
+ npx @k08200/mcp-probe \
348
+ --config mcp-probe.config.json \
349
+ --github-summary \
350
+ --badge-file mcp-probe-badge.json
97
351
  ```
98
352
 
353
+ 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.
354
+
355
+ Copy-ready examples live in [`examples/github-actions`](examples/github-actions):
356
+
357
+ | Example | Use case |
358
+ |---------|----------|
359
+ | [`single-server.yml`](examples/github-actions/single-server.yml) | Validate one stdio MCP package. |
360
+ | [`fleet.yml`](examples/github-actions/fleet.yml) | Validate several MCP servers from `mcp-probe.config.json` on PRs and hourly schedules. |
361
+ | [`remote-server.yml`](examples/github-actions/remote-server.yml) | Validate a remote Streamable HTTP MCP server with auth headers. |
362
+
363
+ 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.
364
+
365
+ ## Recipes
366
+
367
+ 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):
368
+
369
+ | Recipe | Focus |
370
+ |--------|-------|
371
+ | [`datadog.tools.json`](examples/recipes/datadog.tools.json) | Logs/metrics queries that reveal auth handoff and downstream API failures. |
372
+ | [`supabase.tools.json`](examples/recipes/supabase.tools.json) | Project visibility and a harmless `select 1` SQL path. |
373
+ | [`gmail.tools.json`](examples/recipes/gmail.tools.json) | OAuth/token handoff and read-only mailbox access. |
374
+
375
+ 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.
376
+
99
377
  ## JSON output
100
378
 
101
379
  ```bash
102
- mcp-probe @modelcontextprotocol/server-memory --output json
380
+ mcp-probe @modelcontextprotocol/server-memory --probe-tools --output json
103
381
  ```
104
382
 
105
383
  ```json
@@ -111,10 +389,14 @@ mcp-probe @modelcontextprotocol/server-memory --output json
111
389
  { "name": "Target resolution", "status": "pass", "message": "npx --yes @modelcontextprotocol/server-memory" },
112
390
  { "name": "MCP protocol handshake", "status": "pass", "message": "memory-server v0.6.3", "latencyMs": 1392 },
113
391
  { "name": "Tools discovery", "status": "pass", "message": "Found 9 tools", "latencyMs": 33 },
114
- { "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" }
392
+ { "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" },
393
+ { "name": "Tool call dry-run", "status": "pass", "message": "9 passed (2 sidecar, 7 auto)" }
115
394
  ],
116
395
  "serverInfo": { "name": "memory-server", "version": "0.6.3", "capabilities": ["tools"] },
117
396
  "tools": [{ "name": "create_entities", "description": "Create multiple new entities in the knowledge graph" }],
397
+ "toolCallResults": [
398
+ { "tool": "read_graph", "status": "pass", "latencyMs": 41, "source": "auto" }
399
+ ],
118
400
  "totalLatencyMs": 1455
119
401
  }
120
402
  ```
@@ -129,11 +411,13 @@ mcp-probe @modelcontextprotocol/server-memory --output json
129
411
 
130
412
  ## Roadmap
131
413
 
132
- - [ ] `resources/list` and `prompts/list` checks
133
- - [ ] HTTP/SSE transport support
134
- - [ ] Batch checking from a file (`mcp-probe --list servers.txt`)
135
- - [ ] Badge generation (`mcp-probe --badge > badge.json`)
136
- - [ ] Weekly quality report for awesome-mcp-servers
414
+ - [x] Self-check batch workflow for mcp-probe itself
415
+ - [x] HTTP/SSE transport support
416
+ - [x] Batch checking from a config file (`mcp-probe --config mcp-probe.config.json`)
417
+ - [x] GitHub Actions summary and annotations
418
+ - [x] Badge generation (`mcp-probe --badge-file mcp-probe-badge.json`)
419
+ - [x] Structured stderr classification rules
420
+ - [x] Server-specific recipe examples for Datadog, Supabase, and Gmail MCP servers
137
421
 
138
422
  ## Contributing
139
423
 
package/dist/checker.d.ts CHANGED
@@ -1,13 +1,4 @@
1
- import type { CheckReport } from './types.js';
2
- type CheckOptions = {
3
- target: string;
4
- serverArgs?: string[];
5
- timeoutMs: number;
6
- };
7
- export declare function resolveTarget(target: string): {
8
- command: string;
9
- args: string[];
10
- };
1
+ import type { CheckOptions, CheckReport, ResolvedTarget, TransportMode } from './types.js';
2
+ export declare function resolveTarget(target: string, transport?: TransportMode): ResolvedTarget;
11
3
  export declare function checkMcpServer(options: CheckOptions): Promise<CheckReport>;
12
- export {};
13
4
  //# sourceMappingURL=checker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAa,WAAW,EAAe,MAAM,YAAY,CAAC;AAEtE,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKjF;AAQD,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA4FhF"}
1
+ {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,YAAY,EAAE,WAAW,EAAe,cAAc,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AAQhI,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAavF;AAiDD,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8HhF"}
package/dist/checker.js CHANGED
@@ -1,9 +1,57 @@
1
+ import { existsSync, readFileSync } from 'fs';
1
2
  import { probeMcpServer } from './protocols/mcp-client.js';
2
- export function resolveTarget(target) {
3
+ const SIDECAR_FILENAME = '.mcp-probe.json';
4
+ function isUrlTarget(target) {
5
+ return /^https?:\/\//i.test(target);
6
+ }
7
+ export function resolveTarget(target, transport) {
8
+ if (transport === 'http' || transport === 'sse') {
9
+ return { transport, url: target };
10
+ }
11
+ if (isUrlTarget(target)) {
12
+ return { transport: 'http', url: target };
13
+ }
3
14
  if (target.startsWith('.') || target.startsWith('/')) {
4
- return { command: 'node', args: [target] };
15
+ return { transport: 'stdio', command: 'node', args: [target] };
5
16
  }
6
- return { command: 'npx', args: ['--yes', target] };
17
+ return { transport: 'stdio', command: 'npx', args: ['--yes', target] };
18
+ }
19
+ function loadSidecar(toolsFile) {
20
+ const path = toolsFile ?? SIDECAR_FILENAME;
21
+ if (!existsSync(path)) {
22
+ if (!toolsFile)
23
+ return undefined;
24
+ throw new Error(`Cannot read tools file: ${path}`);
25
+ }
26
+ let parsed;
27
+ try {
28
+ const raw = readFileSync(path, 'utf8');
29
+ parsed = JSON.parse(raw);
30
+ }
31
+ catch {
32
+ throw new Error(`Invalid tools file JSON: ${path}`);
33
+ }
34
+ if (!parsed || typeof parsed !== 'object') {
35
+ throw new Error(`Invalid tools file: ${path} must be an object`);
36
+ }
37
+ const sidecar = parsed;
38
+ if (!sidecar.tools || typeof sidecar.tools !== 'object' || Array.isArray(sidecar.tools)) {
39
+ throw new Error(`Invalid tools file: ${path} must contain a tools object`);
40
+ }
41
+ for (const [toolName, entry] of Object.entries(sidecar.tools)) {
42
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
43
+ throw new Error(`Invalid tools file: ${toolName} entry must be an object`);
44
+ }
45
+ const toolEntry = entry;
46
+ if (!toolEntry.input || typeof toolEntry.input !== 'object' || Array.isArray(toolEntry.input)) {
47
+ throw new Error(`Invalid tools file: ${toolName}.input must be an object`);
48
+ }
49
+ const codes = toolEntry.expect?.not_error_code;
50
+ if (codes !== undefined && (!Array.isArray(codes) || !codes.every((c) => typeof c === 'number'))) {
51
+ throw new Error(`Invalid tools file: ${toolName}.expect.not_error_code must be a number array`);
52
+ }
53
+ }
54
+ return sidecar;
7
55
  }
8
56
  function deriveOverallStatus(checks) {
9
57
  if (checks.some((c) => c.status === 'fail'))
@@ -15,16 +63,30 @@ function deriveOverallStatus(checks) {
15
63
  export async function checkMcpServer(options) {
16
64
  const startTime = Date.now();
17
65
  const checks = [];
18
- const resolved = resolveTarget(options.target);
19
- const args = [...resolved.args, ...(options.serverArgs ?? [])];
20
- const { command } = resolved;
66
+ const resolved = resolveTarget(options.target, options.transport);
67
+ const args = [...(resolved.args ?? []), ...(options.serverArgs ?? [])];
68
+ const probeTools = options.probeTools || Boolean(options.toolsFile);
69
+ const resolutionMessage = resolved.transport === 'stdio'
70
+ ? `${resolved.command} ${args.join(' ')}`
71
+ : `${resolved.transport} ${resolved.url}`;
21
72
  checks.push({
22
73
  name: 'Target resolution',
23
74
  status: 'pass',
24
- message: `${command} ${args.join(' ')}`,
75
+ message: resolutionMessage,
25
76
  });
26
77
  try {
27
- const probe = await probeMcpServer({ command, args, timeoutMs: options.timeoutMs });
78
+ const sidecar = probeTools ? loadSidecar(options.toolsFile) : undefined;
79
+ const probe = await probeMcpServer({
80
+ transport: resolved.transport,
81
+ command: resolved.command,
82
+ args,
83
+ url: resolved.url,
84
+ headers: options.headers,
85
+ stderr: options.stderr,
86
+ timeoutMs: options.timeoutMs,
87
+ probeTools,
88
+ sidecar,
89
+ });
28
90
  checks.push({
29
91
  name: 'MCP protocol handshake',
30
92
  status: 'pass',
@@ -66,6 +128,26 @@ export async function checkMcpServer(options) {
66
128
  latencyMs: probe.promptsLatencyMs,
67
129
  });
68
130
  }
131
+ if (probe.toolCallResults && probe.toolCallResults.length > 0) {
132
+ const failed = probe.toolCallResults.filter((r) => r.status === 'fail');
133
+ const warned = probe.toolCallResults.filter((r) => r.status === 'warn');
134
+ const passed = probe.toolCallResults.filter((r) => r.status === 'pass');
135
+ const sidecarCount = probe.toolCallResults.filter((r) => r.source === 'sidecar').length;
136
+ const status = failed.length > 0 ? 'fail' : warned.length > 0 ? 'warn' : 'pass';
137
+ const parts = [];
138
+ if (passed.length > 0)
139
+ parts.push(`${passed.length} passed`);
140
+ if (warned.length > 0)
141
+ parts.push(`${warned.length} auth/permission errors`);
142
+ if (failed.length > 0)
143
+ parts.push(`${failed.length} failed`);
144
+ const sourceNote = sidecarCount > 0 ? ` (${sidecarCount} sidecar, ${probe.toolCallResults.length - sidecarCount} auto)` : '';
145
+ checks.push({
146
+ name: 'Tool call dry-run',
147
+ status,
148
+ message: parts.join(', ') + sourceNote,
149
+ });
150
+ }
69
151
  return {
70
152
  target: options.target,
71
153
  timestamp: new Date().toISOString(),
@@ -75,13 +157,15 @@ export async function checkMcpServer(options) {
75
157
  tools: probe.tools,
76
158
  resources: probe.resources,
77
159
  prompts: probe.prompts,
160
+ toolCallResults: probe.toolCallResults,
78
161
  totalLatencyMs: Date.now() - startTime,
79
162
  };
80
163
  }
81
164
  catch (error) {
82
165
  const message = error instanceof Error ? error.message : String(error);
166
+ const isSidecarError = message.includes('tools file');
83
167
  checks.push({
84
- name: 'MCP protocol handshake',
168
+ name: isSidecarError ? 'Tool sidecar' : 'MCP protocol handshake',
85
169
  status: 'fail',
86
170
  message,
87
171
  });
@@ -1 +1 @@
1
- {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}