@k08200/mcp-probe 0.1.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/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/checker.d.ts +13 -0
- package/dist/checker.d.ts.map +1 -0
- package/dist/checker.js +100 -0
- package/dist/checker.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -0
- package/dist/protocols/mcp-client.d.ts +3 -0
- package/dist/protocols/mcp-client.d.ts.map +1 -0
- package/dist/protocols/mcp-client.js +87 -0
- package/dist/protocols/mcp-client.js.map +1 -0
- package/dist/reporters/json-reporter.d.ts +3 -0
- package/dist/reporters/json-reporter.d.ts.map +1 -0
- package/dist/reporters/json-reporter.js +4 -0
- package/dist/reporters/json-reporter.js.map +1 -0
- package/dist/reporters/terminal.d.ts +3 -0
- package/dist/reporters/terminal.d.ts.map +1 -0
- package/dist/reporters/terminal.js +64 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 k08200
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# mcp-probe
|
|
2
|
+
|
|
3
|
+
[](https://github.com/k08200/mcp-probe/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/mcp-probe)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](package.json)
|
|
7
|
+
|
|
8
|
+
**Quality checker for MCP servers.** Validates protocol handshake, tool discovery, and response latency in one command.
|
|
9
|
+
|
|
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
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @k08200/mcp-probe @modelcontextprotocol/server-memory
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
mcp-probe @modelcontextprotocol/server-memory
|
|
18
|
+
────────────────────────────────────────────────────
|
|
19
|
+
✓ Target resolution
|
|
20
|
+
npx --yes @modelcontextprotocol/server-memory
|
|
21
|
+
✓ MCP protocol handshake 1392ms
|
|
22
|
+
memory-server v0.6.3
|
|
23
|
+
✓ Tools discovery 33ms
|
|
24
|
+
Found 9 tools
|
|
25
|
+
✓ Tool schema validation
|
|
26
|
+
All tool schemas are valid
|
|
27
|
+
────────────────────────────────────────────────────
|
|
28
|
+
Server memory-server v0.6.3
|
|
29
|
+
Caps tools
|
|
30
|
+
|
|
31
|
+
Tools
|
|
32
|
+
▸ create_entities Create multiple new entities in the knowledge graph
|
|
33
|
+
▸ create_relations Create multiple new relations between entities
|
|
34
|
+
▸ add_observations Add new observations to existing entities
|
|
35
|
+
▸ delete_entities Delete entities and their associated relations
|
|
36
|
+
▸ read_graph Read the entire knowledge graph
|
|
37
|
+
▸ search_nodes Search for nodes in the knowledge graph
|
|
38
|
+
▸ ...and 3 more
|
|
39
|
+
|
|
40
|
+
✓ PASS 1455ms total
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# No install needed
|
|
49
|
+
npx @k08200/mcp-probe <target>
|
|
50
|
+
|
|
51
|
+
# Or install globally
|
|
52
|
+
npm install -g @k08200/mcp-probe
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Check an npm package
|
|
59
|
+
mcp-probe @modelcontextprotocol/server-memory
|
|
60
|
+
|
|
61
|
+
# Check a server that requires arguments (e.g. directories to serve)
|
|
62
|
+
mcp-probe @modelcontextprotocol/server-filesystem /tmp /Users/me/projects
|
|
63
|
+
|
|
64
|
+
# Check a local server file
|
|
65
|
+
mcp-probe ./my-server.js
|
|
66
|
+
|
|
67
|
+
# JSON output for CI / scripting
|
|
68
|
+
mcp-probe @scope/server --output json
|
|
69
|
+
|
|
70
|
+
# Custom timeout (default: 10000ms)
|
|
71
|
+
mcp-probe @scope/server --timeout 30000
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## What it checks
|
|
75
|
+
|
|
76
|
+
| Check | Description |
|
|
77
|
+
|-------|-------------|
|
|
78
|
+
| **Target resolution** | Can the package be located and spawned? |
|
|
79
|
+
| **MCP protocol handshake** | Does the server respond to `initialize`? Measures connect latency. |
|
|
80
|
+
| **Tools discovery** | Does `tools/list` return results? Measures list latency. |
|
|
81
|
+
| **Tool schema validation** | Are all tool schemas well-formed? |
|
|
82
|
+
|
|
83
|
+
## Exit codes
|
|
84
|
+
|
|
85
|
+
| Code | Meaning |
|
|
86
|
+
|------|---------|
|
|
87
|
+
| `0` | All checks passed (or warnings only) |
|
|
88
|
+
| `1` | One or more checks failed |
|
|
89
|
+
|
|
90
|
+
## CI integration
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
# .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
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## JSON output
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
mcp-probe @modelcontextprotocol/server-memory --output json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"target": "@modelcontextprotocol/server-memory",
|
|
108
|
+
"timestamp": "2026-05-17T12:00:00.000Z",
|
|
109
|
+
"overallStatus": "pass",
|
|
110
|
+
"checks": [
|
|
111
|
+
{ "name": "Target resolution", "status": "pass", "message": "npx --yes @modelcontextprotocol/server-memory" },
|
|
112
|
+
{ "name": "MCP protocol handshake", "status": "pass", "message": "memory-server v0.6.3", "latencyMs": 1392 },
|
|
113
|
+
{ "name": "Tools discovery", "status": "pass", "message": "Found 9 tools", "latencyMs": 33 },
|
|
114
|
+
{ "name": "Tool schema validation", "status": "pass", "message": "All tool schemas are valid" }
|
|
115
|
+
],
|
|
116
|
+
"serverInfo": { "name": "memory-server", "version": "0.6.3", "capabilities": ["tools"] },
|
|
117
|
+
"tools": [{ "name": "create_entities", "description": "Create multiple new entities in the knowledge graph" }],
|
|
118
|
+
"totalLatencyMs": 1455
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Status values
|
|
123
|
+
|
|
124
|
+
| Status | Icon | Meaning |
|
|
125
|
+
|--------|------|---------|
|
|
126
|
+
| `pass` | ✓ | Check succeeded |
|
|
127
|
+
| `warn` | ⚠ | Non-fatal issue (e.g. no tools registered) |
|
|
128
|
+
| `fail` | ✗ | Check failed — exits with code 1 |
|
|
129
|
+
|
|
130
|
+
## Roadmap
|
|
131
|
+
|
|
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
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
Issues and PRs are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
};
|
|
11
|
+
export declare function checkMcpServer(options: CheckOptions): Promise<CheckReport>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=checker.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/checker.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { probeMcpServer } from './protocols/mcp-client.js';
|
|
2
|
+
export function resolveTarget(target) {
|
|
3
|
+
if (target.startsWith('.') || target.startsWith('/')) {
|
|
4
|
+
return { command: 'node', args: [target] };
|
|
5
|
+
}
|
|
6
|
+
return { command: 'npx', args: ['--yes', target] };
|
|
7
|
+
}
|
|
8
|
+
function deriveOverallStatus(checks) {
|
|
9
|
+
if (checks.some((c) => c.status === 'fail'))
|
|
10
|
+
return 'fail';
|
|
11
|
+
if (checks.some((c) => c.status === 'warn'))
|
|
12
|
+
return 'warn';
|
|
13
|
+
return 'pass';
|
|
14
|
+
}
|
|
15
|
+
export async function checkMcpServer(options) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
const checks = [];
|
|
18
|
+
const resolved = resolveTarget(options.target);
|
|
19
|
+
const args = [...resolved.args, ...(options.serverArgs ?? [])];
|
|
20
|
+
const { command } = resolved;
|
|
21
|
+
checks.push({
|
|
22
|
+
name: 'Target resolution',
|
|
23
|
+
status: 'pass',
|
|
24
|
+
message: `${command} ${args.join(' ')}`,
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const probe = await probeMcpServer({ command, args, timeoutMs: options.timeoutMs });
|
|
28
|
+
checks.push({
|
|
29
|
+
name: 'MCP protocol handshake',
|
|
30
|
+
status: 'pass',
|
|
31
|
+
message: `${probe.serverInfo.name} v${probe.serverInfo.version}`,
|
|
32
|
+
latencyMs: probe.connectLatencyMs,
|
|
33
|
+
});
|
|
34
|
+
const toolsStatus = probe.tools.length > 0 ? 'pass' : 'warn';
|
|
35
|
+
checks.push({
|
|
36
|
+
name: 'Tools discovery',
|
|
37
|
+
status: toolsStatus,
|
|
38
|
+
message: probe.tools.length > 0
|
|
39
|
+
? `Found ${probe.tools.length} tool${probe.tools.length !== 1 ? 's' : ''}`
|
|
40
|
+
: 'No tools registered — server may be resources/prompts-only',
|
|
41
|
+
latencyMs: probe.toolsLatencyMs,
|
|
42
|
+
});
|
|
43
|
+
const toolsMissingName = probe.tools.filter((t) => !t.name);
|
|
44
|
+
if (probe.tools.length > 0) {
|
|
45
|
+
checks.push({
|
|
46
|
+
name: 'Tool schema validation',
|
|
47
|
+
status: toolsMissingName.length > 0 ? 'warn' : 'pass',
|
|
48
|
+
message: toolsMissingName.length > 0
|
|
49
|
+
? `${toolsMissingName.length} tool(s) missing required name field`
|
|
50
|
+
: 'All tool schemas are valid',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (probe.resources.length > 0 || probe.resourcesLatencyMs !== undefined) {
|
|
54
|
+
checks.push({
|
|
55
|
+
name: 'Resources discovery',
|
|
56
|
+
status: 'pass',
|
|
57
|
+
message: `Found ${probe.resources.length} resource${probe.resources.length !== 1 ? 's' : ''}`,
|
|
58
|
+
latencyMs: probe.resourcesLatencyMs,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (probe.prompts.length > 0 || probe.promptsLatencyMs !== undefined) {
|
|
62
|
+
checks.push({
|
|
63
|
+
name: 'Prompts discovery',
|
|
64
|
+
status: 'pass',
|
|
65
|
+
message: `Found ${probe.prompts.length} prompt${probe.prompts.length !== 1 ? 's' : ''}`,
|
|
66
|
+
latencyMs: probe.promptsLatencyMs,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
target: options.target,
|
|
71
|
+
timestamp: new Date().toISOString(),
|
|
72
|
+
overallStatus: deriveOverallStatus(checks),
|
|
73
|
+
checks,
|
|
74
|
+
serverInfo: probe.serverInfo,
|
|
75
|
+
tools: probe.tools,
|
|
76
|
+
resources: probe.resources,
|
|
77
|
+
prompts: probe.prompts,
|
|
78
|
+
totalLatencyMs: Date.now() - startTime,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
checks.push({
|
|
84
|
+
name: 'MCP protocol handshake',
|
|
85
|
+
status: 'fail',
|
|
86
|
+
message,
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
target: options.target,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
overallStatus: 'fail',
|
|
92
|
+
checks,
|
|
93
|
+
tools: [],
|
|
94
|
+
resources: [],
|
|
95
|
+
prompts: [],
|
|
96
|
+
totalLatencyMs: Date.now() - startTime,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { checkMcpServer } from './checker.js';
|
|
5
|
+
import { renderTerminal } from './reporters/terminal.js';
|
|
6
|
+
import { renderJson } from './reporters/json-reporter.js';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('mcp-probe')
|
|
10
|
+
.description('Quality checker for MCP servers')
|
|
11
|
+
.version('0.1.0')
|
|
12
|
+
.argument('<target>', 'npm package, npx-style command, or local file path')
|
|
13
|
+
.argument('[server-args...]', 'extra arguments passed directly to the MCP server')
|
|
14
|
+
.option('-o, --output <format>', 'output format: terminal | json', 'terminal')
|
|
15
|
+
.option('-t, --timeout <ms>', 'connection timeout in ms', '10000')
|
|
16
|
+
.action(async (target, serverArgs, opts) => {
|
|
17
|
+
const timeoutMs = parseInt(opts.timeout, 10);
|
|
18
|
+
if (opts.output === 'json') {
|
|
19
|
+
const report = await checkMcpServer({ target, serverArgs, timeoutMs });
|
|
20
|
+
renderJson(report);
|
|
21
|
+
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const spinner = ora(`Checking ${target}`).start();
|
|
25
|
+
try {
|
|
26
|
+
const report = await checkMcpServer({ target, serverArgs, timeoutMs });
|
|
27
|
+
spinner.stop();
|
|
28
|
+
renderTerminal(report);
|
|
29
|
+
process.exit(report.overallStatus === 'fail' ? 1 : 0);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
spinner.fail('Unexpected error');
|
|
33
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
program.parse();
|
|
38
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +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;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,iCAAiC,CAAC;KAC9C,OAAO,CAAC,OAAO,CAAC;KAChB,QAAQ,CAAC,UAAU,EAAE,oDAAoD,CAAC;KAC1E,QAAQ,CAAC,kBAAkB,EAAE,mDAAmD,CAAC;KACjF,MAAM,CAAC,uBAAuB,EAAE,gCAAgC,EAAE,UAAU,CAAC;KAC7E,MAAM,CAAC,oBAAoB,EAAE,0BAA0B,EAAE,OAAO,CAAC;KACjE,MAAM,CAAC,KAAK,EACX,MAAc,EACd,UAAoB,EACpB,IAAyC,EACzC,EAAE;IACF,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAE7C,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACvE,UAAU,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../../src/protocols/mcp-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoB7D,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAiFhF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
function firstMeaningfulLine(text) {
|
|
4
|
+
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
5
|
+
// 1. "Error: ..." lines are most informative
|
|
6
|
+
const errorColonLine = lines.find((l) => /^Error:/.test(l));
|
|
7
|
+
if (errorColonLine)
|
|
8
|
+
return errorColonLine;
|
|
9
|
+
// 2. Skip stack frames, node internals, carets, and bare code fragments
|
|
10
|
+
const skip = /^at |^node:|^\^$|^const |^throw |^Require stack/;
|
|
11
|
+
const meaningful = lines.find((l) => !skip.test(l) && l.length > 3);
|
|
12
|
+
return meaningful ?? lines[0] ?? text;
|
|
13
|
+
}
|
|
14
|
+
function withTimeout(promise, ms, label) {
|
|
15
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms));
|
|
16
|
+
return Promise.race([promise, timeout]);
|
|
17
|
+
}
|
|
18
|
+
export async function probeMcpServer(options) {
|
|
19
|
+
const transport = new StdioClientTransport({
|
|
20
|
+
command: options.command,
|
|
21
|
+
args: options.args,
|
|
22
|
+
env: { ...process.env },
|
|
23
|
+
stderr: 'pipe',
|
|
24
|
+
});
|
|
25
|
+
// Collect stderr so we can surface crash reasons in error messages
|
|
26
|
+
const stderrChunks = [];
|
|
27
|
+
transport.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
28
|
+
const client = new Client({ name: 'mcp-probe', version: '0.1.0' }, { capabilities: { roots: { listChanged: false } } });
|
|
29
|
+
const connectStart = Date.now();
|
|
30
|
+
try {
|
|
31
|
+
await withTimeout(client.connect(transport), options.timeoutMs, 'Connection');
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const stderrText = Buffer.concat(stderrChunks).toString('utf8').trim();
|
|
35
|
+
const reason = stderrText
|
|
36
|
+
? firstMeaningfulLine(stderrText)
|
|
37
|
+
: err instanceof Error ? err.message : String(err);
|
|
38
|
+
throw new Error(reason);
|
|
39
|
+
}
|
|
40
|
+
const connectLatencyMs = Date.now() - connectStart;
|
|
41
|
+
const rawServerInfo = client.getServerVersion();
|
|
42
|
+
const rawCaps = client.getServerCapabilities();
|
|
43
|
+
const capabilities = Object.keys(rawCaps ?? {});
|
|
44
|
+
const toolsStart = Date.now();
|
|
45
|
+
const toolsResult = await withTimeout(client.listTools(), options.timeoutMs, 'tools/list');
|
|
46
|
+
const toolsLatencyMs = Date.now() - toolsStart;
|
|
47
|
+
let resourcesLatencyMs;
|
|
48
|
+
let promptsLatencyMs;
|
|
49
|
+
const resourcesList = [];
|
|
50
|
+
const promptsList = [];
|
|
51
|
+
if (rawCaps?.resources) {
|
|
52
|
+
const t = Date.now();
|
|
53
|
+
const result = await withTimeout(client.listResources(), options.timeoutMs, 'resources/list');
|
|
54
|
+
resourcesLatencyMs = Date.now() - t;
|
|
55
|
+
for (const r of result.resources) {
|
|
56
|
+
resourcesList.push({ uri: r.uri, name: r.name, description: r.description });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (rawCaps?.prompts) {
|
|
60
|
+
const t = Date.now();
|
|
61
|
+
const result = await withTimeout(client.listPrompts(), options.timeoutMs, 'prompts/list');
|
|
62
|
+
promptsLatencyMs = Date.now() - t;
|
|
63
|
+
for (const p of result.prompts) {
|
|
64
|
+
promptsList.push({ name: p.name, description: p.description });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
await client.close();
|
|
68
|
+
return {
|
|
69
|
+
serverInfo: {
|
|
70
|
+
name: rawServerInfo?.name ?? 'unknown',
|
|
71
|
+
version: rawServerInfo?.version ?? 'unknown',
|
|
72
|
+
capabilities,
|
|
73
|
+
},
|
|
74
|
+
tools: toolsResult.tools.map((t) => ({
|
|
75
|
+
name: t.name,
|
|
76
|
+
description: t.description,
|
|
77
|
+
inputSchema: t.inputSchema,
|
|
78
|
+
})),
|
|
79
|
+
resources: resourcesList,
|
|
80
|
+
prompts: promptsList,
|
|
81
|
+
connectLatencyMs,
|
|
82
|
+
toolsLatencyMs,
|
|
83
|
+
resourcesLatencyMs,
|
|
84
|
+
promptsLatencyMs,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=mcp-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../../src/protocols/mcp-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/json-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAEpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-reporter.js","sourceRoot":"","sources":["../../src/reporters/json-reporter.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAgB5D,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAyDxD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const ICONS = {
|
|
3
|
+
pass: chalk.green('✓'),
|
|
4
|
+
fail: chalk.red('✗'),
|
|
5
|
+
warn: chalk.yellow('⚠'),
|
|
6
|
+
};
|
|
7
|
+
const COLORS = {
|
|
8
|
+
pass: chalk.green,
|
|
9
|
+
fail: chalk.red,
|
|
10
|
+
warn: chalk.yellow,
|
|
11
|
+
};
|
|
12
|
+
const DIVIDER = chalk.dim('─'.repeat(52));
|
|
13
|
+
export function renderTerminal(report) {
|
|
14
|
+
console.log('');
|
|
15
|
+
console.log(chalk.bold.white('mcp-probe') + ' ' + chalk.dim(report.target));
|
|
16
|
+
console.log(DIVIDER);
|
|
17
|
+
for (const check of report.checks) {
|
|
18
|
+
const latency = check.latencyMs !== undefined
|
|
19
|
+
? chalk.dim(` ${check.latencyMs}ms`)
|
|
20
|
+
: '';
|
|
21
|
+
console.log(` ${ICONS[check.status]} ${chalk.bold(check.name)}${latency}`);
|
|
22
|
+
console.log(` ${chalk.dim(check.message)}`);
|
|
23
|
+
}
|
|
24
|
+
console.log(DIVIDER);
|
|
25
|
+
if (report.serverInfo) {
|
|
26
|
+
const { name, version, capabilities } = report.serverInfo;
|
|
27
|
+
console.log(chalk.dim(` Server ${name} v${version}`));
|
|
28
|
+
if (capabilities.length > 0) {
|
|
29
|
+
console.log(chalk.dim(` Caps ${capabilities.join(', ')}`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (report.tools.length > 0) {
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log(chalk.bold(' Tools'));
|
|
35
|
+
for (const tool of report.tools) {
|
|
36
|
+
const desc = tool.description ? chalk.dim(` ${tool.description}`) : '';
|
|
37
|
+
console.log(` ${chalk.cyan('▸')} ${chalk.bold(tool.name)}${desc}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (report.resources.length > 0) {
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(chalk.bold(' Resources'));
|
|
43
|
+
for (const res of report.resources) {
|
|
44
|
+
const label = res.name ?? res.uri;
|
|
45
|
+
const desc = res.description ? chalk.dim(` ${res.description}`) : '';
|
|
46
|
+
console.log(` ${chalk.magenta('▸')} ${chalk.bold(label)}${desc}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (report.prompts.length > 0) {
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log(chalk.bold(' Prompts'));
|
|
52
|
+
for (const prompt of report.prompts) {
|
|
53
|
+
const desc = prompt.description ? chalk.dim(` ${prompt.description}`) : '';
|
|
54
|
+
console.log(` ${chalk.yellow('▸')} ${chalk.bold(prompt.name)}${desc}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
console.log('');
|
|
58
|
+
const status = report.overallStatus;
|
|
59
|
+
const label = COLORS[status](chalk.bold(status.toUpperCase()));
|
|
60
|
+
const total = chalk.dim(` ${report.totalLatencyMs}ms total`);
|
|
61
|
+
console.log(` ${ICONS[status]} ${label}${total}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/reporters/terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type CheckStatus = 'pass' | 'fail' | 'warn';
|
|
2
|
+
export type CheckItem = {
|
|
3
|
+
name: string;
|
|
4
|
+
status: CheckStatus;
|
|
5
|
+
message: string;
|
|
6
|
+
latencyMs?: number;
|
|
7
|
+
};
|
|
8
|
+
export type ToolInfo = {
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
inputSchema?: unknown;
|
|
12
|
+
};
|
|
13
|
+
export type ServerInfo = {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
capabilities: string[];
|
|
17
|
+
};
|
|
18
|
+
export type CheckReport = {
|
|
19
|
+
target: string;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
overallStatus: CheckStatus;
|
|
22
|
+
checks: CheckItem[];
|
|
23
|
+
serverInfo?: ServerInfo;
|
|
24
|
+
tools: ToolInfo[];
|
|
25
|
+
resources: ResourceInfo[];
|
|
26
|
+
prompts: PromptInfo[];
|
|
27
|
+
totalLatencyMs: number;
|
|
28
|
+
};
|
|
29
|
+
export type ResourceInfo = {
|
|
30
|
+
uri: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
};
|
|
34
|
+
export type PromptInfo = {
|
|
35
|
+
name: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
};
|
|
38
|
+
export type ProbeResult = {
|
|
39
|
+
serverInfo: ServerInfo;
|
|
40
|
+
tools: ToolInfo[];
|
|
41
|
+
resources: ResourceInfo[];
|
|
42
|
+
prompts: PromptInfo[];
|
|
43
|
+
connectLatencyMs: number;
|
|
44
|
+
toolsLatencyMs: number;
|
|
45
|
+
resourcesLatencyMs?: number;
|
|
46
|
+
promptsLatencyMs?: number;
|
|
47
|
+
};
|
|
48
|
+
export type ProbeOptions = {
|
|
49
|
+
command: string;
|
|
50
|
+
args: string[];
|
|
51
|
+
timeoutMs: number;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,WAAW,CAAC;IAC3B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@k08200/mcp-probe",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Quality checker for MCP servers — validates protocol handshake, tool discovery, and response latency",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-probe": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsx src/cli.ts",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"test:coverage": "vitest run --coverage",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"ai",
|
|
25
|
+
"llm",
|
|
26
|
+
"cli",
|
|
27
|
+
"testing",
|
|
28
|
+
"validation",
|
|
29
|
+
"inspector",
|
|
30
|
+
"checker"
|
|
31
|
+
],
|
|
32
|
+
"author": "k08200",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://github.com/k08200/mcp-probe#readme",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/k08200/mcp-probe.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/k08200/mcp-probe/issues"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
44
|
+
"chalk": "^5.3.0",
|
|
45
|
+
"commander": "^12.1.0",
|
|
46
|
+
"ora": "^8.1.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.0.0",
|
|
50
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
51
|
+
"tsx": "^4.19.0",
|
|
52
|
+
"typescript": "^5.6.0",
|
|
53
|
+
"vitest": "^2.0.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|