@k08200/mcp-probe 0.1.0 → 0.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.
- package/README.md +224 -13
- package/dist/checker.d.ts +2 -11
- package/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +92 -9
- package/dist/checker.js.map +1 -1
- package/dist/cli.js +115 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +122 -0
- package/dist/config.js.map +1 -0
- package/dist/protocols/mcp-client.d.ts.map +1 -1
- package/dist/protocols/mcp-client.js +212 -61
- package/dist/protocols/mcp-client.js.map +1 -1
- package/dist/reporters/badge.d.ts +12 -0
- package/dist/reporters/badge.d.ts.map +1 -0
- package/dist/reporters/badge.js +34 -0
- package/dist/reporters/badge.js.map +1 -0
- package/dist/reporters/github.d.ts +7 -0
- package/dist/reporters/github.d.ts.map +1 -0
- package/dist/reporters/github.js +138 -0
- package/dist/reporters/github.js.map +1 -0
- package/dist/reporters/json-reporter.d.ts +2 -2
- package/dist/reporters/json-reporter.d.ts.map +1 -1
- package/dist/reporters/json-reporter.js.map +1 -1
- package/dist/reporters/terminal.d.ts +2 -1
- package/dist/reporters/terminal.d.ts.map +1 -1
- package/dist/reporters/terminal.js +38 -0
- package/dist/reporters/terminal.js.map +1 -1
- package/dist/types.d.ts +66 -2
- package/dist/types.d.ts.map +1 -1
- package/examples/datadog.tools.json +13 -0
- package/examples/github-actions/fleet.yml +25 -0
- package/examples/github-actions/remote-server.yml +24 -0
- package/examples/github-actions/single-server.yml +21 -0
- package/examples/mcp-probe.config.json +19 -0
- package/examples/recipes/README.md +35 -0
- package/examples/recipes/datadog.tools.json +23 -0
- package/examples/recipes/gmail.tools.json +18 -0
- package/examples/recipes/supabase.tools.json +19 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# mcp-probe
|
|
2
2
|
|
|
3
3
|
[](https://github.com/k08200/mcp-probe/actions/workflows/ci.yml)
|
|
4
|
-
[](https://www.npmjs.com/package/mcp-probe)
|
|
4
|
+
[](https://www.npmjs.com/package/@k08200/mcp-probe)
|
|
5
5
|
[](LICENSE)
|
|
6
|
-
[](package.json)
|
|
6
|
+
[](package.json)
|
|
7
7
|
|
|
8
|
-
**Quality checker for MCP servers.** Validates protocol handshake, tool
|
|
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
|
|
|
@@ -64,11 +64,35 @@ mcp-probe @modelcontextprotocol/server-filesystem /tmp /Users/me/projects
|
|
|
64
64
|
# Check a local server file
|
|
65
65
|
mcp-probe ./my-server.js
|
|
66
66
|
|
|
67
|
+
# Check a remote Streamable HTTP MCP server
|
|
68
|
+
mcp-probe https://mcp.example.com/mcp
|
|
69
|
+
|
|
70
|
+
# Check a legacy HTTP+SSE MCP server
|
|
71
|
+
mcp-probe https://mcp.example.com/sse --transport sse
|
|
72
|
+
|
|
73
|
+
# Pass headers to remote servers
|
|
74
|
+
mcp-probe https://mcp.example.com/mcp --header "Authorization: Bearer $TOKEN"
|
|
75
|
+
|
|
67
76
|
# JSON output for CI / scripting
|
|
68
77
|
mcp-probe @scope/server --output json
|
|
69
78
|
|
|
70
79
|
# Custom timeout (default: 10000ms)
|
|
71
80
|
mcp-probe @scope/server --timeout 30000
|
|
81
|
+
|
|
82
|
+
# Batch-check several servers from a config file
|
|
83
|
+
mcp-probe --config mcp-probe.config.json
|
|
84
|
+
|
|
85
|
+
# Write GitHub Actions summary and annotations
|
|
86
|
+
mcp-probe --config mcp-probe.config.json --github-summary
|
|
87
|
+
|
|
88
|
+
# Write shields.io endpoint JSON for a status badge
|
|
89
|
+
mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
|
|
90
|
+
|
|
91
|
+
# Call tools with generated minimal inputs
|
|
92
|
+
mcp-probe @scope/server --probe-tools
|
|
93
|
+
|
|
94
|
+
# Call tools with real sample inputs from a sidecar file
|
|
95
|
+
mcp-probe @scope/server --tools-file .mcp-probe.json
|
|
72
96
|
```
|
|
73
97
|
|
|
74
98
|
## What it checks
|
|
@@ -79,6 +103,117 @@ mcp-probe @scope/server --timeout 30000
|
|
|
79
103
|
| **MCP protocol handshake** | Does the server respond to `initialize`? Measures connect latency. |
|
|
80
104
|
| **Tools discovery** | Does `tools/list` return results? Measures list latency. |
|
|
81
105
|
| **Tool schema validation** | Are all tool schemas well-formed? |
|
|
106
|
+
| **Resources discovery** | Runs `resources/list` when the server advertises resources. |
|
|
107
|
+
| **Prompts discovery** | Runs `prompts/list` when the server advertises prompts. |
|
|
108
|
+
| **Tool call dry-run** | Optional `tools/call` checks via `--probe-tools` or `--tools-file`. |
|
|
109
|
+
|
|
110
|
+
## Batch CI gate
|
|
111
|
+
|
|
112
|
+
Use `--config` when a project depends on several MCP servers and you want one CI command to validate all of them:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"timeoutMs": 10000,
|
|
117
|
+
"servers": [
|
|
118
|
+
{
|
|
119
|
+
"name": "memory",
|
|
120
|
+
"target": "@modelcontextprotocol/server-memory",
|
|
121
|
+
"probeTools": true
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "datadog",
|
|
125
|
+
"target": "https://mcp.example.com/mcp",
|
|
126
|
+
"transport": "http",
|
|
127
|
+
"headers": {
|
|
128
|
+
"Authorization": "Bearer ${DATADOG_MCP_TOKEN}"
|
|
129
|
+
},
|
|
130
|
+
"toolsFile": "./recipes/datadog.tools.json"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Run:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
mcp-probe --config mcp-probe.config.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
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.
|
|
143
|
+
|
|
144
|
+
Config fields:
|
|
145
|
+
|
|
146
|
+
| Field | Description |
|
|
147
|
+
|-------|-------------|
|
|
148
|
+
| `timeoutMs` | Optional global timeout in milliseconds. CLI `--timeout` is used when omitted. |
|
|
149
|
+
| `servers[].name` | Human-readable name shown in batch output. |
|
|
150
|
+
| `servers[].target` | npm package, local server path, or remote MCP URL. |
|
|
151
|
+
| `servers[].serverArgs` | Optional arguments passed to the MCP server. |
|
|
152
|
+
| `servers[].transport` | Optional transport override: `stdio`, `http`, or `sse`. URL targets default to `http`; package/path targets default to `stdio`. |
|
|
153
|
+
| `servers[].headers` | Optional HTTP headers for remote MCP servers. `${ENV_VAR}` placeholders are expanded at runtime. |
|
|
154
|
+
| `servers[].probeTools` | Enables dry-run tool calls for that server. |
|
|
155
|
+
| `servers[].toolsFile` | Sidecar input file for meaningful `tools/call` samples. Relative paths resolve from the config file directory. |
|
|
156
|
+
|
|
157
|
+
## Tool call dry-runs
|
|
158
|
+
|
|
159
|
+
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.
|
|
160
|
+
|
|
161
|
+
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:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"tools": {
|
|
166
|
+
"logs_query": {
|
|
167
|
+
"input": {
|
|
168
|
+
"query": "service:web status:error",
|
|
169
|
+
"timeframe": "1h"
|
|
170
|
+
},
|
|
171
|
+
"expect": {
|
|
172
|
+
"not_error_code": [401, 403]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Save this as `.mcp-probe.json` in your project root and run:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
mcp-probe @your-org/datadog-mcp --probe-tools
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Or pass an explicit path:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
mcp-probe @your-org/datadog-mcp --tools-file ./ci/mcp-tools.json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
## Status badges
|
|
194
|
+
|
|
195
|
+
Use `--badge-file` to write a [shields.io endpoint](https://shields.io/badges/endpoint-badge) JSON file:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
mcp-probe --config mcp-probe.config.json --badge-file mcp-probe-badge.json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Example output:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"schemaVersion": 1,
|
|
206
|
+
"label": "mcp fleet",
|
|
207
|
+
"message": "2 pass, 1 warn",
|
|
208
|
+
"color": "yellow"
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Host that JSON file anywhere public and reference it from your README:
|
|
213
|
+
|
|
214
|
+
```markdown
|
|
215
|
+

|
|
216
|
+
```
|
|
82
217
|
|
|
83
218
|
## Exit codes
|
|
84
219
|
|
|
@@ -89,17 +224,88 @@ mcp-probe @scope/server --timeout 30000
|
|
|
89
224
|
|
|
90
225
|
## CI integration
|
|
91
226
|
|
|
227
|
+
Single server workflow:
|
|
228
|
+
|
|
92
229
|
```yaml
|
|
93
230
|
# .github/workflows/mcp-probe.yml
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
231
|
+
name: MCP Probe
|
|
232
|
+
|
|
233
|
+
on:
|
|
234
|
+
pull_request:
|
|
235
|
+
push:
|
|
236
|
+
branches: [main]
|
|
237
|
+
|
|
238
|
+
jobs:
|
|
239
|
+
mcp-probe:
|
|
240
|
+
runs-on: ubuntu-latest
|
|
241
|
+
timeout-minutes: 5
|
|
242
|
+
|
|
243
|
+
steps:
|
|
244
|
+
- uses: actions/checkout@v4
|
|
245
|
+
|
|
246
|
+
- name: Validate MCP server
|
|
247
|
+
run: |
|
|
248
|
+
npx @k08200/mcp-probe @your-org/your-mcp-server \
|
|
249
|
+
--probe-tools \
|
|
250
|
+
--github-summary \
|
|
251
|
+
--badge-file mcp-probe-badge.json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Fleet workflow:
|
|
255
|
+
|
|
256
|
+
```yaml
|
|
257
|
+
# .github/workflows/mcp-fleet.yml
|
|
258
|
+
name: MCP Fleet Probe
|
|
259
|
+
|
|
260
|
+
on:
|
|
261
|
+
pull_request:
|
|
262
|
+
push:
|
|
263
|
+
branches: [main]
|
|
264
|
+
schedule:
|
|
265
|
+
- cron: "0 * * * *"
|
|
266
|
+
|
|
267
|
+
jobs:
|
|
268
|
+
mcp-probe:
|
|
269
|
+
runs-on: ubuntu-latest
|
|
270
|
+
timeout-minutes: 10
|
|
271
|
+
|
|
272
|
+
steps:
|
|
273
|
+
- uses: actions/checkout@v4
|
|
274
|
+
|
|
275
|
+
- name: Validate MCP fleet
|
|
276
|
+
run: |
|
|
277
|
+
npx @k08200/mcp-probe \
|
|
278
|
+
--config mcp-probe.config.json \
|
|
279
|
+
--github-summary \
|
|
280
|
+
--badge-file mcp-probe-badge.json
|
|
97
281
|
```
|
|
98
282
|
|
|
283
|
+
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.
|
|
284
|
+
|
|
285
|
+
Copy-ready examples live in [`examples/github-actions`](examples/github-actions):
|
|
286
|
+
|
|
287
|
+
| Example | Use case |
|
|
288
|
+
|---------|----------|
|
|
289
|
+
| [`single-server.yml`](examples/github-actions/single-server.yml) | Validate one stdio MCP package. |
|
|
290
|
+
| [`fleet.yml`](examples/github-actions/fleet.yml) | Validate several MCP servers from `mcp-probe.config.json` on PRs and hourly schedules. |
|
|
291
|
+
| [`remote-server.yml`](examples/github-actions/remote-server.yml) | Validate a remote Streamable HTTP MCP server with auth headers. |
|
|
292
|
+
|
|
293
|
+
## Recipes
|
|
294
|
+
|
|
295
|
+
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):
|
|
296
|
+
|
|
297
|
+
| Recipe | Focus |
|
|
298
|
+
|--------|-------|
|
|
299
|
+
| [`datadog.tools.json`](examples/recipes/datadog.tools.json) | Logs/metrics queries that reveal auth handoff and downstream API failures. |
|
|
300
|
+
| [`supabase.tools.json`](examples/recipes/supabase.tools.json) | Project visibility and a harmless `select 1` SQL path. |
|
|
301
|
+
| [`gmail.tools.json`](examples/recipes/gmail.tools.json) | OAuth/token handoff and read-only mailbox access. |
|
|
302
|
+
|
|
303
|
+
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.
|
|
304
|
+
|
|
99
305
|
## JSON output
|
|
100
306
|
|
|
101
307
|
```bash
|
|
102
|
-
mcp-probe @modelcontextprotocol/server-memory --output json
|
|
308
|
+
mcp-probe @modelcontextprotocol/server-memory --probe-tools --output json
|
|
103
309
|
```
|
|
104
310
|
|
|
105
311
|
```json
|
|
@@ -111,10 +317,14 @@ mcp-probe @modelcontextprotocol/server-memory --output json
|
|
|
111
317
|
{ "name": "Target resolution", "status": "pass", "message": "npx --yes @modelcontextprotocol/server-memory" },
|
|
112
318
|
{ "name": "MCP protocol handshake", "status": "pass", "message": "memory-server v0.6.3", "latencyMs": 1392 },
|
|
113
319
|
{ "name": "Tools discovery", "status": "pass", "message": "Found 9 tools", "latencyMs": 33 },
|
|
114
|
-
{ "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" }
|
|
320
|
+
{ "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" },
|
|
321
|
+
{ "name": "Tool call dry-run", "status": "pass", "message": "9 passed (2 sidecar, 7 auto)" }
|
|
115
322
|
],
|
|
116
323
|
"serverInfo": { "name": "memory-server", "version": "0.6.3", "capabilities": ["tools"] },
|
|
117
324
|
"tools": [{ "name": "create_entities", "description": "Create multiple new entities in the knowledge graph" }],
|
|
325
|
+
"toolCallResults": [
|
|
326
|
+
{ "tool": "read_graph", "status": "pass", "latencyMs": 41, "source": "auto" }
|
|
327
|
+
],
|
|
118
328
|
"totalLatencyMs": 1455
|
|
119
329
|
}
|
|
120
330
|
```
|
|
@@ -129,11 +339,12 @@ mcp-probe @modelcontextprotocol/server-memory --output json
|
|
|
129
339
|
|
|
130
340
|
## Roadmap
|
|
131
341
|
|
|
132
|
-
- [
|
|
133
|
-
- [
|
|
134
|
-
- [
|
|
135
|
-
- [
|
|
136
|
-
- [ ]
|
|
342
|
+
- [x] HTTP/SSE transport support
|
|
343
|
+
- [x] Batch checking from a config file (`mcp-probe --config mcp-probe.config.json`)
|
|
344
|
+
- [x] GitHub Actions summary and annotations
|
|
345
|
+
- [x] Badge generation (`mcp-probe --badge-file mcp-probe-badge.json`)
|
|
346
|
+
- [ ] Structured stderr conventions for MCP server authors
|
|
347
|
+
- [x] Server-specific recipe examples for Datadog, Supabase, and Gmail MCP servers
|
|
137
348
|
|
|
138
349
|
## Contributing
|
|
139
350
|
|
package/dist/checker.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type { CheckReport } from './types.js';
|
|
2
|
-
|
|
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
|
package/dist/checker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"
|
|
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,CA6HhF"}
|
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
|
-
|
|
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,29 @@ 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
|
|
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:
|
|
75
|
+
message: resolutionMessage,
|
|
25
76
|
});
|
|
26
77
|
try {
|
|
27
|
-
const
|
|
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
|
+
timeoutMs: options.timeoutMs,
|
|
86
|
+
probeTools,
|
|
87
|
+
sidecar,
|
|
88
|
+
});
|
|
28
89
|
checks.push({
|
|
29
90
|
name: 'MCP protocol handshake',
|
|
30
91
|
status: 'pass',
|
|
@@ -66,6 +127,26 @@ export async function checkMcpServer(options) {
|
|
|
66
127
|
latencyMs: probe.promptsLatencyMs,
|
|
67
128
|
});
|
|
68
129
|
}
|
|
130
|
+
if (probe.toolCallResults && probe.toolCallResults.length > 0) {
|
|
131
|
+
const failed = probe.toolCallResults.filter((r) => r.status === 'fail');
|
|
132
|
+
const warned = probe.toolCallResults.filter((r) => r.status === 'warn');
|
|
133
|
+
const passed = probe.toolCallResults.filter((r) => r.status === 'pass');
|
|
134
|
+
const sidecarCount = probe.toolCallResults.filter((r) => r.source === 'sidecar').length;
|
|
135
|
+
const status = failed.length > 0 ? 'fail' : warned.length > 0 ? 'warn' : 'pass';
|
|
136
|
+
const parts = [];
|
|
137
|
+
if (passed.length > 0)
|
|
138
|
+
parts.push(`${passed.length} passed`);
|
|
139
|
+
if (warned.length > 0)
|
|
140
|
+
parts.push(`${warned.length} auth/permission errors`);
|
|
141
|
+
if (failed.length > 0)
|
|
142
|
+
parts.push(`${failed.length} failed`);
|
|
143
|
+
const sourceNote = sidecarCount > 0 ? ` (${sidecarCount} sidecar, ${probe.toolCallResults.length - sidecarCount} auto)` : '';
|
|
144
|
+
checks.push({
|
|
145
|
+
name: 'Tool call dry-run',
|
|
146
|
+
status,
|
|
147
|
+
message: parts.join(', ') + sourceNote,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
69
150
|
return {
|
|
70
151
|
target: options.target,
|
|
71
152
|
timestamp: new Date().toISOString(),
|
|
@@ -75,13 +156,15 @@ export async function checkMcpServer(options) {
|
|
|
75
156
|
tools: probe.tools,
|
|
76
157
|
resources: probe.resources,
|
|
77
158
|
prompts: probe.prompts,
|
|
159
|
+
toolCallResults: probe.toolCallResults,
|
|
78
160
|
totalLatencyMs: Date.now() - startTime,
|
|
79
161
|
};
|
|
80
162
|
}
|
|
81
163
|
catch (error) {
|
|
82
164
|
const message = error instanceof Error ? error.message : String(error);
|
|
165
|
+
const isSidecarError = message.includes('tools file');
|
|
83
166
|
checks.push({
|
|
84
|
-
name: 'MCP protocol handshake',
|
|
167
|
+
name: isSidecarError ? 'Tool sidecar' : 'MCP protocol handshake',
|
|
85
168
|
status: 'fail',
|
|
86
169
|
message,
|
|
87
170
|
});
|
package/dist/checker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -2,29 +2,139 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { checkMcpServer } from './checker.js';
|
|
5
|
-
import {
|
|
5
|
+
import { checkConfigFile } from './config.js';
|
|
6
|
+
import { renderBatchTerminal, renderTerminal } from './reporters/terminal.js';
|
|
6
7
|
import { renderJson } from './reporters/json-reporter.js';
|
|
8
|
+
import { renderGithubActions } from './reporters/github.js';
|
|
9
|
+
import { writeBadgeFile } from './reporters/badge.js';
|
|
7
10
|
const program = new Command();
|
|
11
|
+
function collect(value, previous) {
|
|
12
|
+
previous.push(value);
|
|
13
|
+
return previous;
|
|
14
|
+
}
|
|
15
|
+
function parseHeaders(values) {
|
|
16
|
+
if (values.length === 0)
|
|
17
|
+
return undefined;
|
|
18
|
+
const headers = {};
|
|
19
|
+
for (const value of values) {
|
|
20
|
+
const colon = value.indexOf(':');
|
|
21
|
+
if (colon <= 0) {
|
|
22
|
+
throw new Error(`Invalid header: ${value}. Use "Name: value".`);
|
|
23
|
+
}
|
|
24
|
+
const name = value.slice(0, colon).trim();
|
|
25
|
+
const headerValue = value.slice(colon + 1).trim();
|
|
26
|
+
if (!name || !headerValue) {
|
|
27
|
+
throw new Error(`Invalid header: ${value}. Use "Name: value".`);
|
|
28
|
+
}
|
|
29
|
+
headers[name] = headerValue;
|
|
30
|
+
}
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
function parseTransport(value) {
|
|
34
|
+
if (!value)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (value === 'stdio' || value === 'http' || value === 'sse')
|
|
37
|
+
return value;
|
|
38
|
+
throw new Error('Transport must be "stdio", "http", or "sse".');
|
|
39
|
+
}
|
|
8
40
|
program
|
|
9
41
|
.name('mcp-probe')
|
|
10
42
|
.description('Quality checker for MCP servers')
|
|
11
|
-
.version('0.
|
|
12
|
-
.argument('
|
|
43
|
+
.version('0.8.0')
|
|
44
|
+
.argument('[target]', 'npm package, local file path, or remote MCP URL')
|
|
13
45
|
.argument('[server-args...]', 'extra arguments passed directly to the MCP server')
|
|
14
46
|
.option('-o, --output <format>', 'output format: terminal | json', 'terminal')
|
|
15
47
|
.option('-t, --timeout <ms>', 'connection timeout in ms', '10000')
|
|
48
|
+
.option('-c, --config <path>', 'batch config JSON file')
|
|
49
|
+
.option('--transport <mode>', 'transport mode: stdio | http | sse')
|
|
50
|
+
.option('-H, --header <header>', 'HTTP header for remote MCP servers, e.g. "Authorization: Bearer TOKEN"', collect, [])
|
|
51
|
+
.option('--github-summary', 'write GitHub Actions job summary and annotations')
|
|
52
|
+
.option('--badge-file <path>', 'write shields.io endpoint JSON for README/status badges')
|
|
53
|
+
.option('--probe-tools', 'call each tool to validate the full call path (auto-discovers .mcp-probe.json)')
|
|
54
|
+
.option('--tools-file <path>', 'path to sidecar JSON with declared tool inputs (implies --probe-tools)')
|
|
16
55
|
.action(async (target, serverArgs, opts) => {
|
|
17
56
|
const timeoutMs = parseInt(opts.timeout, 10);
|
|
57
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
58
|
+
console.error('Timeout must be a positive integer.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (!['terminal', 'json'].includes(opts.output)) {
|
|
62
|
+
console.error('Output format must be "terminal" or "json".');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
let transport;
|
|
66
|
+
let headers;
|
|
67
|
+
try {
|
|
68
|
+
transport = parseTransport(opts.transport);
|
|
69
|
+
headers = parseHeaders(opts.header);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
if (opts.config) {
|
|
76
|
+
if (target) {
|
|
77
|
+
console.error('Use either --config or a target, not both.');
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
if (opts.output === 'json') {
|
|
81
|
+
try {
|
|
82
|
+
const report = await checkConfigFile(opts.config, timeoutMs);
|
|
83
|
+
if (opts.githubSummary)
|
|
84
|
+
renderGithubActions(report);
|
|
85
|
+
if (opts.badgeFile)
|
|
86
|
+
writeBadgeFile(report, opts.badgeFile);
|
|
87
|
+
renderJson(report);
|
|
88
|
+
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const spinner = ora(`Checking config ${opts.config}`).start();
|
|
97
|
+
try {
|
|
98
|
+
const report = await checkConfigFile(opts.config, timeoutMs);
|
|
99
|
+
spinner.stop();
|
|
100
|
+
if (opts.githubSummary)
|
|
101
|
+
renderGithubActions(report);
|
|
102
|
+
if (opts.badgeFile)
|
|
103
|
+
writeBadgeFile(report, opts.badgeFile);
|
|
104
|
+
renderBatchTerminal(report);
|
|
105
|
+
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
spinner.fail('Unexpected error');
|
|
109
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!target) {
|
|
115
|
+
console.error('Missing target. Pass a target or --config <path>.');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const probeTools = opts.probeTools || !!opts.toolsFile;
|
|
119
|
+
const toolsFile = opts.toolsFile;
|
|
18
120
|
if (opts.output === 'json') {
|
|
19
|
-
const report = await checkMcpServer({ target, serverArgs, timeoutMs });
|
|
121
|
+
const report = await checkMcpServer({ target, serverArgs, timeoutMs, transport, headers, probeTools, toolsFile });
|
|
122
|
+
if (opts.githubSummary)
|
|
123
|
+
renderGithubActions(report);
|
|
124
|
+
if (opts.badgeFile)
|
|
125
|
+
writeBadgeFile(report, opts.badgeFile);
|
|
20
126
|
renderJson(report);
|
|
21
127
|
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
22
128
|
return;
|
|
23
129
|
}
|
|
24
130
|
const spinner = ora(`Checking ${target}`).start();
|
|
25
131
|
try {
|
|
26
|
-
const report = await checkMcpServer({ target, serverArgs, timeoutMs });
|
|
132
|
+
const report = await checkMcpServer({ target, serverArgs, timeoutMs, transport, headers, probeTools, toolsFile });
|
|
27
133
|
spinner.stop();
|
|
134
|
+
if (opts.githubSummary)
|
|
135
|
+
renderGithubActions(report);
|
|
136
|
+
if (opts.badgeFile)
|
|
137
|
+
writeBadgeFile(report, opts.badgeFile);
|
|
28
138
|
renderTerminal(report);
|
|
29
139
|
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
30
140
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BatchReport, ProbeConfig } from './types.js';
|
|
2
|
+
export declare function loadConfig(configFile: string): ProbeConfig;
|
|
3
|
+
export declare function checkConfigFile(configFile: string, defaultTimeoutMs?: number): Promise<BatchReport>;
|
|
4
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAA2C,WAAW,EAAE,MAAM,YAAY,CAAC;AA8CpG,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CA0B1D;AA8BD,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,SAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CA6BxG"}
|