mcp-medic 1.2.6 → 1.2.7
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/dist/cli.d.ts +1 -0
- package/dist/cli.js +10 -0
- package/dist/fleet.d.ts +1 -0
- package/dist/fleet.js +33 -21
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -113,6 +113,14 @@ export function parseArgs(argv) {
|
|
|
113
113
|
}
|
|
114
114
|
args.protocolVersion = val;
|
|
115
115
|
}
|
|
116
|
+
else if (arg === '--jobs') {
|
|
117
|
+
const value = argv[++i];
|
|
118
|
+
const parsed = value ? Number(value) : NaN;
|
|
119
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
120
|
+
throw new Error(`--jobs requires a positive safe integer, got: ${value ?? '(none)'}`);
|
|
121
|
+
}
|
|
122
|
+
args.jobs = parsed;
|
|
123
|
+
}
|
|
116
124
|
else {
|
|
117
125
|
positional.push(arg);
|
|
118
126
|
}
|
|
@@ -213,6 +221,7 @@ OPTIONS
|
|
|
213
221
|
--timeout <ms> Per-server handshake timeout in milliseconds (default: 5000)
|
|
214
222
|
--protocol-version <v> MCP protocolVersion to request: "auto" (default, latest supported)
|
|
215
223
|
or an explicit version, e.g. ${SUPPORTED_PROTOCOL_VERSIONS[SUPPORTED_PROTOCOL_VERSIONS.length - 1]}
|
|
224
|
+
--jobs <n> Maximum config files processed at once in check-all (default: 1)
|
|
216
225
|
--help, -h Show help
|
|
217
226
|
--version, -V Print the installed mcp-medic version
|
|
218
227
|
|
|
@@ -568,6 +577,7 @@ export async function main(argv = process.argv.slice(2), deps = {}) {
|
|
|
568
577
|
const fleetReport = await runFleetChecks(glob, {
|
|
569
578
|
checks,
|
|
570
579
|
timeoutMs: args.timeoutMs,
|
|
580
|
+
jobs: args.jobs,
|
|
571
581
|
verbose: args.verbose,
|
|
572
582
|
protocolVersion: args.protocolVersion,
|
|
573
583
|
});
|
package/dist/fleet.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare function findConfigFiles(globOrPattern: string, rootDir?: string)
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function runFleetChecks(globPattern: string, options?: RunOptions & {
|
|
39
39
|
cwd?: string;
|
|
40
|
+
jobs?: number;
|
|
40
41
|
}): Promise<FleetReport>;
|
|
41
42
|
/**
|
|
42
43
|
* Compares two MCP configurations to identify server and attribute drift.
|
package/dist/fleet.js
CHANGED
|
@@ -4,6 +4,20 @@ import { loadConfig } from './config-loader.js';
|
|
|
4
4
|
import { runChecks } from './orchestrator.js';
|
|
5
5
|
import { allChecks } from './checks/index.js';
|
|
6
6
|
import { redactRecord } from './redact.js';
|
|
7
|
+
async function mapWithConcurrency(items, concurrency, worker) {
|
|
8
|
+
const results = new Array(items.length);
|
|
9
|
+
let nextIndex = 0;
|
|
10
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
11
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
12
|
+
while (true) {
|
|
13
|
+
const index = nextIndex++;
|
|
14
|
+
if (index >= items.length)
|
|
15
|
+
return;
|
|
16
|
+
results[index] = await worker(items[index]);
|
|
17
|
+
}
|
|
18
|
+
}));
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
7
21
|
function findFilesMatching(dir, rootDir, pattern, results = []) {
|
|
8
22
|
if (!existsSync(dir))
|
|
9
23
|
return results;
|
|
@@ -72,38 +86,36 @@ export function findConfigFiles(globOrPattern, rootDir = process.cwd()) {
|
|
|
72
86
|
export async function runFleetChecks(globPattern, options = {}) {
|
|
73
87
|
const root = options.cwd ?? process.cwd();
|
|
74
88
|
const filePaths = findConfigFiles(globPattern, root);
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
const jobs = options.jobs ?? 1;
|
|
90
|
+
if (!Number.isSafeInteger(jobs) || jobs < 1) {
|
|
91
|
+
throw new TypeError(`jobs must be a positive safe integer, got: ${jobs}`);
|
|
92
|
+
}
|
|
93
|
+
const processed = await mapWithConcurrency(filePaths, jobs, async (filePath) => {
|
|
80
94
|
try {
|
|
81
95
|
const rawText = readFileSync(filePath, 'utf-8');
|
|
82
96
|
const rawJson = JSON.parse(rawText);
|
|
83
97
|
const { config, errors } = loadConfig(rawJson, filePath);
|
|
84
98
|
if (errors.length > 0 || !config) {
|
|
85
|
-
|
|
86
|
-
filePath,
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
totalErrors += errors.length;
|
|
90
|
-
continue;
|
|
99
|
+
return {
|
|
100
|
+
fileResult: { filePath, error: `Config validation failed: ${errors.join(', ')}` },
|
|
101
|
+
errorCount: errors.length,
|
|
102
|
+
};
|
|
91
103
|
}
|
|
92
104
|
const checks = options.checks ?? allChecks;
|
|
93
105
|
const report = await runChecks(config, { ...options, checks });
|
|
94
|
-
|
|
95
|
-
totalServers += report.summary.servers;
|
|
96
|
-
totalErrors += report.summary.errors;
|
|
97
|
-
totalWarnings += report.summary.warnings;
|
|
106
|
+
return { fileResult: { filePath, report }, errorCount: report.summary.errors };
|
|
98
107
|
}
|
|
99
108
|
catch (err) {
|
|
100
|
-
|
|
101
|
-
filePath,
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
totalErrors += 1;
|
|
109
|
+
return {
|
|
110
|
+
fileResult: { filePath, error: `Could not process file: ${err instanceof Error ? err.message : String(err)}` },
|
|
111
|
+
errorCount: 1,
|
|
112
|
+
};
|
|
105
113
|
}
|
|
106
|
-
}
|
|
114
|
+
});
|
|
115
|
+
const fileResults = processed.map((item) => item.fileResult);
|
|
116
|
+
const totalServers = fileResults.reduce((sum, result) => sum + (result.report?.summary.servers ?? 0), 0);
|
|
117
|
+
const totalErrors = processed.reduce((sum, item) => sum + item.errorCount, 0);
|
|
118
|
+
const totalWarnings = fileResults.reduce((sum, result) => sum + (result.report?.summary.warnings ?? 0), 0);
|
|
107
119
|
return {
|
|
108
120
|
totalFiles: filePaths.length,
|
|
109
121
|
successfulFiles: fileResults.filter((f) => !f.error).length,
|