@bryan-thompson/inspector-assessment-cli 1.26.6 → 1.27.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.
@@ -9,7 +9,8 @@
9
9
  * @module cli/lib/cli-parser
10
10
  */
11
11
  import { ASSESSMENT_CATEGORY_METADATA, } from "../../../client/lib/lib/assessmentTypes.js";
12
- import { ASSESSMENT_PROFILES, isValidProfileName, getProfileHelpText, } from "../profiles.js";
12
+ import { ASSESSMENT_PROFILES, isValidProfileName, getProfileHelpText, TIER_1_CORE_SECURITY, TIER_2_COMPLIANCE, TIER_3_CAPABILITY, TIER_4_EXTENDED, } from "../profiles.js";
13
+ import packageJson from "../../package.json" with { type: "json" };
13
14
  // ============================================================================
14
15
  // Constants
15
16
  // ============================================================================
@@ -249,6 +250,15 @@ export function parseArgs(argv) {
249
250
  }
250
251
  break;
251
252
  }
253
+ case "--list-modules":
254
+ printModules();
255
+ options.listModules = true;
256
+ return options;
257
+ case "--version":
258
+ case "-V":
259
+ printVersion();
260
+ options.versionRequested = true;
261
+ return options;
252
262
  case "--help":
253
263
  case "-h":
254
264
  printHelp();
@@ -310,8 +320,20 @@ export function parseArgs(argv) {
310
320
  return options;
311
321
  }
312
322
  // ============================================================================
313
- // Help Text
323
+ // Version and Help Text
314
324
  // ============================================================================
325
+ /**
326
+ * Get package version from package.json
327
+ */
328
+ function getPackageVersion() {
329
+ return packageJson.version;
330
+ }
331
+ /**
332
+ * Print version to console
333
+ */
334
+ export function printVersion() {
335
+ console.log(`mcp-assess-full ${getPackageVersion()}`);
336
+ }
315
337
  /**
316
338
  * Print help message to console
317
339
  */
@@ -349,6 +371,7 @@ Options:
349
371
  --silent Suppress all diagnostic logging
350
372
  --log-level <level> Set log level: silent, error, warn, info (default), debug
351
373
  Also supports LOG_LEVEL environment variable
374
+ --version, -V Show version number
352
375
  --help, -h Show this help message
353
376
 
354
377
  Environment Variables:
@@ -417,3 +440,61 @@ Examples:
417
440
  mcp-assess-full --server my-server --compare ./baseline.json --diff-only
418
441
  `);
419
442
  }
443
+ /**
444
+ * Module description mappings for printModules output.
445
+ * Uses human-friendly descriptions that may differ from ASSESSMENT_CATEGORY_METADATA.
446
+ */
447
+ const MODULE_DESCRIPTIONS = {
448
+ functionality: "Tool functionality validation",
449
+ security: "Security vulnerability detection (23 attack patterns)",
450
+ temporal: "Temporal/rug pull detection",
451
+ errorHandling: "Error handling compliance",
452
+ protocolCompliance: "MCP protocol + JSON-RPC validation",
453
+ aupCompliance: "Acceptable use policy compliance",
454
+ toolAnnotations: "Tool annotation validation (readOnlyHint, destructiveHint)",
455
+ prohibitedLibraries: "Prohibited library detection",
456
+ manifestValidation: "MCPB manifest.json validation",
457
+ authentication: "OAuth/auth evaluation",
458
+ resources: "Resource path traversal + sensitive data exposure",
459
+ prompts: "Prompt AUP compliance + injection testing",
460
+ crossCapability: "Cross-capability attack chain detection",
461
+ developerExperience: "Documentation + usability assessment",
462
+ portability: "Cross-platform compatibility",
463
+ externalAPIScanner: "External API detection (requires --source)",
464
+ };
465
+ /**
466
+ * Print available modules organized by tier
467
+ */
468
+ export function printModules() {
469
+ const formatModule = (name) => {
470
+ const desc = MODULE_DESCRIPTIONS[name] ||
471
+ ASSESSMENT_CATEGORY_METADATA[name]?.description ||
472
+ "";
473
+ return ` ${name.padEnd(22)} ${desc}`;
474
+ };
475
+ console.log(`
476
+ Available Assessment Modules (16 total):
477
+
478
+ Tier 1 - Core Security (${TIER_1_CORE_SECURITY.length} modules):
479
+ ${TIER_1_CORE_SECURITY.map(formatModule).join("\n")}
480
+
481
+ Tier 2 - Compliance (${TIER_2_COMPLIANCE.length} modules):
482
+ ${TIER_2_COMPLIANCE.map(formatModule).join("\n")}
483
+
484
+ Tier 3 - Capability-Based (${TIER_3_CAPABILITY.length} modules):
485
+ ${TIER_3_CAPABILITY.map(formatModule).join("\n")}
486
+
487
+ Tier 4 - Extended (${TIER_4_EXTENDED.length} modules):
488
+ ${TIER_4_EXTENDED.map(formatModule).join("\n")}
489
+
490
+ Usage:
491
+ --only-modules <list> Run only specified modules (comma-separated)
492
+ --skip-modules <list> Skip specified modules (comma-separated)
493
+ --profile <name> Use predefined profile (quick, security, compliance, full)
494
+
495
+ Examples:
496
+ mcp-assess-full my-server --only-modules functionality,security
497
+ mcp-assess-full my-server --skip-modules temporal,portability
498
+ mcp-assess-full my-server --profile compliance
499
+ `);
500
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Comparison Handler Module
3
+ *
4
+ * Handles assessment comparison/diff logic for comparing
5
+ * current results against a baseline.
6
+ *
7
+ * @module cli/lib/comparison-handler
8
+ */
9
+ import * as fs from "fs";
10
+ import { compareAssessments, } from "../../../client/lib/lib/assessmentDiffer.js";
11
+ import { formatDiffAsMarkdown } from "../../../client/lib/lib/reportFormatters/DiffReportFormatter.js";
12
+ // ============================================================================
13
+ // Comparison Functions
14
+ // ============================================================================
15
+ /**
16
+ * Handle comparison mode - compare current results against a baseline.
17
+ *
18
+ * @param results - Current assessment results
19
+ * @param options - CLI options including comparePath and diffOnly
20
+ * @returns ComparisonResult if comparison was performed, null if no comparison
21
+ */
22
+ export function handleComparison(results, options) {
23
+ if (!options.comparePath) {
24
+ return null;
25
+ }
26
+ if (!fs.existsSync(options.comparePath)) {
27
+ console.error(`Error: Baseline file not found: ${options.comparePath}`);
28
+ // Return null to indicate comparison failed - caller handles null returns
29
+ return null;
30
+ }
31
+ const baselineData = JSON.parse(fs.readFileSync(options.comparePath, "utf-8"));
32
+ // Validate baseline has expected structure
33
+ if (!baselineData.functionality || !baselineData.security) {
34
+ console.warn("Warning: Baseline file may be incomplete (missing functionality or security)");
35
+ }
36
+ const baseline = baselineData;
37
+ const diff = compareAssessments(baseline, results);
38
+ // Handle diff-only mode
39
+ if (options.diffOnly) {
40
+ let diffPath;
41
+ if (options.format === "markdown") {
42
+ diffPath =
43
+ options.outputPath || `/tmp/inspector-diff-${options.serverName}.md`;
44
+ fs.writeFileSync(diffPath, formatDiffAsMarkdown(diff));
45
+ }
46
+ else {
47
+ diffPath =
48
+ options.outputPath || `/tmp/inspector-diff-${options.serverName}.json`;
49
+ fs.writeFileSync(diffPath, JSON.stringify(diff, null, 2));
50
+ }
51
+ const exitCode = diff.summary.overallChange === "regressed" ? 1 : 0;
52
+ return { diff, exitCode, diffOutputPath: diffPath };
53
+ }
54
+ // Return comparison result for normal mode
55
+ const exitCode = diff.summary.overallChange === "regressed" ? 1 : 0;
56
+ return { diff, exitCode };
57
+ }
58
+ /**
59
+ * Display comparison summary to console.
60
+ *
61
+ * @param diff - Assessment diff to display
62
+ */
63
+ export function displayComparisonSummary(diff) {
64
+ console.log("\n" + "=".repeat(70));
65
+ console.log("VERSION COMPARISON");
66
+ console.log("=".repeat(70));
67
+ console.log(`Baseline: ${diff.baseline.version || "N/A"} (${diff.baseline.date})`);
68
+ console.log(`Current: ${diff.current.version || "N/A"} (${diff.current.date})`);
69
+ console.log(`Overall Change: ${diff.summary.overallChange.toUpperCase()}`);
70
+ console.log(`Modules Improved: ${diff.summary.modulesImproved}`);
71
+ console.log(`Modules Regressed: ${diff.summary.modulesRegressed}`);
72
+ if (diff.securityDelta.newVulnerabilities.length > 0) {
73
+ console.log(`\nāš ļø NEW VULNERABILITIES: ${diff.securityDelta.newVulnerabilities.length}`);
74
+ }
75
+ if (diff.securityDelta.fixedVulnerabilities.length > 0) {
76
+ console.log(`āœ… FIXED VULNERABILITIES: ${diff.securityDelta.fixedVulnerabilities.length}`);
77
+ }
78
+ if (diff.functionalityDelta.newBrokenTools.length > 0) {
79
+ console.log(`āŒ NEW BROKEN TOOLS: ${diff.functionalityDelta.newBrokenTools.length}`);
80
+ }
81
+ if (diff.functionalityDelta.fixedTools.length > 0) {
82
+ console.log(`āœ… FIXED TOOLS: ${diff.functionalityDelta.fixedTools.length}`);
83
+ }
84
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Result Output Module
3
+ *
4
+ * Handles saving assessment results to files and displaying
5
+ * summaries to the console.
6
+ *
7
+ * @module cli/lib/result-output
8
+ */
9
+ import * as fs from "fs";
10
+ import { ASSESSMENT_CATEGORY_METADATA, } from "../../../client/lib/lib/assessmentTypes.js";
11
+ import { createFormatter } from "../../../client/lib/lib/reportFormatters/index.js";
12
+ import { generatePolicyComplianceReport } from "../../../client/lib/services/assessment/PolicyComplianceGenerator.js";
13
+ // ============================================================================
14
+ // Result Output
15
+ // ============================================================================
16
+ /**
17
+ * Save results to file with appropriate format
18
+ */
19
+ export function saveResults(serverName, results, options) {
20
+ const format = options.format || "json";
21
+ // Generate policy compliance report if requested
22
+ const policyReport = options.includePolicy
23
+ ? generatePolicyComplianceReport(results, serverName)
24
+ : undefined;
25
+ // Create formatter with options
26
+ const formatter = createFormatter({
27
+ format,
28
+ includePolicyMapping: options.includePolicy,
29
+ policyReport,
30
+ serverName,
31
+ includeDetails: true,
32
+ prettyPrint: true,
33
+ });
34
+ const fileExtension = formatter.getFileExtension();
35
+ const defaultPath = `/tmp/inspector-full-assessment-${serverName}${fileExtension}`;
36
+ const finalPath = options.outputPath || defaultPath;
37
+ // For JSON format, add metadata wrapper
38
+ if (format === "json") {
39
+ // Filter out undefined/skipped modules from results (--skip-modules support)
40
+ const filteredResults = Object.fromEntries(Object.entries(results).filter(([_, v]) => v !== undefined));
41
+ const output = {
42
+ timestamp: new Date().toISOString(),
43
+ assessmentType: "full",
44
+ ...filteredResults,
45
+ ...(policyReport ? { policyCompliance: policyReport } : {}),
46
+ };
47
+ fs.writeFileSync(finalPath, JSON.stringify(output, null, 2));
48
+ }
49
+ else {
50
+ // For other formats (markdown), use the formatter
51
+ const content = formatter.format(results);
52
+ fs.writeFileSync(finalPath, content);
53
+ }
54
+ return finalPath;
55
+ }
56
+ // ============================================================================
57
+ // Summary Display
58
+ // ============================================================================
59
+ /**
60
+ * Display summary to console
61
+ */
62
+ export function displaySummary(results) {
63
+ const { overallStatus, summary, totalTestsRun, executionTime,
64
+ // Destructuring order matches display order below
65
+ functionality, security, documentation, errorHandling, usability, mcpSpecCompliance, aupCompliance, toolAnnotations, prohibitedLibraries, manifestValidation, portability, externalAPIScanner, authentication, temporal, resources, prompts, crossCapability, } = results;
66
+ console.log("\n" + "=".repeat(70));
67
+ console.log("FULL ASSESSMENT RESULTS");
68
+ console.log("=".repeat(70));
69
+ console.log(`Server: ${results.serverName}`);
70
+ console.log(`Overall Status: ${overallStatus}`);
71
+ console.log(`Total Tests Run: ${totalTestsRun}`);
72
+ console.log(`Execution Time: ${executionTime}ms`);
73
+ console.log("-".repeat(70));
74
+ console.log("\nšŸ“Š MODULE STATUS:");
75
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
+ const modules = [
77
+ ["Functionality", functionality, "functionality"],
78
+ ["Security", security, "security"],
79
+ ["Documentation", documentation, "documentation"],
80
+ ["Error Handling", errorHandling, "errorHandling"],
81
+ ["Usability", usability, "usability"],
82
+ ["MCP Spec Compliance", mcpSpecCompliance, "mcpSpecCompliance"],
83
+ ["AUP Compliance", aupCompliance, "aupCompliance"],
84
+ ["Tool Annotations", toolAnnotations, "toolAnnotations"],
85
+ ["Prohibited Libraries", prohibitedLibraries, "prohibitedLibraries"],
86
+ ["Manifest Validation", manifestValidation, "manifestValidation"],
87
+ ["Portability", portability, "portability"],
88
+ ["External API Scanner", externalAPIScanner, "externalAPIScanner"],
89
+ ["Authentication", authentication, "authentication"],
90
+ ["Temporal", temporal, "temporal"],
91
+ ["Resources", resources, "resources"],
92
+ ["Prompts", prompts, "prompts"],
93
+ ["Cross-Capability", crossCapability, "crossCapability"],
94
+ ];
95
+ for (const [name, module, categoryKey] of modules) {
96
+ if (module) {
97
+ const metadata = ASSESSMENT_CATEGORY_METADATA[categoryKey];
98
+ const optionalMarker = metadata?.tier === "optional" ? " (optional)" : "";
99
+ const icon = module.status === "PASS"
100
+ ? "āœ…"
101
+ : module.status === "FAIL"
102
+ ? "āŒ"
103
+ : "āš ļø";
104
+ console.log(` ${icon} ${name}${optionalMarker}: ${module.status}`);
105
+ }
106
+ }
107
+ console.log("\nšŸ“‹ KEY FINDINGS:");
108
+ console.log(` ${summary}`);
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ const securityModule = security;
111
+ if (securityModule?.vulnerabilities?.length > 0) {
112
+ const vulns = securityModule.vulnerabilities;
113
+ console.log(`\nšŸ”’ SECURITY VULNERABILITIES (${vulns.length}):`);
114
+ for (const vuln of vulns.slice(0, 5)) {
115
+ console.log(` • ${vuln}`);
116
+ }
117
+ if (vulns.length > 5) {
118
+ console.log(` ... and ${vulns.length - 5} more`);
119
+ }
120
+ }
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ const aupModule = aupCompliance;
123
+ if (aupModule?.violations?.length > 0) {
124
+ const violations = aupModule.violations;
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
+ const critical = violations.filter((v) => v.severity === "CRITICAL");
127
+ console.log(`\nāš–ļø AUP FINDINGS:`);
128
+ console.log(` Total flagged: ${violations.length}`);
129
+ if (critical.length > 0) {
130
+ console.log(` 🚨 CRITICAL violations: ${critical.length}`);
131
+ }
132
+ }
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ const annotationsModule = toolAnnotations;
135
+ if (annotationsModule) {
136
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
+ const funcModule = functionality;
138
+ console.log(`\nšŸ·ļø TOOL ANNOTATIONS:`);
139
+ console.log(` Annotated: ${annotationsModule.annotatedCount || 0}/${funcModule?.workingTools || 0}`);
140
+ if (annotationsModule.missingAnnotationsCount > 0) {
141
+ console.log(` Missing: ${annotationsModule.missingAnnotationsCount}`);
142
+ }
143
+ if (annotationsModule.misalignedAnnotationsCount > 0) {
144
+ console.log(` āš ļø Misalignments: ${annotationsModule.misalignedAnnotationsCount}`);
145
+ }
146
+ }
147
+ if (results.recommendations?.length > 0) {
148
+ console.log("\nšŸ’” RECOMMENDATIONS:");
149
+ for (const rec of results.recommendations.slice(0, 5)) {
150
+ console.log(` • ${rec}`);
151
+ }
152
+ }
153
+ console.log("\n" + "=".repeat(70));
154
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bryan-thompson/inspector-assessment-cli",
3
- "version": "1.26.6",
3
+ "version": "1.27.0",
4
4
  "description": "CLI for the Enhanced MCP Inspector with assessment capabilities",
5
5
  "license": "MIT",
6
6
  "author": "Bryan Thompson <bryan@triepod.ai>",