mcp-trust 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 +153 -0
- package/bin/mcp-trust.js +2 -0
- package/dist/checks/github.d.ts +4 -0
- package/dist/checks/github.js +175 -0
- package/dist/checks/github.js.map +1 -0
- package/dist/checks/index.d.ts +2 -0
- package/dist/checks/index.js +3 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/scorer.d.ts +2 -0
- package/dist/checks/scorer.js +109 -0
- package/dist/checks/scorer.js.map +1 -0
- package/dist/commands/audit.d.ts +9 -0
- package/dist/commands/audit.js +53 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/dist/prober/index.d.ts +1 -0
- package/dist/prober/index.js +2 -0
- package/dist/prober/index.js.map +1 -0
- package/dist/prober/spawner.d.ts +2 -0
- package/dist/prober/spawner.js +230 -0
- package/dist/prober/spawner.js.map +1 -0
- package/dist/readers/claude.d.ts +2 -0
- package/dist/readers/claude.js +48 -0
- package/dist/readers/claude.js.map +1 -0
- package/dist/readers/cline.d.ts +2 -0
- package/dist/readers/cline.js +58 -0
- package/dist/readers/cline.js.map +1 -0
- package/dist/readers/codex.d.ts +2 -0
- package/dist/readers/codex.js +104 -0
- package/dist/readers/codex.js.map +1 -0
- package/dist/readers/cursor.d.ts +2 -0
- package/dist/readers/cursor.js +53 -0
- package/dist/readers/cursor.js.map +1 -0
- package/dist/readers/index.d.ts +2 -0
- package/dist/readers/index.js +18 -0
- package/dist/readers/index.js.map +1 -0
- package/dist/reporters/index.d.ts +2 -0
- package/dist/reporters/index.js +3 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/reporters/json.d.ts +2 -0
- package/dist/reporters/json.js +23 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/terminal.d.ts +3 -0
- package/dist/reporters/terminal.js +120 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mcp-doctor contributors
|
|
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,153 @@
|
|
|
1
|
+
# mcp-doctor (npm: mcp-trust)
|
|
2
|
+
|
|
3
|
+
**The MCP ecosystem is sick. 60% of npm-published servers fail basic connectivity. 52% are abandoned.** `mcp-doctor` reads the MCP servers installed on your machine, probes each one with the protocol handshake, and tells you which are alive, dead, dangerous, or fake — before they hit production.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx mcp-doctor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
mcp-doctor v0.1.0
|
|
11
|
+
Auditing MCP servers installed on this machine
|
|
12
|
+
|
|
13
|
+
[ALIVE] B github [claude] 14 tools, 4 no-arg, 47ms
|
|
14
|
+
[modelcontextprotocol/servers] 12d ago, 48201★, 1.2M dl/wk
|
|
15
|
+
[DEAD ] F someones-mcp [cursor]
|
|
16
|
+
spawn npx ENOENT
|
|
17
|
+
→ Verify the package is installed globally or use the full path
|
|
18
|
+
[ALIVE] D postgres [claude] 28 tools, 0 no-arg, 192ms
|
|
19
|
+
[tylersamples/mcp-postgres] 287d ago, 84★, 210 dl/wk
|
|
20
|
+
• Last commit was 287 days ago (6+ months)
|
|
21
|
+
• Only 210 weekly npm downloads (low usage)
|
|
22
|
+
→ Server is likely abandoned
|
|
23
|
+
[ALIVE] F rando-ai-tool [codex] 3 tools, 3 no-arg, 89ms
|
|
24
|
+
[github.com/x/x] 422d ago, 2★, 0 dl/wk
|
|
25
|
+
• 100% of tools take no arguments (3/3)
|
|
26
|
+
• Repository is archived (no longer maintained)
|
|
27
|
+
• Last commit was 422 days ago (over a year)
|
|
28
|
+
|
|
29
|
+
────────────────────────────────────────────────────────────
|
|
30
|
+
Total: 4 Alive: 3 Dead: 1 Other: 0 Avg score: 47/100
|
|
31
|
+
|
|
32
|
+
Worst offenders:
|
|
33
|
+
• rando-ai-tool
|
|
34
|
+
• someones-mcp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Why this exists
|
|
38
|
+
|
|
39
|
+
The MCP ecosystem crossed a threshold in 2025–2026 and never looked back. As of April 2026:
|
|
40
|
+
|
|
41
|
+
- **2,000+ servers** in the official registry, **19,700+** if you count every npm package
|
|
42
|
+
- **52% are abandoned** (no commits in 90+ days, broken builds, unpatched CVEs)
|
|
43
|
+
- **60% of npm-published servers fail the basic `tools/list` handshake** when probed cold
|
|
44
|
+
- **28% of tools exposed are empty-argument stubs** (`list_models`, `ping`) — useless to an agent
|
|
45
|
+
- **9,922 distinct tools** across 359 reachable servers — past the 20–128 tool budget of any single LLM client
|
|
46
|
+
|
|
47
|
+
Every developer who uses Claude Desktop, Cursor, Codex, or Cline has between 4 and 12 MCP servers running. The median team has six of them in the "dead" or "lightly maintained" tier. They just don't know it yet, because the agent doesn't call the broken path every day.
|
|
48
|
+
|
|
49
|
+
**`mcp-trust` is the missing trust layer for the MCP ecosystem.** It runs locally, takes 5 seconds, and tells you what is actually alive.
|
|
50
|
+
|
|
51
|
+
## What it checks
|
|
52
|
+
|
|
53
|
+
For every server it finds in your config, `mcp-doctor` runs:
|
|
54
|
+
|
|
55
|
+
1. **Protocol probe** — spawns the server, sends `initialize` and `tools/list`, kills the process if it doesn't respond in 15s
|
|
56
|
+
2. **GitHub health** — last commit date, star count, open issues, archive status
|
|
57
|
+
3. **Security advisories** — pulls active CVEs from the GitHub Advisory Database
|
|
58
|
+
4. **npm health** — weekly download count (low numbers = nobody's using it)
|
|
59
|
+
5. **Tool quality** — flags servers with >50 tools (exceeds LLM client budgets) and >50% empty-arg tools (usually stubs)
|
|
60
|
+
6. **Verdict** — A/B/C/D/F score with specific issues and recommendations
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx mcp-trust
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or install globally:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g mcp-trust
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
mcp-doctor # audit everything
|
|
78
|
+
mcp-doctor audit --json # machine-readable output
|
|
79
|
+
mcp-doctor audit --config-only # list configured servers, don't probe
|
|
80
|
+
mcp-doctor audit --fail-on-dead # exit 1 if any server is dead (for CI)
|
|
81
|
+
mcp-doctor audit --concurrency 8 # parallelize probes
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Config locations scanned
|
|
85
|
+
|
|
86
|
+
| Client | File |
|
|
87
|
+
|---|---|
|
|
88
|
+
| Claude Desktop | `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
89
|
+
| Cursor | `%APPDATA%\Cursor\mcp.json` |
|
|
90
|
+
| Codex | `~/.codex/config.toml` |
|
|
91
|
+
| Cline | `~/.cline/mcp.json` |
|
|
92
|
+
|
|
93
|
+
### CI integration
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
# .github/workflows/mcp-audit.yml
|
|
97
|
+
name: MCP audit
|
|
98
|
+
on: [pull_request]
|
|
99
|
+
jobs:
|
|
100
|
+
audit:
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
- run: npx mcp-trust audit --fail-on-dead --json > mcp-report.json
|
|
105
|
+
- uses: actions/upload-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: mcp-audit
|
|
108
|
+
path: mcp-report.json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Environment variables
|
|
112
|
+
|
|
113
|
+
- `GITHUB_TOKEN` — bumps GitHub API rate limit from 60 to 5000 req/h
|
|
114
|
+
|
|
115
|
+
## Verdict scale
|
|
116
|
+
|
|
117
|
+
| Score | Verdict | Meaning |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| 90–100 | A | Healthy, maintained, responsive |
|
|
120
|
+
| 75–89 | B | Good, with minor concerns |
|
|
121
|
+
| 60–74 | C | Usable, but watch for issues |
|
|
122
|
+
| 40–59 | D | Multiple problems, plan a replacement |
|
|
123
|
+
| 0–39 | F | Dead, dangerous, or abandoned — remove it |
|
|
124
|
+
|
|
125
|
+
## Status types
|
|
126
|
+
|
|
127
|
+
- `ALIVE` — responded to `initialize` and `tools/list` within timeout
|
|
128
|
+
- `DEAD` — process exited before responding
|
|
129
|
+
- `HANG` — process started but never completed the handshake
|
|
130
|
+
- `AUTH` — server requires credentials to even respond
|
|
131
|
+
- `ARGS` — server expects CLI arguments that are missing
|
|
132
|
+
- `ENV` — server expects an environment variable that isn't set
|
|
133
|
+
- `NOEX` — command not found (not installed globally)
|
|
134
|
+
- `BRKN` — other failure (syntax error, runtime exception)
|
|
135
|
+
|
|
136
|
+
## Output
|
|
137
|
+
|
|
138
|
+
A grade A through F, a list of specific issues, and concrete recommendations. Designed to be readable by humans in 5 seconds and parseable by tools via `--json`.
|
|
139
|
+
|
|
140
|
+
## Limitations
|
|
141
|
+
|
|
142
|
+
- Only audits servers configured locally. Does not see marketplace servers you haven't installed
|
|
143
|
+
- For `stdio`-based servers only. HTTP/streamable MCP servers are not yet supported
|
|
144
|
+
- Server reputation signals (GitHub stars, downloads) can be gamed. Treat them as one signal among many
|
|
145
|
+
- Network access required for GitHub / npm lookups (the local probe works offline)
|
|
146
|
+
|
|
147
|
+
## Contributing
|
|
148
|
+
|
|
149
|
+
PRs welcome. The protocol probe is in `src/prober/spawner.ts`, the scoring rules are in `src/checks/scorer.ts`. Add a new client reader by following the pattern in `src/readers/claude.ts`.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
package/bin/mcp-trust.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RepoHealth } from "../types.js";
|
|
2
|
+
export declare function parseGitHubRepo(repo: string | undefined): string | null;
|
|
3
|
+
export declare function checkRepoHealth(command: string, args: string[], env: Record<string, string> | undefined, token?: string): Promise<RepoHealth | null>;
|
|
4
|
+
export declare function extractPackageName(command: string, args: string[]): string | null;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const GITHUB_API = "https://api.github.com";
|
|
2
|
+
const NPM_API = "https://api.npmjs.org";
|
|
3
|
+
export function parseGitHubRepo(repo) {
|
|
4
|
+
if (!repo)
|
|
5
|
+
return null;
|
|
6
|
+
const cleaned = repo.replace(/^git\+/, "").replace(/\.git$/, "");
|
|
7
|
+
const match = cleaned.match(/github\.com[/:]([\w.-]+)\/([\w.-]+)/);
|
|
8
|
+
if (!match)
|
|
9
|
+
return null;
|
|
10
|
+
return `${match[1]}/${match[2]}`;
|
|
11
|
+
}
|
|
12
|
+
async function fetchNpmRepo(packageName) {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`${NPM_API}/${encodeURIComponent(packageName)}/latest`, {
|
|
15
|
+
headers: { Accept: "application/json" },
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
return null;
|
|
19
|
+
const data = (await res.json());
|
|
20
|
+
const repo = data.repository;
|
|
21
|
+
if (typeof repo === "string")
|
|
22
|
+
return parseGitHubRepo(repo);
|
|
23
|
+
if (repo && typeof repo === "object")
|
|
24
|
+
return parseGitHubRepo(repo.url);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function fetchNpmDownloads(packageName) {
|
|
32
|
+
try {
|
|
33
|
+
const res = await fetch(`${NPM_API}/downloads/point/last-week/${encodeURIComponent(packageName)}`, {
|
|
34
|
+
headers: { Accept: "application/json" },
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
return 0;
|
|
38
|
+
const data = (await res.json());
|
|
39
|
+
return data.downloads ?? 0;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function fetchGitHubRepo(repo, token) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(`${GITHUB_API}/repos/${repo}`, {
|
|
48
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok)
|
|
51
|
+
return null;
|
|
52
|
+
return (await res.json());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function fetchLastCommit(repo, token) {
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(`${GITHUB_API}/repos/${repo}/commits?per_page=1`, {
|
|
61
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok)
|
|
64
|
+
return null;
|
|
65
|
+
const data = (await res.json());
|
|
66
|
+
return data[0] ?? null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function fetchAdvisories(repo, token) {
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(`${GITHUB_API}/advisories?ecosystem=npm&affects=${encodeURIComponent(repo)}`, {
|
|
75
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok)
|
|
78
|
+
return [];
|
|
79
|
+
const data = (await res.json());
|
|
80
|
+
return data.filter((a) => !a.withdrawn_at);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function checkRepoHealth(command, args, env, token) {
|
|
87
|
+
const packageName = extractPackageName(command, args);
|
|
88
|
+
if (!packageName)
|
|
89
|
+
return null;
|
|
90
|
+
const repo = await fetchNpmRepo(packageName);
|
|
91
|
+
if (!repo)
|
|
92
|
+
return null;
|
|
93
|
+
const [repoInfo, lastCommit, advisories, weeklyDownloads] = await Promise.all([
|
|
94
|
+
fetchGitHubRepo(repo, token),
|
|
95
|
+
fetchLastCommit(repo, token),
|
|
96
|
+
fetchAdvisories(repo, token),
|
|
97
|
+
fetchNpmDownloads(packageName),
|
|
98
|
+
]);
|
|
99
|
+
if (!repoInfo)
|
|
100
|
+
return null;
|
|
101
|
+
const lastCommitDate = lastCommit?.commit?.author?.date ?? repoInfo.pushed_at;
|
|
102
|
+
const daysAgo = Math.floor((Date.now() - new Date(lastCommitDate).getTime()) / 86_400_000);
|
|
103
|
+
return {
|
|
104
|
+
repo,
|
|
105
|
+
lastCommit: lastCommitDate,
|
|
106
|
+
lastCommitDaysAgo: daysAgo,
|
|
107
|
+
stars: repoInfo.stargazers_count,
|
|
108
|
+
openIssues: repoInfo.open_issues_count,
|
|
109
|
+
cveCount: advisories.length,
|
|
110
|
+
weeklyDownloads,
|
|
111
|
+
contributors: 0,
|
|
112
|
+
archived: repoInfo.archived,
|
|
113
|
+
sourceUrl: `https://github.com/${repo}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function extractPackageName(command, args) {
|
|
117
|
+
if (command === "npx" || command.endsWith("/npx") || command.endsWith("\\npx.exe")) {
|
|
118
|
+
for (let i = 0; i < args.length; i++) {
|
|
119
|
+
const arg = args[i];
|
|
120
|
+
if (!arg)
|
|
121
|
+
continue;
|
|
122
|
+
if (arg === "-y" || arg === "--yes")
|
|
123
|
+
continue;
|
|
124
|
+
if (arg === "-p" || arg === "--package") {
|
|
125
|
+
return args[i + 1] ?? null;
|
|
126
|
+
}
|
|
127
|
+
if (arg.startsWith("@") || arg.startsWith("npm:")) {
|
|
128
|
+
return arg.replace(/^npm:/, "");
|
|
129
|
+
}
|
|
130
|
+
if (arg && !arg.startsWith("-")) {
|
|
131
|
+
const cleaned = arg.startsWith("npm:") ? arg.slice(4) : arg;
|
|
132
|
+
const match = cleaned.match(/^(@?[\w./-]+)/);
|
|
133
|
+
if (match)
|
|
134
|
+
return match[1] ?? null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (command === "uvx" || command === "uv" || command === "pipx" || command === "python" || command === "python3") {
|
|
140
|
+
for (let i = 0; i < args.length; i++) {
|
|
141
|
+
const arg = args[i];
|
|
142
|
+
if (!arg)
|
|
143
|
+
continue;
|
|
144
|
+
if (arg === "run" || arg === "tool" || arg === "exec")
|
|
145
|
+
continue;
|
|
146
|
+
if (arg === "-m")
|
|
147
|
+
continue;
|
|
148
|
+
if (arg && !arg.startsWith("-")) {
|
|
149
|
+
return arg;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (command === "docker") {
|
|
155
|
+
for (let i = 0; i < args.length; i++) {
|
|
156
|
+
const arg = args[i];
|
|
157
|
+
if (!arg)
|
|
158
|
+
continue;
|
|
159
|
+
if (arg === "run")
|
|
160
|
+
continue;
|
|
161
|
+
if (arg === "-i" || arg === "--interactive" || arg === "--rm")
|
|
162
|
+
continue;
|
|
163
|
+
if (arg === "-e" || arg === "--env") {
|
|
164
|
+
i++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (arg.includes("/")) {
|
|
168
|
+
return arg.split("/").pop() ?? null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/checks/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export function scoreServer(probed, health) {
|
|
2
|
+
let score = 100;
|
|
3
|
+
const issues = [];
|
|
4
|
+
const recommendations = [];
|
|
5
|
+
if (probed.status !== "alive") {
|
|
6
|
+
score -= 80;
|
|
7
|
+
issues.push(`Server status: ${probed.status}`);
|
|
8
|
+
if (probed.status === "dead") {
|
|
9
|
+
issues.push("Process exited before responding to initialize");
|
|
10
|
+
recommendations.push("Check the install command and try running it manually");
|
|
11
|
+
}
|
|
12
|
+
else if (probed.status === "hangs") {
|
|
13
|
+
issues.push("Server did not respond within 5s");
|
|
14
|
+
recommendations.push("Server may be waiting on credentials or external network");
|
|
15
|
+
}
|
|
16
|
+
else if (probed.status === "install_error") {
|
|
17
|
+
issues.push("Command not found or not executable");
|
|
18
|
+
recommendations.push("Verify the package is installed globally or use the full path");
|
|
19
|
+
}
|
|
20
|
+
else if (probed.status === "auth_required") {
|
|
21
|
+
issues.push("Server requires credentials before it will respond");
|
|
22
|
+
recommendations.push("Set the required API keys in the env section of your MCP config");
|
|
23
|
+
}
|
|
24
|
+
else if (probed.status === "needs_args") {
|
|
25
|
+
issues.push("Server expects CLI arguments that are missing");
|
|
26
|
+
recommendations.push("Add the required arguments to the args array");
|
|
27
|
+
}
|
|
28
|
+
else if (probed.status === "needs_env") {
|
|
29
|
+
issues.push("Server expects an environment variable that is not set");
|
|
30
|
+
recommendations.push("Set the required env var in the env section of your MCP config");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (probed.status === "alive") {
|
|
34
|
+
if (probed.emptyArgTools > 0 && probed.toolCount > 0) {
|
|
35
|
+
const pct = Math.round((probed.emptyArgTools / probed.toolCount) * 100);
|
|
36
|
+
if (pct > 50) {
|
|
37
|
+
score -= 10;
|
|
38
|
+
issues.push(`${pct}% of tools take no arguments (${probed.emptyArgTools}/${probed.toolCount})`);
|
|
39
|
+
recommendations.push("Tools with no required args are often stubs or discovery probes");
|
|
40
|
+
}
|
|
41
|
+
else if (pct > 25) {
|
|
42
|
+
score -= 5;
|
|
43
|
+
issues.push(`${pct}% of tools take no arguments`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (probed.toolCount > 50) {
|
|
47
|
+
score -= 10;
|
|
48
|
+
issues.push(`Exposes ${probed.toolCount} tools (most LLM clients cap at 20-128)`);
|
|
49
|
+
recommendations.push("This server will likely exceed your client's tool budget");
|
|
50
|
+
}
|
|
51
|
+
else if (probed.toolCount === 0) {
|
|
52
|
+
score -= 20;
|
|
53
|
+
issues.push("Server responds but exposes zero tools");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (health) {
|
|
57
|
+
if (health.archived) {
|
|
58
|
+
score -= 70;
|
|
59
|
+
issues.push("Repository is archived (no longer maintained)");
|
|
60
|
+
recommendations.push("Find an actively maintained alternative");
|
|
61
|
+
}
|
|
62
|
+
if (health.lastCommitDaysAgo !== null) {
|
|
63
|
+
if (health.lastCommitDaysAgo > 365) {
|
|
64
|
+
score -= 30;
|
|
65
|
+
issues.push(`Last commit was ${health.lastCommitDaysAgo} days ago (over a year)`);
|
|
66
|
+
recommendations.push("Server is likely abandoned");
|
|
67
|
+
}
|
|
68
|
+
else if (health.lastCommitDaysAgo > 180) {
|
|
69
|
+
score -= 15;
|
|
70
|
+
issues.push(`Last commit was ${health.lastCommitDaysAgo} days ago (6+ months)`);
|
|
71
|
+
}
|
|
72
|
+
else if (health.lastCommitDaysAgo > 90) {
|
|
73
|
+
score -= 5;
|
|
74
|
+
issues.push(`Last commit was ${health.lastCommitDaysAgo} days ago`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (health.cveCount > 0) {
|
|
78
|
+
score -= 25;
|
|
79
|
+
issues.push(`${health.cveCount} active security advisory${health.cveCount === 1 ? "" : "ies"} on GitHub`);
|
|
80
|
+
recommendations.push("Review advisories at " + health.sourceUrl + "/security/advisories");
|
|
81
|
+
}
|
|
82
|
+
if (health.weeklyDownloads > 0 && health.weeklyDownloads < 100) {
|
|
83
|
+
score -= 10;
|
|
84
|
+
issues.push(`Only ${health.weeklyDownloads} weekly npm downloads (low usage)`);
|
|
85
|
+
}
|
|
86
|
+
else if (health.weeklyDownloads >= 1_000_000) {
|
|
87
|
+
score += 5;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (probed.status === "alive") {
|
|
91
|
+
score -= 5;
|
|
92
|
+
issues.push("Could not resolve to a GitHub repo for health check");
|
|
93
|
+
}
|
|
94
|
+
score = Math.max(0, Math.min(100, score));
|
|
95
|
+
const verdict = scoreToVerdict(score);
|
|
96
|
+
return { server: probed, health, verdict, score, issues, recommendations };
|
|
97
|
+
}
|
|
98
|
+
function scoreToVerdict(score) {
|
|
99
|
+
if (score >= 90)
|
|
100
|
+
return "A";
|
|
101
|
+
if (score >= 75)
|
|
102
|
+
return "B";
|
|
103
|
+
if (score >= 60)
|
|
104
|
+
return "C";
|
|
105
|
+
if (score >= 40)
|
|
106
|
+
return "D";
|
|
107
|
+
return "F";
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sourceRoot":"","sources":["../../src/checks/scorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ServerReport } from "../types.js";
|
|
2
|
+
export interface AuditOptions {
|
|
3
|
+
json?: boolean;
|
|
4
|
+
failOnDead?: boolean;
|
|
5
|
+
configOnly?: boolean;
|
|
6
|
+
concurrency?: number;
|
|
7
|
+
githubToken?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function runAudit(opts?: AuditOptions): Promise<ServerReport[]>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readAllConfigs } from "../readers/index.js";
|
|
2
|
+
import { probeServer } from "../prober/index.js";
|
|
3
|
+
import { checkRepoHealth, scoreServer } from "../checks/index.js";
|
|
4
|
+
import { renderReport } from "../reporters/index.js";
|
|
5
|
+
export async function runAudit(opts = {}) {
|
|
6
|
+
const configs = readAllConfigs();
|
|
7
|
+
if (configs.length === 0 && !opts.json) {
|
|
8
|
+
renderReport([]);
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
if (opts.configOnly) {
|
|
12
|
+
const reports = configs.map((c) => scoreServer({
|
|
13
|
+
config: c,
|
|
14
|
+
status: "not_probed",
|
|
15
|
+
probedAt: Date.now(),
|
|
16
|
+
durationMs: 0,
|
|
17
|
+
toolCount: 0,
|
|
18
|
+
emptyArgTools: 0,
|
|
19
|
+
toolNames: [],
|
|
20
|
+
}, null));
|
|
21
|
+
if (opts.json)
|
|
22
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
23
|
+
else
|
|
24
|
+
renderReport(reports);
|
|
25
|
+
return reports;
|
|
26
|
+
}
|
|
27
|
+
const concurrency = opts.concurrency ?? 4;
|
|
28
|
+
const token = opts.githubToken ?? process.env.GITHUB_TOKEN;
|
|
29
|
+
const reports = [];
|
|
30
|
+
for (let i = 0; i < configs.length; i += concurrency) {
|
|
31
|
+
const batch = configs.slice(i, i + concurrency);
|
|
32
|
+
const batchResults = await Promise.all(batch.map(async (cfg) => {
|
|
33
|
+
const probed = await probeServer(cfg);
|
|
34
|
+
const health = await checkRepoHealth(cfg.command, cfg.args, cfg.env, token);
|
|
35
|
+
return scoreServer(probed, health);
|
|
36
|
+
}));
|
|
37
|
+
reports.push(...batchResults);
|
|
38
|
+
}
|
|
39
|
+
if (opts.json) {
|
|
40
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
renderReport(reports);
|
|
44
|
+
}
|
|
45
|
+
if (opts.failOnDead) {
|
|
46
|
+
const hasFailing = reports.some((r) => r.server.status !== "alive" && r.server.status !== "not_probed");
|
|
47
|
+
if (hasFailing) {
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return reports;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.d.ts
ADDED