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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/bin/mcp-trust.js +2 -0
  4. package/dist/checks/github.d.ts +4 -0
  5. package/dist/checks/github.js +175 -0
  6. package/dist/checks/github.js.map +1 -0
  7. package/dist/checks/index.d.ts +2 -0
  8. package/dist/checks/index.js +3 -0
  9. package/dist/checks/index.js.map +1 -0
  10. package/dist/checks/scorer.d.ts +2 -0
  11. package/dist/checks/scorer.js +109 -0
  12. package/dist/checks/scorer.js.map +1 -0
  13. package/dist/commands/audit.d.ts +9 -0
  14. package/dist/commands/audit.js +53 -0
  15. package/dist/commands/audit.js.map +1 -0
  16. package/dist/commands/index.d.ts +2 -0
  17. package/dist/commands/index.js +2 -0
  18. package/dist/commands/index.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +96 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/prober/index.d.ts +1 -0
  23. package/dist/prober/index.js +2 -0
  24. package/dist/prober/index.js.map +1 -0
  25. package/dist/prober/spawner.d.ts +2 -0
  26. package/dist/prober/spawner.js +230 -0
  27. package/dist/prober/spawner.js.map +1 -0
  28. package/dist/readers/claude.d.ts +2 -0
  29. package/dist/readers/claude.js +48 -0
  30. package/dist/readers/claude.js.map +1 -0
  31. package/dist/readers/cline.d.ts +2 -0
  32. package/dist/readers/cline.js +58 -0
  33. package/dist/readers/cline.js.map +1 -0
  34. package/dist/readers/codex.d.ts +2 -0
  35. package/dist/readers/codex.js +104 -0
  36. package/dist/readers/codex.js.map +1 -0
  37. package/dist/readers/cursor.d.ts +2 -0
  38. package/dist/readers/cursor.js +53 -0
  39. package/dist/readers/cursor.js.map +1 -0
  40. package/dist/readers/index.d.ts +2 -0
  41. package/dist/readers/index.js +18 -0
  42. package/dist/readers/index.js.map +1 -0
  43. package/dist/reporters/index.d.ts +2 -0
  44. package/dist/reporters/index.js +3 -0
  45. package/dist/reporters/index.js.map +1 -0
  46. package/dist/reporters/json.d.ts +2 -0
  47. package/dist/reporters/json.js +23 -0
  48. package/dist/reporters/json.js.map +1 -0
  49. package/dist/reporters/terminal.d.ts +3 -0
  50. package/dist/reporters/terminal.js +120 -0
  51. package/dist/reporters/terminal.js.map +1 -0
  52. package/dist/types.d.ts +50 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. 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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import("../dist/index.js");
@@ -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,2 @@
1
+ export { checkRepoHealth } from "./github.js";
2
+ export { scoreServer } from "./scorer.js";
@@ -0,0 +1,3 @@
1
+ export { checkRepoHealth } from "./github.js";
2
+ export { scoreServer } from "./scorer.js";
3
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ import type { ProbedServer, RepoHealth, ServerReport } from "../types.js";
2
+ export declare function scoreServer(probed: ProbedServer, health: RepoHealth | null): ServerReport;
@@ -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,2 @@
1
+ export { runAudit } from "./audit.js";
2
+ export type { AuditOptions } from "./audit.js";
@@ -0,0 +1,2 @@
1
+ export { runAudit } from "./audit.js";
2
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};