@bryan-thompson/inspector-assessment-client 1.30.1 → 1.32.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/dist/assets/{OAuthCallback-BbE88qbF.js → OAuthCallback-Cl2ANLYP.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-CfRYq1JG.js → OAuthDebugCallback-Ws62T4Ns.js} +1 -1
- package/dist/assets/{index-cHhcEXbr.css → index-BoUA5OL1.css} +3 -0
- package/dist/assets/{index-CsUB73MT.js → index-DaPIdOcS.js} +3746 -115
- package/dist/index.html +2 -2
- package/lib/lib/assessment/configTypes.d.ts +6 -0
- package/lib/lib/assessment/configTypes.d.ts.map +1 -1
- package/lib/lib/assessment/extendedTypes.d.ts +95 -0
- package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
- package/lib/lib/assessment/resultTypes.d.ts +14 -2
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/assessment/sharedSchemas.d.ts +140 -0
- package/lib/lib/assessment/sharedSchemas.d.ts.map +1 -0
- package/lib/lib/assessment/sharedSchemas.js +113 -0
- package/lib/lib/moduleScoring.d.ts.map +1 -1
- package/lib/lib/moduleScoring.js +5 -0
- package/lib/lib/securityPatterns.d.ts.map +1 -1
- package/lib/lib/securityPatterns.js +2 -2
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +20 -18
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +143 -144
- package/lib/services/assessment/ResponseValidator.d.ts +10 -0
- package/lib/services/assessment/ResponseValidator.d.ts.map +1 -1
- package/lib/services/assessment/ResponseValidator.js +30 -6
- package/lib/services/assessment/config/performanceConfig.d.ts +2 -0
- package/lib/services/assessment/config/performanceConfig.d.ts.map +1 -1
- package/lib/services/assessment/config/performanceConfig.js +5 -33
- package/lib/services/assessment/config/performanceConfigSchemas.d.ts +111 -0
- package/lib/services/assessment/config/performanceConfigSchemas.d.ts.map +1 -0
- package/lib/services/assessment/config/performanceConfigSchemas.js +123 -0
- package/lib/services/assessment/modules/ConformanceAssessor.d.ts +64 -0
- package/lib/services/assessment/modules/ConformanceAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ConformanceAssessor.js +329 -0
- package/lib/services/assessment/modules/ResourceAssessor.d.ts +14 -0
- package/lib/services/assessment/modules/ResourceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ResourceAssessor.js +226 -0
- package/lib/services/assessment/modules/TemporalAssessor.d.ts +14 -0
- package/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/TemporalAssessor.js +29 -1
- package/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts +9 -0
- package/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts.map +1 -1
- package/lib/services/assessment/modules/annotations/AlignmentChecker.js +97 -5
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts +6 -4
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -1
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +58 -0
- package/lib/services/assessment/modules/annotations/index.d.ts +1 -1
- package/lib/services/assessment/modules/annotations/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/annotations/index.js +2 -1
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +3 -3
- package/lib/services/assessment/registry/AssessorDefinitions.d.ts +38 -0
- package/lib/services/assessment/registry/AssessorDefinitions.d.ts.map +1 -0
- package/lib/services/assessment/registry/AssessorDefinitions.js +370 -0
- package/lib/services/assessment/registry/AssessorRegistry.d.ts +124 -0
- package/lib/services/assessment/registry/AssessorRegistry.d.ts.map +1 -0
- package/lib/services/assessment/registry/AssessorRegistry.js +321 -0
- package/lib/services/assessment/registry/estimators.d.ts +93 -0
- package/lib/services/assessment/registry/estimators.d.ts.map +1 -0
- package/lib/services/assessment/registry/estimators.js +176 -0
- package/lib/services/assessment/registry/index.d.ts +13 -0
- package/lib/services/assessment/registry/index.d.ts.map +1 -0
- package/lib/services/assessment/registry/index.js +16 -0
- package/lib/services/assessment/registry/types.d.ts +180 -0
- package/lib/services/assessment/registry/types.d.ts.map +1 -0
- package/lib/services/assessment/registry/types.js +35 -0
- package/lib/services/assessment/responseValidatorSchemas.d.ts +751 -0
- package/lib/services/assessment/responseValidatorSchemas.d.ts.map +1 -0
- package/lib/services/assessment/responseValidatorSchemas.js +244 -0
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod Schemas for Performance Configuration
|
|
3
|
+
*
|
|
4
|
+
* Runtime validation schemas for performance configuration.
|
|
5
|
+
* Replaces manual validatePerformanceConfig() function with declarative schemas.
|
|
6
|
+
*
|
|
7
|
+
* @module assessment/config/performanceConfigSchemas
|
|
8
|
+
* @see performanceConfig.ts for the interface definitions
|
|
9
|
+
* @see sharedSchemas.ts for PERF_CONFIG_RANGES constants
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { PERF_CONFIG_RANGES } from "../../../lib/assessment/sharedSchemas.js";
|
|
13
|
+
export { PERF_CONFIG_RANGES };
|
|
14
|
+
/**
|
|
15
|
+
* Schema for performance configuration fields.
|
|
16
|
+
* All fields are optional since partial configs are merged with defaults.
|
|
17
|
+
*
|
|
18
|
+
* Validation ranges are defined in PERF_CONFIG_RANGES (sharedSchemas.ts).
|
|
19
|
+
*/
|
|
20
|
+
export declare const PerformanceConfigSchema: z.ZodObject<{
|
|
21
|
+
/**
|
|
22
|
+
* Interval in milliseconds between progress batch flushes.
|
|
23
|
+
*/
|
|
24
|
+
batchFlushIntervalMs: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
/**
|
|
26
|
+
* Batch size for functionality assessment progress events.
|
|
27
|
+
*/
|
|
28
|
+
functionalityBatchSize: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
/**
|
|
30
|
+
* Batch size for security assessment progress events.
|
|
31
|
+
*/
|
|
32
|
+
securityBatchSize: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
/**
|
|
34
|
+
* Timeout for individual test scenario execution in milliseconds.
|
|
35
|
+
*/
|
|
36
|
+
testTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
/**
|
|
38
|
+
* Timeout for individual security payload tests in milliseconds.
|
|
39
|
+
*/
|
|
40
|
+
securityTestTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
/**
|
|
42
|
+
* Warning threshold for queue depth monitoring.
|
|
43
|
+
*/
|
|
44
|
+
queueWarningThreshold: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
/**
|
|
46
|
+
* Maximum EventEmitter listeners to prevent Node.js warnings.
|
|
47
|
+
*/
|
|
48
|
+
eventEmitterMaxListeners: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
batchFlushIntervalMs?: number;
|
|
51
|
+
functionalityBatchSize?: number;
|
|
52
|
+
securityBatchSize?: number;
|
|
53
|
+
testTimeoutMs?: number;
|
|
54
|
+
securityTestTimeoutMs?: number;
|
|
55
|
+
queueWarningThreshold?: number;
|
|
56
|
+
eventEmitterMaxListeners?: number;
|
|
57
|
+
}, {
|
|
58
|
+
batchFlushIntervalMs?: number;
|
|
59
|
+
functionalityBatchSize?: number;
|
|
60
|
+
securityBatchSize?: number;
|
|
61
|
+
testTimeoutMs?: number;
|
|
62
|
+
securityTestTimeoutMs?: number;
|
|
63
|
+
queueWarningThreshold?: number;
|
|
64
|
+
eventEmitterMaxListeners?: number;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Type inferred from the schema.
|
|
68
|
+
* Equivalent to Partial<PerformanceConfig> from performanceConfig.ts
|
|
69
|
+
*/
|
|
70
|
+
export type PartialPerformanceConfig = z.infer<typeof PerformanceConfigSchema>;
|
|
71
|
+
/**
|
|
72
|
+
* Validate a partial performance config using Zod.
|
|
73
|
+
* Drop-in replacement for the manual validatePerformanceConfig() function.
|
|
74
|
+
*
|
|
75
|
+
* @param config - Partial config to validate
|
|
76
|
+
* @returns Array of validation error messages (empty if valid)
|
|
77
|
+
*/
|
|
78
|
+
export declare function validatePerformanceConfigWithZod(config: unknown): string[];
|
|
79
|
+
/**
|
|
80
|
+
* Parse and validate a performance config, returning the validated data.
|
|
81
|
+
* Throws ZodError if validation fails.
|
|
82
|
+
*
|
|
83
|
+
* @param config - Config to parse and validate
|
|
84
|
+
* @returns Validated partial config
|
|
85
|
+
* @throws ZodError if validation fails
|
|
86
|
+
*/
|
|
87
|
+
export declare function parsePerformanceConfig(config: unknown): PartialPerformanceConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Safely parse a performance config without throwing.
|
|
90
|
+
*
|
|
91
|
+
* @param config - Config to parse and validate
|
|
92
|
+
* @returns SafeParseResult with success status and data/error
|
|
93
|
+
*/
|
|
94
|
+
export declare function safeParsePerformanceConfig(config: unknown): z.SafeParseReturnType<{
|
|
95
|
+
batchFlushIntervalMs?: number;
|
|
96
|
+
functionalityBatchSize?: number;
|
|
97
|
+
securityBatchSize?: number;
|
|
98
|
+
testTimeoutMs?: number;
|
|
99
|
+
securityTestTimeoutMs?: number;
|
|
100
|
+
queueWarningThreshold?: number;
|
|
101
|
+
eventEmitterMaxListeners?: number;
|
|
102
|
+
}, {
|
|
103
|
+
batchFlushIntervalMs?: number;
|
|
104
|
+
functionalityBatchSize?: number;
|
|
105
|
+
securityBatchSize?: number;
|
|
106
|
+
testTimeoutMs?: number;
|
|
107
|
+
securityTestTimeoutMs?: number;
|
|
108
|
+
queueWarningThreshold?: number;
|
|
109
|
+
eventEmitterMaxListeners?: number;
|
|
110
|
+
}>;
|
|
111
|
+
//# sourceMappingURL=performanceConfigSchemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performanceConfigSchemas.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/config/performanceConfigSchemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAG3E,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;IAClC;;OAEG;;IAcH;;OAEG;;IAcH;;OAEG;;IAcH;;OAEG;;IAcH;;OAEG;;IAcH;;OAEG;;IAcH;;OAEG;;;;;;;;;;;;;;;;;;EAaH,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAW1E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,GACd,wBAAwB,CAE1B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;;;;;GAEzD"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod Schemas for Performance Configuration
|
|
3
|
+
*
|
|
4
|
+
* Runtime validation schemas for performance configuration.
|
|
5
|
+
* Replaces manual validatePerformanceConfig() function with declarative schemas.
|
|
6
|
+
*
|
|
7
|
+
* @module assessment/config/performanceConfigSchemas
|
|
8
|
+
* @see performanceConfig.ts for the interface definitions
|
|
9
|
+
* @see sharedSchemas.ts for PERF_CONFIG_RANGES constants
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
// Import validation range constants from single source of truth
|
|
13
|
+
import { PERF_CONFIG_RANGES } from "../../../lib/assessment/sharedSchemas.js";
|
|
14
|
+
// Re-export for consumers who need the range constants
|
|
15
|
+
export { PERF_CONFIG_RANGES };
|
|
16
|
+
/**
|
|
17
|
+
* Schema for performance configuration fields.
|
|
18
|
+
* All fields are optional since partial configs are merged with defaults.
|
|
19
|
+
*
|
|
20
|
+
* Validation ranges are defined in PERF_CONFIG_RANGES (sharedSchemas.ts).
|
|
21
|
+
*/
|
|
22
|
+
export const PerformanceConfigSchema = z.object({
|
|
23
|
+
/**
|
|
24
|
+
* Interval in milliseconds between progress batch flushes.
|
|
25
|
+
*/
|
|
26
|
+
batchFlushIntervalMs: z
|
|
27
|
+
.number()
|
|
28
|
+
.int("batchFlushIntervalMs must be an integer")
|
|
29
|
+
.min(PERF_CONFIG_RANGES.batchFlushIntervalMs.min, `batchFlushIntervalMs must be >= ${PERF_CONFIG_RANGES.batchFlushIntervalMs.min}`)
|
|
30
|
+
.max(PERF_CONFIG_RANGES.batchFlushIntervalMs.max, `batchFlushIntervalMs must be <= ${PERF_CONFIG_RANGES.batchFlushIntervalMs.max}`)
|
|
31
|
+
.optional(),
|
|
32
|
+
/**
|
|
33
|
+
* Batch size for functionality assessment progress events.
|
|
34
|
+
*/
|
|
35
|
+
functionalityBatchSize: z
|
|
36
|
+
.number()
|
|
37
|
+
.int("functionalityBatchSize must be an integer")
|
|
38
|
+
.min(PERF_CONFIG_RANGES.functionalityBatchSize.min, `functionalityBatchSize must be >= ${PERF_CONFIG_RANGES.functionalityBatchSize.min}`)
|
|
39
|
+
.max(PERF_CONFIG_RANGES.functionalityBatchSize.max, `functionalityBatchSize must be <= ${PERF_CONFIG_RANGES.functionalityBatchSize.max}`)
|
|
40
|
+
.optional(),
|
|
41
|
+
/**
|
|
42
|
+
* Batch size for security assessment progress events.
|
|
43
|
+
*/
|
|
44
|
+
securityBatchSize: z
|
|
45
|
+
.number()
|
|
46
|
+
.int("securityBatchSize must be an integer")
|
|
47
|
+
.min(PERF_CONFIG_RANGES.securityBatchSize.min, `securityBatchSize must be >= ${PERF_CONFIG_RANGES.securityBatchSize.min}`)
|
|
48
|
+
.max(PERF_CONFIG_RANGES.securityBatchSize.max, `securityBatchSize must be <= ${PERF_CONFIG_RANGES.securityBatchSize.max}`)
|
|
49
|
+
.optional(),
|
|
50
|
+
/**
|
|
51
|
+
* Timeout for individual test scenario execution in milliseconds.
|
|
52
|
+
*/
|
|
53
|
+
testTimeoutMs: z
|
|
54
|
+
.number()
|
|
55
|
+
.int("testTimeoutMs must be an integer")
|
|
56
|
+
.min(PERF_CONFIG_RANGES.testTimeoutMs.min, `testTimeoutMs must be >= ${PERF_CONFIG_RANGES.testTimeoutMs.min}`)
|
|
57
|
+
.max(PERF_CONFIG_RANGES.testTimeoutMs.max, `testTimeoutMs must be <= ${PERF_CONFIG_RANGES.testTimeoutMs.max}`)
|
|
58
|
+
.optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Timeout for individual security payload tests in milliseconds.
|
|
61
|
+
*/
|
|
62
|
+
securityTestTimeoutMs: z
|
|
63
|
+
.number()
|
|
64
|
+
.int("securityTestTimeoutMs must be an integer")
|
|
65
|
+
.min(PERF_CONFIG_RANGES.securityTestTimeoutMs.min, `securityTestTimeoutMs must be >= ${PERF_CONFIG_RANGES.securityTestTimeoutMs.min}`)
|
|
66
|
+
.max(PERF_CONFIG_RANGES.securityTestTimeoutMs.max, `securityTestTimeoutMs must be <= ${PERF_CONFIG_RANGES.securityTestTimeoutMs.max}`)
|
|
67
|
+
.optional(),
|
|
68
|
+
/**
|
|
69
|
+
* Warning threshold for queue depth monitoring.
|
|
70
|
+
*/
|
|
71
|
+
queueWarningThreshold: z
|
|
72
|
+
.number()
|
|
73
|
+
.int("queueWarningThreshold must be an integer")
|
|
74
|
+
.min(PERF_CONFIG_RANGES.queueWarningThreshold.min, `queueWarningThreshold must be >= ${PERF_CONFIG_RANGES.queueWarningThreshold.min}`)
|
|
75
|
+
.max(PERF_CONFIG_RANGES.queueWarningThreshold.max, `queueWarningThreshold must be <= ${PERF_CONFIG_RANGES.queueWarningThreshold.max}`)
|
|
76
|
+
.optional(),
|
|
77
|
+
/**
|
|
78
|
+
* Maximum EventEmitter listeners to prevent Node.js warnings.
|
|
79
|
+
*/
|
|
80
|
+
eventEmitterMaxListeners: z
|
|
81
|
+
.number()
|
|
82
|
+
.int("eventEmitterMaxListeners must be an integer")
|
|
83
|
+
.min(PERF_CONFIG_RANGES.eventEmitterMaxListeners.min, `eventEmitterMaxListeners must be >= ${PERF_CONFIG_RANGES.eventEmitterMaxListeners.min}`)
|
|
84
|
+
.max(PERF_CONFIG_RANGES.eventEmitterMaxListeners.max, `eventEmitterMaxListeners must be <= ${PERF_CONFIG_RANGES.eventEmitterMaxListeners.max}`)
|
|
85
|
+
.optional(),
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Validate a partial performance config using Zod.
|
|
89
|
+
* Drop-in replacement for the manual validatePerformanceConfig() function.
|
|
90
|
+
*
|
|
91
|
+
* @param config - Partial config to validate
|
|
92
|
+
* @returns Array of validation error messages (empty if valid)
|
|
93
|
+
*/
|
|
94
|
+
export function validatePerformanceConfigWithZod(config) {
|
|
95
|
+
const result = PerformanceConfigSchema.safeParse(config);
|
|
96
|
+
if (result.success) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
return result.error.errors.map((e) => {
|
|
100
|
+
const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
|
|
101
|
+
return `${path}${e.message}`;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse and validate a performance config, returning the validated data.
|
|
106
|
+
* Throws ZodError if validation fails.
|
|
107
|
+
*
|
|
108
|
+
* @param config - Config to parse and validate
|
|
109
|
+
* @returns Validated partial config
|
|
110
|
+
* @throws ZodError if validation fails
|
|
111
|
+
*/
|
|
112
|
+
export function parsePerformanceConfig(config) {
|
|
113
|
+
return PerformanceConfigSchema.parse(config);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Safely parse a performance config without throwing.
|
|
117
|
+
*
|
|
118
|
+
* @param config - Config to parse and validate
|
|
119
|
+
* @returns SafeParseResult with success status and data/error
|
|
120
|
+
*/
|
|
121
|
+
export function safeParsePerformanceConfig(config) {
|
|
122
|
+
return PerformanceConfigSchema.safeParse(config);
|
|
123
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conformance Assessor Module
|
|
3
|
+
*
|
|
4
|
+
* Integrates official MCP conformance tests from @modelcontextprotocol/conformance.
|
|
5
|
+
* Runs server-side conformance validation against the MCP specification.
|
|
6
|
+
*
|
|
7
|
+
* Requirements:
|
|
8
|
+
* - HTTP/SSE transport (requires serverUrl in config)
|
|
9
|
+
* - Opt-in via --conformance flag or assessmentCategories.conformance = true
|
|
10
|
+
*
|
|
11
|
+
* @module assessment/modules/ConformanceAssessor
|
|
12
|
+
*/
|
|
13
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
14
|
+
import { AssessmentContext } from "../AssessmentOrchestrator.js";
|
|
15
|
+
import type { ConformanceAssessment } from "../../../lib/assessment/extendedTypes.js";
|
|
16
|
+
/**
|
|
17
|
+
* Conformance Assessor
|
|
18
|
+
*
|
|
19
|
+
* Runs official MCP conformance tests against the server.
|
|
20
|
+
* Requires HTTP/SSE transport with serverUrl available.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ConformanceAssessor extends BaseAssessor<ConformanceAssessment> {
|
|
23
|
+
/**
|
|
24
|
+
* Run conformance assessment
|
|
25
|
+
*/
|
|
26
|
+
assess(context: AssessmentContext): Promise<ConformanceAssessment>;
|
|
27
|
+
/**
|
|
28
|
+
* Run a single conformance scenario
|
|
29
|
+
*/
|
|
30
|
+
private runScenario;
|
|
31
|
+
/**
|
|
32
|
+
* Find the checks.json file in the results directory
|
|
33
|
+
*/
|
|
34
|
+
private findChecksFile;
|
|
35
|
+
/**
|
|
36
|
+
* Parse checks.json file from conformance results
|
|
37
|
+
*/
|
|
38
|
+
private parseChecksFile;
|
|
39
|
+
/**
|
|
40
|
+
* Check if the MCP conformance package is available
|
|
41
|
+
*/
|
|
42
|
+
private isConformancePackageAvailable;
|
|
43
|
+
/**
|
|
44
|
+
* Cleanup temporary directory
|
|
45
|
+
*/
|
|
46
|
+
private cleanupTempDir;
|
|
47
|
+
/**
|
|
48
|
+
* Determine overall conformance status
|
|
49
|
+
*/
|
|
50
|
+
private determineConformanceStatus;
|
|
51
|
+
/**
|
|
52
|
+
* Generate human-readable explanation
|
|
53
|
+
*/
|
|
54
|
+
private generateExplanation;
|
|
55
|
+
/**
|
|
56
|
+
* Generate recommendations based on failures
|
|
57
|
+
*/
|
|
58
|
+
private generateRecommendations;
|
|
59
|
+
/**
|
|
60
|
+
* Create a skipped result when conformance tests cannot run
|
|
61
|
+
*/
|
|
62
|
+
private createSkippedResult;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=ConformanceAssessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConformanceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ConformanceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,qBAAqB,EAGtB,MAAM,gCAAgC,CAAC;AAgCxC;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,qBAAqB,CAAC;IAC1E;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAkGxE;;OAEG;YACW,WAAW;IAwEzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAwBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAmBvB;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAarC;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA+BlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA+C/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAmB5B"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conformance Assessor Module
|
|
3
|
+
*
|
|
4
|
+
* Integrates official MCP conformance tests from @modelcontextprotocol/conformance.
|
|
5
|
+
* Runs server-side conformance validation against the MCP specification.
|
|
6
|
+
*
|
|
7
|
+
* Requirements:
|
|
8
|
+
* - HTTP/SSE transport (requires serverUrl in config)
|
|
9
|
+
* - Opt-in via --conformance flag or assessmentCategories.conformance = true
|
|
10
|
+
*
|
|
11
|
+
* @module assessment/modules/ConformanceAssessor
|
|
12
|
+
*/
|
|
13
|
+
import { execFileSync } from "child_process";
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
import * as os from "os";
|
|
17
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
18
|
+
/**
|
|
19
|
+
* Version of the conformance package we're integrating with
|
|
20
|
+
*/
|
|
21
|
+
const CONFORMANCE_PACKAGE_VERSION = "0.1.9";
|
|
22
|
+
/**
|
|
23
|
+
* Available server scenarios from the conformance package
|
|
24
|
+
* Updated for @modelcontextprotocol/conformance v0.1.9+
|
|
25
|
+
*/
|
|
26
|
+
const SERVER_SCENARIOS = [
|
|
27
|
+
"server-initialize",
|
|
28
|
+
"tools-list",
|
|
29
|
+
"tools-call-simple-text",
|
|
30
|
+
"resources-list",
|
|
31
|
+
"resources-read-text",
|
|
32
|
+
"prompts-list",
|
|
33
|
+
"prompts-get-simple",
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Conformance Assessor
|
|
37
|
+
*
|
|
38
|
+
* Runs official MCP conformance tests against the server.
|
|
39
|
+
* Requires HTTP/SSE transport with serverUrl available.
|
|
40
|
+
*/
|
|
41
|
+
export class ConformanceAssessor extends BaseAssessor {
|
|
42
|
+
/**
|
|
43
|
+
* Run conformance assessment
|
|
44
|
+
*/
|
|
45
|
+
async assess(context) {
|
|
46
|
+
const serverUrl = context.config.serverUrl;
|
|
47
|
+
// Check if serverUrl is available (required for conformance tests)
|
|
48
|
+
if (!serverUrl) {
|
|
49
|
+
this.logger.info("Conformance tests skipped: serverUrl not available (requires HTTP/SSE transport)");
|
|
50
|
+
return this.createSkippedResult("Server URL not available. Conformance tests require HTTP or SSE transport.");
|
|
51
|
+
}
|
|
52
|
+
// Check if conformance package is available
|
|
53
|
+
if (!this.isConformancePackageAvailable()) {
|
|
54
|
+
this.logger.warn("MCP conformance package not available. Install with: npm install -g @modelcontextprotocol/conformance");
|
|
55
|
+
return this.createSkippedResult("MCP conformance package not installed. Run: npm install -g @modelcontextprotocol/conformance");
|
|
56
|
+
}
|
|
57
|
+
this.logger.info(`Running conformance tests against: ${serverUrl}`);
|
|
58
|
+
const scenarios = [];
|
|
59
|
+
const allChecks = [];
|
|
60
|
+
let passedScenarios = 0;
|
|
61
|
+
let totalScenarios = 0;
|
|
62
|
+
// Run each server scenario
|
|
63
|
+
for (const scenario of SERVER_SCENARIOS) {
|
|
64
|
+
totalScenarios++;
|
|
65
|
+
try {
|
|
66
|
+
const scenarioResult = await this.runScenario(serverUrl, scenario);
|
|
67
|
+
scenarios.push(scenarioResult);
|
|
68
|
+
// Count scenario pass/fail (not individual checks)
|
|
69
|
+
if (scenarioResult.status === "pass") {
|
|
70
|
+
passedScenarios++;
|
|
71
|
+
}
|
|
72
|
+
// Aggregate any detailed checks for reporting
|
|
73
|
+
for (const check of scenarioResult.checks) {
|
|
74
|
+
allChecks.push(check);
|
|
75
|
+
}
|
|
76
|
+
this.testCount++;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
// Log error but continue with other scenarios
|
|
80
|
+
this.logger.warn(`Scenario ${scenario} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
scenarios.push({
|
|
82
|
+
name: scenario,
|
|
83
|
+
status: "skip",
|
|
84
|
+
checks: [],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Calculate compliance score based on scenarios
|
|
89
|
+
const complianceScore = totalScenarios > 0
|
|
90
|
+
? Math.round((passedScenarios / totalScenarios) * 100)
|
|
91
|
+
: 0;
|
|
92
|
+
// Determine overall status
|
|
93
|
+
const status = this.determineConformanceStatus(passedScenarios, totalScenarios, scenarios);
|
|
94
|
+
// Generate explanation and recommendations
|
|
95
|
+
const explanation = this.generateExplanation(status, passedScenarios, totalScenarios, scenarios);
|
|
96
|
+
const recommendations = this.generateRecommendations(scenarios, allChecks);
|
|
97
|
+
return {
|
|
98
|
+
status,
|
|
99
|
+
conformanceVersion: CONFORMANCE_PACKAGE_VERSION,
|
|
100
|
+
protocolVersion: context.config.mcpProtocolVersion || "2025-06",
|
|
101
|
+
scenarios,
|
|
102
|
+
officialChecks: allChecks,
|
|
103
|
+
passedChecks: passedScenarios,
|
|
104
|
+
totalChecks: totalScenarios,
|
|
105
|
+
complianceScore,
|
|
106
|
+
explanation,
|
|
107
|
+
recommendations,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Run a single conformance scenario
|
|
112
|
+
*/
|
|
113
|
+
async runScenario(serverUrl, scenario) {
|
|
114
|
+
const startTime = Date.now();
|
|
115
|
+
try {
|
|
116
|
+
// Create temp directory for results
|
|
117
|
+
const resultsDir = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-conformance-"));
|
|
118
|
+
// Run conformance CLI (results are written to checks.json, not stdout)
|
|
119
|
+
execFileSync("npx", [
|
|
120
|
+
"@modelcontextprotocol/conformance",
|
|
121
|
+
"server",
|
|
122
|
+
"--url",
|
|
123
|
+
serverUrl,
|
|
124
|
+
"--scenario",
|
|
125
|
+
scenario,
|
|
126
|
+
], {
|
|
127
|
+
encoding: "utf-8",
|
|
128
|
+
timeout: 60000, // 60 second timeout per scenario
|
|
129
|
+
cwd: resultsDir,
|
|
130
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
131
|
+
});
|
|
132
|
+
// Parse results from checks.json
|
|
133
|
+
const checksPath = this.findChecksFile(resultsDir, scenario);
|
|
134
|
+
const checks = checksPath ? this.parseChecksFile(checksPath) : [];
|
|
135
|
+
// Determine scenario status
|
|
136
|
+
const hasFailures = checks.some((c) => c.status === "fail");
|
|
137
|
+
const status = hasFailures ? "fail" : "pass";
|
|
138
|
+
// Cleanup temp directory
|
|
139
|
+
this.cleanupTempDir(resultsDir);
|
|
140
|
+
return {
|
|
141
|
+
name: scenario,
|
|
142
|
+
status,
|
|
143
|
+
checks,
|
|
144
|
+
executionTime: Date.now() - startTime,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
this.logger.debug(`Scenario ${scenario} execution error: ${error instanceof Error ? error.message : String(error)}`);
|
|
149
|
+
// Return skip status for failed scenarios
|
|
150
|
+
return {
|
|
151
|
+
name: scenario,
|
|
152
|
+
status: "skip",
|
|
153
|
+
checks: [
|
|
154
|
+
{
|
|
155
|
+
name: `${scenario}-execution`,
|
|
156
|
+
status: "fail",
|
|
157
|
+
message: error instanceof Error
|
|
158
|
+
? error.message
|
|
159
|
+
: "Scenario execution failed",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
executionTime: Date.now() - startTime,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Find the checks.json file in the results directory
|
|
168
|
+
*/
|
|
169
|
+
findChecksFile(resultsDir, scenario) {
|
|
170
|
+
// Look for results in timestamped subdirectory
|
|
171
|
+
try {
|
|
172
|
+
const entries = fs.readdirSync(resultsDir);
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
if (entry.startsWith(`server-${scenario}`) ||
|
|
175
|
+
entry.startsWith(scenario)) {
|
|
176
|
+
const checksPath = path.join(resultsDir, entry, "checks.json");
|
|
177
|
+
if (fs.existsSync(checksPath)) {
|
|
178
|
+
return checksPath;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Directory might not exist
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Parse checks.json file from conformance results
|
|
190
|
+
*/
|
|
191
|
+
parseChecksFile(checksPath) {
|
|
192
|
+
try {
|
|
193
|
+
const content = fs.readFileSync(checksPath, "utf-8");
|
|
194
|
+
const results = JSON.parse(content);
|
|
195
|
+
return results.map((r) => ({
|
|
196
|
+
name: r.name,
|
|
197
|
+
status: r.status === "pass" ? "pass" : "fail",
|
|
198
|
+
message: r.message || "",
|
|
199
|
+
timestamp: r.timestamp,
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
this.logger.debug(`Failed to parse checks.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if the MCP conformance package is available
|
|
209
|
+
*/
|
|
210
|
+
isConformancePackageAvailable() {
|
|
211
|
+
try {
|
|
212
|
+
execFileSync("npx", ["@modelcontextprotocol/conformance", "--version"], {
|
|
213
|
+
timeout: 30000,
|
|
214
|
+
stdio: "pipe",
|
|
215
|
+
encoding: "utf-8",
|
|
216
|
+
});
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Cleanup temporary directory
|
|
225
|
+
*/
|
|
226
|
+
cleanupTempDir(dirPath) {
|
|
227
|
+
try {
|
|
228
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Ignore cleanup errors
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Determine overall conformance status
|
|
236
|
+
*/
|
|
237
|
+
determineConformanceStatus(passed, total, scenarios) {
|
|
238
|
+
// If no checks ran, need more info
|
|
239
|
+
if (total === 0) {
|
|
240
|
+
return "NEED_MORE_INFO";
|
|
241
|
+
}
|
|
242
|
+
// Check if any critical scenarios failed
|
|
243
|
+
const criticalScenarios = ["server-initialize", "tools-list"];
|
|
244
|
+
const criticalFailures = scenarios.filter((s) => criticalScenarios.includes(s.name) && s.status === "fail");
|
|
245
|
+
if (criticalFailures.length > 0) {
|
|
246
|
+
return "FAIL";
|
|
247
|
+
}
|
|
248
|
+
// Use pass rate for status
|
|
249
|
+
const passRate = passed / total;
|
|
250
|
+
if (passRate >= 0.9) {
|
|
251
|
+
return "PASS";
|
|
252
|
+
}
|
|
253
|
+
if (passRate >= 0.7) {
|
|
254
|
+
return "NEED_MORE_INFO";
|
|
255
|
+
}
|
|
256
|
+
return "FAIL";
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Generate human-readable explanation
|
|
260
|
+
*/
|
|
261
|
+
generateExplanation(status, passed, total, scenarios) {
|
|
262
|
+
const passRate = total > 0 ? Math.round((passed / total) * 100) : 0;
|
|
263
|
+
if (status === "PASS") {
|
|
264
|
+
return `Server passes ${passRate}% of official MCP conformance checks (${passed}/${total}). The implementation correctly follows the MCP protocol specification.`;
|
|
265
|
+
}
|
|
266
|
+
if (status === "NEED_MORE_INFO") {
|
|
267
|
+
const skipped = scenarios.filter((s) => s.status === "skip").length;
|
|
268
|
+
if (skipped > 0) {
|
|
269
|
+
return `Conformance testing partially completed. ${skipped} scenario(s) were skipped. ${passed}/${total} checks passed (${passRate}%).`;
|
|
270
|
+
}
|
|
271
|
+
return `Server passes ${passRate}% of conformance checks (${passed}/${total}). Some non-critical checks failed; review recommended.`;
|
|
272
|
+
}
|
|
273
|
+
// FAIL
|
|
274
|
+
const failures = scenarios.filter((s) => s.status === "fail");
|
|
275
|
+
return `Server fails conformance testing. ${failures.length} scenario(s) failed. Only ${passRate}% of checks passed (${passed}/${total}).`;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Generate recommendations based on failures
|
|
279
|
+
*/
|
|
280
|
+
generateRecommendations(scenarios, checks) {
|
|
281
|
+
const recommendations = [];
|
|
282
|
+
// Check for initialization failures
|
|
283
|
+
const initScenario = scenarios.find((s) => s.name === "server-initialize");
|
|
284
|
+
if (initScenario?.status === "fail") {
|
|
285
|
+
recommendations.push("Fix initialization handshake issues - ensure server responds correctly to initialize request with valid serverInfo and capabilities.");
|
|
286
|
+
}
|
|
287
|
+
// Check for tools-list failures
|
|
288
|
+
const toolsListScenario = scenarios.find((s) => s.name === "tools-list");
|
|
289
|
+
if (toolsListScenario?.status === "fail") {
|
|
290
|
+
recommendations.push("Review tools/list implementation - ensure all tools have valid names, descriptions, and input schemas.");
|
|
291
|
+
}
|
|
292
|
+
// Check for skipped scenarios
|
|
293
|
+
const skipped = scenarios.filter((s) => s.status === "skip");
|
|
294
|
+
if (skipped.length > 0) {
|
|
295
|
+
recommendations.push(`Run conformance tests again to complete ${skipped.length} skipped scenario(s): ${skipped.map((s) => s.name).join(", ")}.`);
|
|
296
|
+
}
|
|
297
|
+
// Generic recommendations based on check failures
|
|
298
|
+
const failedChecks = checks.filter((c) => c.status === "fail");
|
|
299
|
+
if (failedChecks.length > 0 && recommendations.length < 3) {
|
|
300
|
+
recommendations.push("Review MCP specification at modelcontextprotocol.io for protocol compliance requirements.");
|
|
301
|
+
}
|
|
302
|
+
if (recommendations.length === 0) {
|
|
303
|
+
recommendations.push("Consider running full conformance suite periodically to catch regressions.");
|
|
304
|
+
}
|
|
305
|
+
return recommendations;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Create a skipped result when conformance tests cannot run
|
|
309
|
+
*/
|
|
310
|
+
createSkippedResult(reason) {
|
|
311
|
+
return {
|
|
312
|
+
status: "NEED_MORE_INFO",
|
|
313
|
+
conformanceVersion: CONFORMANCE_PACKAGE_VERSION,
|
|
314
|
+
protocolVersion: this.config.mcpProtocolVersion || "2025-06",
|
|
315
|
+
scenarios: [],
|
|
316
|
+
officialChecks: [],
|
|
317
|
+
passedChecks: 0,
|
|
318
|
+
totalChecks: 0,
|
|
319
|
+
complianceScore: 0,
|
|
320
|
+
explanation: `Conformance testing skipped: ${reason}`,
|
|
321
|
+
recommendations: [
|
|
322
|
+
"Use HTTP or SSE transport to enable conformance testing.",
|
|
323
|
+
"Configure serverUrl in assessment configuration for STDIO servers.",
|
|
324
|
+
],
|
|
325
|
+
skipped: true,
|
|
326
|
+
skipReason: reason,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -28,6 +28,20 @@ export declare class ResourceAssessor extends BaseAssessor {
|
|
|
28
28
|
*/
|
|
29
29
|
private inferDataClassification;
|
|
30
30
|
private testResourceTemplate;
|
|
31
|
+
/**
|
|
32
|
+
* Issue #119, Challenge #14: Test URI injection vulnerabilities in resource templates
|
|
33
|
+
* Injects malicious payloads into URI parameters and checks for sensitive content leakage
|
|
34
|
+
*/
|
|
35
|
+
private testParameterizedUriInjection;
|
|
36
|
+
/**
|
|
37
|
+
* Issue #119, Challenge #14: Probe for hidden/undeclared resources
|
|
38
|
+
* Tests common hidden resource patterns to find accessible but undeclared resources
|
|
39
|
+
*/
|
|
40
|
+
private testHiddenResourceDiscovery;
|
|
41
|
+
/**
|
|
42
|
+
* Helper: Probe a single hidden resource URI
|
|
43
|
+
*/
|
|
44
|
+
private probeHiddenResource;
|
|
31
45
|
private isValidUri;
|
|
32
46
|
private isValidUriTemplate;
|
|
33
47
|
private isSensitiveUri;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ResourceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ResourceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ResourceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAuQ9D,qBAAa,gBAAiB,SAAQ,YAAY;IAC1C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8FrE,OAAO,CAAC,yBAAyB;YAiBnB,YAAY;IAoG1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAY/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;YAsBjB,oBAAoB;IAkGlC;;;OAGG;YACW,6BAA6B;IA4F3C;;;OAGG;YACW,2BAA2B;IAgEzC;;OAEG;YACW,mBAAmB;IAmEjC,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,wBAAwB;IAIhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,mBAAmB;IAoC3B,OAAO,CAAC,uBAAuB;CA+DhC"}
|