@bryan-thompson/inspector-assessment-client 1.5.0 → 1.7.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-DGVqLct6.js → OAuthCallback-Xo9zS7pv.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-DHflRQgp.js → OAuthDebugCallback-CaIey8K_.js} +1 -1
- package/dist/assets/{index-Btl7vuTl.js → index-nCPw6E-c.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/lib/assessmentTypes.d.ts +670 -0
- package/lib/lib/assessmentTypes.d.ts.map +1 -0
- package/lib/lib/assessmentTypes.js +220 -0
- package/lib/lib/aupPatterns.d.ts +63 -0
- package/lib/lib/aupPatterns.d.ts.map +1 -0
- package/lib/lib/aupPatterns.js +344 -0
- package/lib/lib/prohibitedLibraries.d.ts +76 -0
- package/lib/lib/prohibitedLibraries.d.ts.map +1 -0
- package/lib/lib/prohibitedLibraries.js +364 -0
- package/lib/lib/securityPatterns.d.ts +64 -0
- package/lib/lib/securityPatterns.d.ts.map +1 -0
- package/lib/lib/securityPatterns.js +453 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +88 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -0
- package/lib/services/assessment/AssessmentOrchestrator.js +418 -0
- package/lib/services/assessment/ResponseValidator.d.ts +69 -0
- package/lib/services/assessment/ResponseValidator.d.ts.map +1 -0
- package/lib/services/assessment/ResponseValidator.js +1038 -0
- package/lib/services/assessment/TestDataGenerator.d.ts +86 -0
- package/lib/services/assessment/TestDataGenerator.d.ts.map +1 -0
- package/lib/services/assessment/TestDataGenerator.js +669 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts +91 -0
- package/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -0
- package/lib/services/assessment/TestScenarioEngine.js +505 -0
- package/lib/services/assessment/ToolClassifier.d.ts +61 -0
- package/lib/services/assessment/ToolClassifier.d.ts.map +1 -0
- package/lib/services/assessment/ToolClassifier.js +349 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts +160 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -0
- package/lib/services/assessment/lib/claudeCodeBridge.js +357 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.d.ts +100 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/AUPComplianceAssessor.js +474 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts +71 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/BaseAssessor.js +171 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts +45 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/DocumentationAssessor.js +355 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts +20 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/FunctionalityAssessor.js +253 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +70 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +508 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +70 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +430 -0
- package/lib/services/assessment/modules/PortabilityAssessor.d.ts +43 -0
- package/lib/services/assessment/modules/PortabilityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/PortabilityAssessor.js +347 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts +41 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ProhibitedLibrariesAssessor.js +256 -0
- package/lib/services/assessment/modules/SecurityAssessor.d.ts +176 -0
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/SecurityAssessor.js +1333 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +96 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +593 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts +21 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/UsabilityAssessor.js +241 -0
- package/lib/services/assessment/modules/index.d.ts +33 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/index.js +35 -0
- package/package.json +15 -3
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Annotation Assessor
|
|
3
|
+
* Verifies MCP tools have proper annotations per Policy #17
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - readOnlyHint presence and accuracy
|
|
7
|
+
* - destructiveHint presence and accuracy
|
|
8
|
+
* - Tool behavior inference from name patterns
|
|
9
|
+
* - Annotation misalignment detection
|
|
10
|
+
*
|
|
11
|
+
* Reference: Anthropic MCP Directory Policy #17
|
|
12
|
+
*/
|
|
13
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
14
|
+
import { AssessmentContext } from "../AssessmentOrchestrator.js";
|
|
15
|
+
import type { ToolAnnotationAssessment, ToolAnnotationResult } from "../../../lib/assessmentTypes.js";
|
|
16
|
+
import type { ClaudeCodeBridge } from "../lib/claudeCodeBridge.js";
|
|
17
|
+
/**
|
|
18
|
+
* Enhanced tool annotation result with Claude inference
|
|
19
|
+
*/
|
|
20
|
+
export interface EnhancedToolAnnotationResult extends ToolAnnotationResult {
|
|
21
|
+
claudeInference?: {
|
|
22
|
+
expectedReadOnly: boolean;
|
|
23
|
+
expectedDestructive: boolean;
|
|
24
|
+
confidence: number;
|
|
25
|
+
reasoning: string;
|
|
26
|
+
suggestedAnnotations: {
|
|
27
|
+
readOnlyHint?: boolean;
|
|
28
|
+
destructiveHint?: boolean;
|
|
29
|
+
idempotentHint?: boolean;
|
|
30
|
+
};
|
|
31
|
+
misalignmentDetected: boolean;
|
|
32
|
+
misalignmentDetails?: string;
|
|
33
|
+
source: "claude-inferred" | "pattern-based";
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Enhanced assessment with Claude integration
|
|
38
|
+
*/
|
|
39
|
+
export interface EnhancedToolAnnotationAssessment extends ToolAnnotationAssessment {
|
|
40
|
+
toolResults: EnhancedToolAnnotationResult[];
|
|
41
|
+
claudeEnhanced: boolean;
|
|
42
|
+
highConfidenceMisalignments: EnhancedToolAnnotationResult[];
|
|
43
|
+
}
|
|
44
|
+
export declare class ToolAnnotationAssessor extends BaseAssessor {
|
|
45
|
+
private claudeBridge?;
|
|
46
|
+
/**
|
|
47
|
+
* Set Claude Code Bridge for enhanced behavior inference
|
|
48
|
+
*/
|
|
49
|
+
setClaudeBridge(bridge: ClaudeCodeBridge): void;
|
|
50
|
+
/**
|
|
51
|
+
* Check if Claude enhancement is available
|
|
52
|
+
*/
|
|
53
|
+
isClaudeEnabled(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Run tool annotation assessment
|
|
56
|
+
*/
|
|
57
|
+
assess(context: AssessmentContext): Promise<ToolAnnotationAssessment | EnhancedToolAnnotationAssessment>;
|
|
58
|
+
/**
|
|
59
|
+
* Enhance tool assessment with Claude inference
|
|
60
|
+
*/
|
|
61
|
+
private enhanceWithClaudeInference;
|
|
62
|
+
/**
|
|
63
|
+
* Generate enhanced explanation with Claude analysis
|
|
64
|
+
*/
|
|
65
|
+
private generateEnhancedExplanation;
|
|
66
|
+
/**
|
|
67
|
+
* Generate enhanced recommendations with Claude analysis
|
|
68
|
+
*/
|
|
69
|
+
private generateEnhancedRecommendations;
|
|
70
|
+
/**
|
|
71
|
+
* Assess a single tool's annotations
|
|
72
|
+
*/
|
|
73
|
+
private assessTool;
|
|
74
|
+
/**
|
|
75
|
+
* Extract annotations from a tool
|
|
76
|
+
* MCP SDK may have annotations in different locations
|
|
77
|
+
*/
|
|
78
|
+
private extractAnnotations;
|
|
79
|
+
/**
|
|
80
|
+
* Infer expected behavior from tool name and description
|
|
81
|
+
*/
|
|
82
|
+
private inferBehavior;
|
|
83
|
+
/**
|
|
84
|
+
* Determine overall status
|
|
85
|
+
*/
|
|
86
|
+
private determineAnnotationStatus;
|
|
87
|
+
/**
|
|
88
|
+
* Generate explanation
|
|
89
|
+
*/
|
|
90
|
+
private generateExplanation;
|
|
91
|
+
/**
|
|
92
|
+
* Generate recommendations
|
|
93
|
+
*/
|
|
94
|
+
private generateRecommendations;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=ToolAnnotationAssessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolAnnotationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ToolAnnotationAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EAErB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;IACxE,eAAe,CAAC,EAAE;QAChB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,mBAAmB,EAAE,OAAO,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,oBAAoB,EAAE;YACpB,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,eAAe,CAAC,EAAE,OAAO,CAAC;YAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;SAC1B,CAAC;QACF,oBAAoB,EAAE,OAAO,CAAC;QAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAAC;KAC7C,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gCAAiC,SAAQ,wBAAwB;IAChF,WAAW,EAAE,4BAA4B,EAAE,CAAC;IAC5C,cAAc,EAAE,OAAO,CAAC;IACxB,2BAA2B,EAAE,4BAA4B,EAAE,CAAC;CAC7D;AAwED,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,OAAO,CAAC,YAAY,CAAC,CAAmB;IAExC;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAK/C;;OAEG;IACH,eAAe,IAAI,OAAO;IAO1B;;OAEG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,wBAAwB,GAAG,gCAAgC,CAAC;IAqIvE;;OAEG;YACW,0BAA0B;IA+IxC;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAiCnC;;OAEG;IACH,OAAO,CAAC,+BAA+B;IAoFvC;;OAEG;IACH,OAAO,CAAC,UAAU;IAqElB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAyC1B;;OAEG;IACH,OAAO,CAAC,aAAa;IA0ErB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAsCjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmC3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA2ChC"}
|
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Annotation Assessor
|
|
3
|
+
* Verifies MCP tools have proper annotations per Policy #17
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - readOnlyHint presence and accuracy
|
|
7
|
+
* - destructiveHint presence and accuracy
|
|
8
|
+
* - Tool behavior inference from name patterns
|
|
9
|
+
* - Annotation misalignment detection
|
|
10
|
+
*
|
|
11
|
+
* Reference: Anthropic MCP Directory Policy #17
|
|
12
|
+
*/
|
|
13
|
+
import { BaseAssessor } from "./BaseAssessor.js";
|
|
14
|
+
/**
|
|
15
|
+
* Patterns for inferring expected tool behavior from name
|
|
16
|
+
*/
|
|
17
|
+
const READ_ONLY_PATTERNS = [
|
|
18
|
+
/^get[_-]?/i,
|
|
19
|
+
/^list[_-]?/i,
|
|
20
|
+
/^fetch[_-]?/i,
|
|
21
|
+
/^read[_-]?/i,
|
|
22
|
+
/^query[_-]?/i,
|
|
23
|
+
/^search[_-]?/i,
|
|
24
|
+
/^find[_-]?/i,
|
|
25
|
+
/^show[_-]?/i,
|
|
26
|
+
/^view[_-]?/i,
|
|
27
|
+
/^describe[_-]?/i,
|
|
28
|
+
/^check[_-]?/i,
|
|
29
|
+
/^verify[_-]?/i,
|
|
30
|
+
/^validate[_-]?/i,
|
|
31
|
+
/^count[_-]?/i,
|
|
32
|
+
/^status[_-]?/i,
|
|
33
|
+
/^info[_-]?/i,
|
|
34
|
+
/^lookup[_-]?/i,
|
|
35
|
+
/^browse[_-]?/i,
|
|
36
|
+
/^preview[_-]?/i,
|
|
37
|
+
/^download[_-]?/i, // Downloads but doesn't modify server state
|
|
38
|
+
];
|
|
39
|
+
const DESTRUCTIVE_PATTERNS = [
|
|
40
|
+
/^delete[_-]?/i,
|
|
41
|
+
/^remove[_-]?/i,
|
|
42
|
+
/^destroy[_-]?/i,
|
|
43
|
+
/^drop[_-]?/i,
|
|
44
|
+
/^purge[_-]?/i,
|
|
45
|
+
/^clear[_-]?/i,
|
|
46
|
+
/^wipe[_-]?/i,
|
|
47
|
+
/^erase[_-]?/i,
|
|
48
|
+
/^reset[_-]?/i,
|
|
49
|
+
/^truncate[_-]?/i,
|
|
50
|
+
/^revoke[_-]?/i,
|
|
51
|
+
/^terminate[_-]?/i,
|
|
52
|
+
/^cancel[_-]?/i,
|
|
53
|
+
/^kill[_-]?/i,
|
|
54
|
+
/^force[_-]?/i,
|
|
55
|
+
];
|
|
56
|
+
const WRITE_PATTERNS = [
|
|
57
|
+
/^create[_-]?/i,
|
|
58
|
+
/^add[_-]?/i,
|
|
59
|
+
/^insert[_-]?/i,
|
|
60
|
+
/^update[_-]?/i,
|
|
61
|
+
/^modify[_-]?/i,
|
|
62
|
+
/^edit[_-]?/i,
|
|
63
|
+
/^change[_-]?/i,
|
|
64
|
+
/^set[_-]?/i,
|
|
65
|
+
/^put[_-]?/i,
|
|
66
|
+
/^patch[_-]?/i,
|
|
67
|
+
/^post[_-]?/i,
|
|
68
|
+
/^write[_-]?/i,
|
|
69
|
+
/^save[_-]?/i,
|
|
70
|
+
/^upload[_-]?/i,
|
|
71
|
+
/^send[_-]?/i,
|
|
72
|
+
/^submit[_-]?/i,
|
|
73
|
+
/^publish[_-]?/i,
|
|
74
|
+
/^enable[_-]?/i,
|
|
75
|
+
/^disable[_-]?/i,
|
|
76
|
+
/^start[_-]?/i,
|
|
77
|
+
/^stop[_-]?/i,
|
|
78
|
+
/^run[_-]?/i,
|
|
79
|
+
/^execute[_-]?/i,
|
|
80
|
+
];
|
|
81
|
+
export class ToolAnnotationAssessor extends BaseAssessor {
|
|
82
|
+
claudeBridge;
|
|
83
|
+
/**
|
|
84
|
+
* Set Claude Code Bridge for enhanced behavior inference
|
|
85
|
+
*/
|
|
86
|
+
setClaudeBridge(bridge) {
|
|
87
|
+
this.claudeBridge = bridge;
|
|
88
|
+
this.log("Claude Code Bridge enabled for behavior inference");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if Claude enhancement is available
|
|
92
|
+
*/
|
|
93
|
+
isClaudeEnabled() {
|
|
94
|
+
return (this.claudeBridge !== undefined &&
|
|
95
|
+
this.claudeBridge.isFeatureEnabled("annotationInference"));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Run tool annotation assessment
|
|
99
|
+
*/
|
|
100
|
+
async assess(context) {
|
|
101
|
+
this.log("Starting tool annotation assessment");
|
|
102
|
+
this.testCount = 0;
|
|
103
|
+
const toolResults = [];
|
|
104
|
+
let annotatedCount = 0;
|
|
105
|
+
let missingAnnotationsCount = 0;
|
|
106
|
+
let misalignedAnnotationsCount = 0;
|
|
107
|
+
const useClaudeInference = this.isClaudeEnabled();
|
|
108
|
+
if (useClaudeInference) {
|
|
109
|
+
this.log("Claude Code integration enabled - using semantic behavior inference");
|
|
110
|
+
}
|
|
111
|
+
for (const tool of context.tools) {
|
|
112
|
+
this.testCount++;
|
|
113
|
+
const result = this.assessTool(tool);
|
|
114
|
+
// Enhance with Claude inference if available
|
|
115
|
+
if (useClaudeInference) {
|
|
116
|
+
const enhancedResult = await this.enhanceWithClaudeInference(tool, result);
|
|
117
|
+
toolResults.push(enhancedResult);
|
|
118
|
+
// Count based on Claude analysis if high confidence
|
|
119
|
+
if (enhancedResult.claudeInference &&
|
|
120
|
+
enhancedResult.claudeInference.confidence >= 70 &&
|
|
121
|
+
enhancedResult.claudeInference.misalignmentDetected) {
|
|
122
|
+
misalignedAnnotationsCount++;
|
|
123
|
+
}
|
|
124
|
+
else if (result.issues.some((i) => i.includes("misaligned"))) {
|
|
125
|
+
misalignedAnnotationsCount++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Standard pattern-based result
|
|
130
|
+
const inferredBehavior = result.inferredBehavior ?? {
|
|
131
|
+
expectedReadOnly: false,
|
|
132
|
+
expectedDestructive: false,
|
|
133
|
+
reason: "No behavior inference available",
|
|
134
|
+
};
|
|
135
|
+
toolResults.push({
|
|
136
|
+
...result,
|
|
137
|
+
claudeInference: {
|
|
138
|
+
expectedReadOnly: inferredBehavior.expectedReadOnly,
|
|
139
|
+
expectedDestructive: inferredBehavior.expectedDestructive,
|
|
140
|
+
confidence: 50, // Lower confidence for pattern-based
|
|
141
|
+
reasoning: inferredBehavior.reason,
|
|
142
|
+
suggestedAnnotations: {
|
|
143
|
+
readOnlyHint: inferredBehavior.expectedReadOnly,
|
|
144
|
+
destructiveHint: inferredBehavior.expectedDestructive,
|
|
145
|
+
},
|
|
146
|
+
misalignmentDetected: result.issues.some((i) => i.includes("misaligned")),
|
|
147
|
+
source: "pattern-based",
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
if (result.issues.some((i) => i.includes("misaligned"))) {
|
|
151
|
+
misalignedAnnotationsCount++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (toolResults[toolResults.length - 1].hasAnnotations) {
|
|
155
|
+
annotatedCount++;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
missingAnnotationsCount++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const status = this.determineAnnotationStatus(toolResults, context.tools.length);
|
|
162
|
+
const explanation = this.generateExplanation(annotatedCount, missingAnnotationsCount, misalignedAnnotationsCount, context.tools.length);
|
|
163
|
+
const recommendations = this.generateRecommendations(toolResults);
|
|
164
|
+
this.log(`Assessment complete: ${annotatedCount}/${context.tools.length} tools annotated, ${misalignedAnnotationsCount} misaligned`);
|
|
165
|
+
// Return enhanced assessment if Claude was used
|
|
166
|
+
if (useClaudeInference) {
|
|
167
|
+
const highConfidenceMisalignments = toolResults.filter((r) => r.claudeInference &&
|
|
168
|
+
r.claudeInference.confidence >= 70 &&
|
|
169
|
+
r.claudeInference.misalignmentDetected);
|
|
170
|
+
this.log(`Claude inference found ${highConfidenceMisalignments.length} high-confidence misalignments`);
|
|
171
|
+
return {
|
|
172
|
+
toolResults,
|
|
173
|
+
annotatedCount,
|
|
174
|
+
missingAnnotationsCount,
|
|
175
|
+
misalignedAnnotationsCount,
|
|
176
|
+
status,
|
|
177
|
+
explanation: this.generateEnhancedExplanation(annotatedCount, missingAnnotationsCount, highConfidenceMisalignments.length, context.tools.length),
|
|
178
|
+
recommendations: this.generateEnhancedRecommendations(toolResults),
|
|
179
|
+
claudeEnhanced: true,
|
|
180
|
+
highConfidenceMisalignments,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
toolResults,
|
|
185
|
+
annotatedCount,
|
|
186
|
+
missingAnnotationsCount,
|
|
187
|
+
misalignedAnnotationsCount,
|
|
188
|
+
status,
|
|
189
|
+
explanation,
|
|
190
|
+
recommendations,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Enhance tool assessment with Claude inference
|
|
195
|
+
*/
|
|
196
|
+
async enhanceWithClaudeInference(tool, baseResult) {
|
|
197
|
+
const inferredBehavior = baseResult.inferredBehavior ?? {
|
|
198
|
+
expectedReadOnly: false,
|
|
199
|
+
expectedDestructive: false,
|
|
200
|
+
reason: "No behavior inference available",
|
|
201
|
+
};
|
|
202
|
+
if (!this.claudeBridge) {
|
|
203
|
+
return {
|
|
204
|
+
...baseResult,
|
|
205
|
+
claudeInference: {
|
|
206
|
+
expectedReadOnly: inferredBehavior.expectedReadOnly,
|
|
207
|
+
expectedDestructive: inferredBehavior.expectedDestructive,
|
|
208
|
+
confidence: 50,
|
|
209
|
+
reasoning: inferredBehavior.reason,
|
|
210
|
+
suggestedAnnotations: {
|
|
211
|
+
readOnlyHint: inferredBehavior.expectedReadOnly,
|
|
212
|
+
destructiveHint: inferredBehavior.expectedDestructive,
|
|
213
|
+
},
|
|
214
|
+
misalignmentDetected: baseResult.issues.some((i) => i.includes("misaligned")),
|
|
215
|
+
source: "pattern-based",
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const currentAnnotations = baseResult.annotations
|
|
221
|
+
? {
|
|
222
|
+
readOnlyHint: baseResult.annotations.readOnlyHint,
|
|
223
|
+
destructiveHint: baseResult.annotations.destructiveHint,
|
|
224
|
+
}
|
|
225
|
+
: undefined;
|
|
226
|
+
const inference = await this.claudeBridge.inferToolBehavior(tool, currentAnnotations);
|
|
227
|
+
// Handle null result (Claude unavailable or error)
|
|
228
|
+
if (!inference) {
|
|
229
|
+
return {
|
|
230
|
+
...baseResult,
|
|
231
|
+
claudeInference: {
|
|
232
|
+
expectedReadOnly: inferredBehavior.expectedReadOnly,
|
|
233
|
+
expectedDestructive: inferredBehavior.expectedDestructive,
|
|
234
|
+
confidence: 0,
|
|
235
|
+
reasoning: "Claude inference unavailable. Using pattern-based analysis.",
|
|
236
|
+
suggestedAnnotations: {},
|
|
237
|
+
misalignmentDetected: false,
|
|
238
|
+
misalignmentDetails: undefined,
|
|
239
|
+
source: "pattern-based",
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Merge Claude inference with pattern-based findings
|
|
244
|
+
const updatedIssues = [...baseResult.issues];
|
|
245
|
+
const updatedRecommendations = [...baseResult.recommendations];
|
|
246
|
+
// Add Claude-detected misalignment if high confidence
|
|
247
|
+
if (inference.misalignmentDetected && inference.confidence >= 70) {
|
|
248
|
+
const misalignmentMsg = inference.misalignmentDetails
|
|
249
|
+
? `Claude analysis (${inference.confidence}% confidence): ${inference.misalignmentDetails}`
|
|
250
|
+
: `Claude analysis detected annotation misalignment with ${inference.confidence}% confidence`;
|
|
251
|
+
if (!updatedIssues.some((i) => i.includes("Claude analysis"))) {
|
|
252
|
+
updatedIssues.push(misalignmentMsg);
|
|
253
|
+
}
|
|
254
|
+
// Add specific recommendations based on Claude inference
|
|
255
|
+
if (inference.suggestedAnnotations) {
|
|
256
|
+
const { readOnlyHint, destructiveHint, idempotentHint } = inference.suggestedAnnotations;
|
|
257
|
+
if (readOnlyHint !== undefined &&
|
|
258
|
+
readOnlyHint !== baseResult.annotations?.readOnlyHint) {
|
|
259
|
+
updatedRecommendations.push(`Claude suggests: Set readOnlyHint=${readOnlyHint} for ${tool.name}`);
|
|
260
|
+
}
|
|
261
|
+
if (destructiveHint !== undefined &&
|
|
262
|
+
destructiveHint !== baseResult.annotations?.destructiveHint) {
|
|
263
|
+
updatedRecommendations.push(`Claude suggests: Set destructiveHint=${destructiveHint} for ${tool.name}`);
|
|
264
|
+
}
|
|
265
|
+
if (idempotentHint !== undefined) {
|
|
266
|
+
updatedRecommendations.push(`Claude suggests: Consider adding idempotentHint=${idempotentHint} for ${tool.name}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
...baseResult,
|
|
272
|
+
issues: updatedIssues,
|
|
273
|
+
recommendations: updatedRecommendations,
|
|
274
|
+
claudeInference: {
|
|
275
|
+
expectedReadOnly: inference.expectedReadOnly,
|
|
276
|
+
expectedDestructive: inference.expectedDestructive,
|
|
277
|
+
confidence: inference.confidence,
|
|
278
|
+
reasoning: inference.reasoning,
|
|
279
|
+
suggestedAnnotations: inference.suggestedAnnotations,
|
|
280
|
+
misalignmentDetected: inference.misalignmentDetected,
|
|
281
|
+
misalignmentDetails: inference.misalignmentDetails,
|
|
282
|
+
source: "claude-inferred",
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
this.logError(`Claude inference failed for ${tool.name}`, error);
|
|
288
|
+
// Fall back to pattern-based (use inferredBehavior from top of function)
|
|
289
|
+
return {
|
|
290
|
+
...baseResult,
|
|
291
|
+
claudeInference: {
|
|
292
|
+
expectedReadOnly: inferredBehavior.expectedReadOnly,
|
|
293
|
+
expectedDestructive: inferredBehavior.expectedDestructive,
|
|
294
|
+
confidence: 50,
|
|
295
|
+
reasoning: `Claude inference failed, using pattern-based: ${inferredBehavior.reason}`,
|
|
296
|
+
suggestedAnnotations: {
|
|
297
|
+
readOnlyHint: inferredBehavior.expectedReadOnly,
|
|
298
|
+
destructiveHint: inferredBehavior.expectedDestructive,
|
|
299
|
+
},
|
|
300
|
+
misalignmentDetected: baseResult.issues.some((i) => i.includes("misaligned")),
|
|
301
|
+
source: "pattern-based",
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Generate enhanced explanation with Claude analysis
|
|
308
|
+
*/
|
|
309
|
+
generateEnhancedExplanation(annotatedCount, missingCount, highConfidenceMisalignments, totalTools) {
|
|
310
|
+
const parts = [];
|
|
311
|
+
if (totalTools === 0) {
|
|
312
|
+
return "No tools found to assess for annotations.";
|
|
313
|
+
}
|
|
314
|
+
parts.push(`Tool annotation coverage: ${annotatedCount}/${totalTools} tools have annotations.`);
|
|
315
|
+
if (missingCount > 0) {
|
|
316
|
+
parts.push(`${missingCount} tool(s) are missing required annotations (readOnlyHint, destructiveHint).`);
|
|
317
|
+
}
|
|
318
|
+
if (highConfidenceMisalignments > 0) {
|
|
319
|
+
parts.push(`Claude analysis identified ${highConfidenceMisalignments} high-confidence annotation misalignment(s).`);
|
|
320
|
+
}
|
|
321
|
+
parts.push("Analysis enhanced with Claude semantic behavior inference.");
|
|
322
|
+
return parts.join(" ");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Generate enhanced recommendations with Claude analysis
|
|
326
|
+
*/
|
|
327
|
+
generateEnhancedRecommendations(results) {
|
|
328
|
+
const recommendations = [];
|
|
329
|
+
// Prioritize Claude high-confidence misalignments
|
|
330
|
+
const claudeMisalignments = results.filter((r) => r.claudeInference &&
|
|
331
|
+
r.claudeInference.source === "claude-inferred" &&
|
|
332
|
+
r.claudeInference.confidence >= 70 &&
|
|
333
|
+
r.claudeInference.misalignmentDetected);
|
|
334
|
+
if (claudeMisalignments.length > 0) {
|
|
335
|
+
recommendations.push("HIGH CONFIDENCE: Claude analysis identified the following annotation issues:");
|
|
336
|
+
for (const result of claudeMisalignments.slice(0, 5)) {
|
|
337
|
+
if (result.claudeInference) {
|
|
338
|
+
recommendations.push(` - ${result.toolName}: ${result.claudeInference.reasoning}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Collect Claude suggestions
|
|
343
|
+
const claudeSuggestions = results
|
|
344
|
+
.filter((r) => r.claudeInference &&
|
|
345
|
+
r.claudeInference.source === "claude-inferred" &&
|
|
346
|
+
r.claudeInference.confidence >= 60)
|
|
347
|
+
.flatMap((r) => r.recommendations.filter((rec) => rec.includes("Claude")));
|
|
348
|
+
if (claudeSuggestions.length > 0) {
|
|
349
|
+
recommendations.push(...claudeSuggestions.slice(0, 5));
|
|
350
|
+
}
|
|
351
|
+
// Add pattern-based recommendations for remaining tools
|
|
352
|
+
const patternRecs = new Set();
|
|
353
|
+
for (const result of results) {
|
|
354
|
+
for (const rec of result.recommendations) {
|
|
355
|
+
if (!rec.includes("Claude")) {
|
|
356
|
+
patternRecs.add(rec);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const destructiveRecs = Array.from(patternRecs).filter((r) => r.includes("destructive"));
|
|
361
|
+
const otherRecs = Array.from(patternRecs).filter((r) => !r.includes("destructive"));
|
|
362
|
+
if (destructiveRecs.length > 0) {
|
|
363
|
+
recommendations.push("PRIORITY: Potential destructive tools without proper hints:");
|
|
364
|
+
recommendations.push(...destructiveRecs.slice(0, 3));
|
|
365
|
+
}
|
|
366
|
+
if (otherRecs.length > 0 && recommendations.length < 10) {
|
|
367
|
+
recommendations.push(...otherRecs.slice(0, 3));
|
|
368
|
+
}
|
|
369
|
+
if (recommendations.length === 0) {
|
|
370
|
+
recommendations.push("All tools have proper annotations. No action required.");
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
recommendations.push("Reference: MCP Directory Policy #17 requires tools to have readOnlyHint and destructiveHint annotations.");
|
|
374
|
+
}
|
|
375
|
+
return recommendations;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Assess a single tool's annotations
|
|
379
|
+
*/
|
|
380
|
+
assessTool(tool) {
|
|
381
|
+
const issues = [];
|
|
382
|
+
const recommendations = [];
|
|
383
|
+
// Extract annotations from tool
|
|
384
|
+
const annotations = this.extractAnnotations(tool);
|
|
385
|
+
const hasAnnotations = annotations.readOnlyHint !== undefined ||
|
|
386
|
+
annotations.destructiveHint !== undefined;
|
|
387
|
+
// Infer expected behavior from tool name
|
|
388
|
+
const inferredBehavior = this.inferBehavior(tool.name, tool.description);
|
|
389
|
+
// Check for missing annotations
|
|
390
|
+
if (!hasAnnotations) {
|
|
391
|
+
issues.push("Missing tool annotations (readOnlyHint, destructiveHint)");
|
|
392
|
+
recommendations.push(`Add annotations to ${tool.name}: readOnlyHint=${inferredBehavior.expectedReadOnly}, destructiveHint=${inferredBehavior.expectedDestructive}`);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// Check for misaligned annotations
|
|
396
|
+
if (annotations.readOnlyHint !== undefined &&
|
|
397
|
+
annotations.readOnlyHint !== inferredBehavior.expectedReadOnly) {
|
|
398
|
+
issues.push(`Potentially misaligned readOnlyHint: set to ${annotations.readOnlyHint}, expected ${inferredBehavior.expectedReadOnly} based on tool name pattern`);
|
|
399
|
+
recommendations.push(`Verify readOnlyHint for ${tool.name}: currently ${annotations.readOnlyHint}, tool name suggests ${inferredBehavior.expectedReadOnly}`);
|
|
400
|
+
}
|
|
401
|
+
if (annotations.destructiveHint !== undefined &&
|
|
402
|
+
annotations.destructiveHint !== inferredBehavior.expectedDestructive) {
|
|
403
|
+
issues.push(`Potentially misaligned destructiveHint: set to ${annotations.destructiveHint}, expected ${inferredBehavior.expectedDestructive} based on tool name pattern`);
|
|
404
|
+
recommendations.push(`Verify destructiveHint for ${tool.name}: currently ${annotations.destructiveHint}, tool name suggests ${inferredBehavior.expectedDestructive}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// Check for destructive tools without explicit hint
|
|
408
|
+
if (inferredBehavior.expectedDestructive &&
|
|
409
|
+
annotations.destructiveHint !== true) {
|
|
410
|
+
issues.push("Tool appears destructive but destructiveHint is not set to true");
|
|
411
|
+
recommendations.push(`Set destructiveHint=true for ${tool.name} - this tool appears to perform destructive operations`);
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
toolName: tool.name,
|
|
415
|
+
hasAnnotations,
|
|
416
|
+
annotations: hasAnnotations ? annotations : undefined,
|
|
417
|
+
inferredBehavior,
|
|
418
|
+
issues,
|
|
419
|
+
recommendations,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Extract annotations from a tool
|
|
424
|
+
* MCP SDK may have annotations in different locations
|
|
425
|
+
*/
|
|
426
|
+
extractAnnotations(tool) {
|
|
427
|
+
// Try to find annotations in various locations
|
|
428
|
+
const toolAny = tool;
|
|
429
|
+
// Check direct properties
|
|
430
|
+
let readOnlyHint = toolAny.readOnlyHint;
|
|
431
|
+
let destructiveHint = toolAny.destructiveHint;
|
|
432
|
+
let idempotentHint = toolAny.idempotentHint;
|
|
433
|
+
let openWorldHint = toolAny.openWorldHint;
|
|
434
|
+
// Check annotations object (MCP 2024-11 spec)
|
|
435
|
+
if (toolAny.annotations) {
|
|
436
|
+
readOnlyHint = readOnlyHint ?? toolAny.annotations.readOnlyHint;
|
|
437
|
+
destructiveHint = destructiveHint ?? toolAny.annotations.destructiveHint;
|
|
438
|
+
idempotentHint = idempotentHint ?? toolAny.annotations.idempotentHint;
|
|
439
|
+
openWorldHint = openWorldHint ?? toolAny.annotations.openWorldHint;
|
|
440
|
+
}
|
|
441
|
+
// Check metadata (some servers use this)
|
|
442
|
+
if (toolAny.metadata) {
|
|
443
|
+
readOnlyHint = readOnlyHint ?? toolAny.metadata.readOnlyHint;
|
|
444
|
+
destructiveHint = destructiveHint ?? toolAny.metadata.destructiveHint;
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
readOnlyHint,
|
|
448
|
+
destructiveHint,
|
|
449
|
+
title: toolAny.title || toolAny.annotations?.title,
|
|
450
|
+
description: tool.description,
|
|
451
|
+
idempotentHint,
|
|
452
|
+
openWorldHint,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Infer expected behavior from tool name and description
|
|
457
|
+
*/
|
|
458
|
+
inferBehavior(toolName, description) {
|
|
459
|
+
const lowerName = toolName.toLowerCase();
|
|
460
|
+
const lowerDesc = (description || "").toLowerCase();
|
|
461
|
+
// Check for destructive patterns first (higher priority)
|
|
462
|
+
for (const pattern of DESTRUCTIVE_PATTERNS) {
|
|
463
|
+
if (pattern.test(lowerName)) {
|
|
464
|
+
return {
|
|
465
|
+
expectedReadOnly: false,
|
|
466
|
+
expectedDestructive: true,
|
|
467
|
+
reason: `Tool name matches destructive pattern: ${pattern.source}`,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Check for read-only patterns
|
|
472
|
+
for (const pattern of READ_ONLY_PATTERNS) {
|
|
473
|
+
if (pattern.test(lowerName)) {
|
|
474
|
+
return {
|
|
475
|
+
expectedReadOnly: true,
|
|
476
|
+
expectedDestructive: false,
|
|
477
|
+
reason: `Tool name matches read-only pattern: ${pattern.source}`,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// Check for write patterns (not destructive but not read-only)
|
|
482
|
+
for (const pattern of WRITE_PATTERNS) {
|
|
483
|
+
if (pattern.test(lowerName)) {
|
|
484
|
+
return {
|
|
485
|
+
expectedReadOnly: false,
|
|
486
|
+
expectedDestructive: false,
|
|
487
|
+
reason: `Tool name matches write pattern: ${pattern.source}`,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// Check description for hints
|
|
492
|
+
if (lowerDesc.includes("delete") || lowerDesc.includes("remove")) {
|
|
493
|
+
return {
|
|
494
|
+
expectedReadOnly: false,
|
|
495
|
+
expectedDestructive: true,
|
|
496
|
+
reason: "Description mentions delete/remove operations",
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
if (lowerDesc.includes("read") ||
|
|
500
|
+
lowerDesc.includes("get") ||
|
|
501
|
+
lowerDesc.includes("fetch")) {
|
|
502
|
+
return {
|
|
503
|
+
expectedReadOnly: true,
|
|
504
|
+
expectedDestructive: false,
|
|
505
|
+
reason: "Description suggests read-only operation",
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
// Default: assume write (safer to warn about missing annotations)
|
|
509
|
+
return {
|
|
510
|
+
expectedReadOnly: false,
|
|
511
|
+
expectedDestructive: false,
|
|
512
|
+
reason: "Could not infer from name pattern - defaulting to write operation",
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Determine overall status
|
|
517
|
+
*/
|
|
518
|
+
determineAnnotationStatus(results, totalTools) {
|
|
519
|
+
if (totalTools === 0)
|
|
520
|
+
return "PASS";
|
|
521
|
+
const annotatedCount = results.filter((r) => r.hasAnnotations).length;
|
|
522
|
+
const misalignedCount = results.filter((r) => r.issues.some((i) => i.includes("misaligned"))).length;
|
|
523
|
+
const destructiveWithoutHint = results.filter((r) => r.issues.some((i) => i.includes("destructive") && i.includes("not set"))).length;
|
|
524
|
+
// Destructive tools without proper hints = FAIL (check this FIRST)
|
|
525
|
+
if (destructiveWithoutHint > 0) {
|
|
526
|
+
return "FAIL";
|
|
527
|
+
}
|
|
528
|
+
// All tools annotated and no misalignments = PASS
|
|
529
|
+
if (annotatedCount === totalTools && misalignedCount === 0) {
|
|
530
|
+
return "PASS";
|
|
531
|
+
}
|
|
532
|
+
// Some annotations missing = NEED_MORE_INFO
|
|
533
|
+
const annotationRate = annotatedCount / totalTools;
|
|
534
|
+
if (annotationRate >= 0.8) {
|
|
535
|
+
return "NEED_MORE_INFO";
|
|
536
|
+
}
|
|
537
|
+
// Mostly missing annotations = FAIL
|
|
538
|
+
if (annotationRate < 0.5) {
|
|
539
|
+
return "FAIL";
|
|
540
|
+
}
|
|
541
|
+
return "NEED_MORE_INFO";
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Generate explanation
|
|
545
|
+
*/
|
|
546
|
+
generateExplanation(annotatedCount, missingCount, misalignedCount, totalTools) {
|
|
547
|
+
const parts = [];
|
|
548
|
+
if (totalTools === 0) {
|
|
549
|
+
return "No tools found to assess for annotations.";
|
|
550
|
+
}
|
|
551
|
+
parts.push(`Tool annotation coverage: ${annotatedCount}/${totalTools} tools have annotations.`);
|
|
552
|
+
if (missingCount > 0) {
|
|
553
|
+
parts.push(`${missingCount} tool(s) are missing required annotations (readOnlyHint, destructiveHint).`);
|
|
554
|
+
}
|
|
555
|
+
if (misalignedCount > 0) {
|
|
556
|
+
parts.push(`${misalignedCount} tool(s) have potentially misaligned annotations based on naming patterns.`);
|
|
557
|
+
}
|
|
558
|
+
if (missingCount === 0 && misalignedCount === 0) {
|
|
559
|
+
parts.push("All tools are properly annotated.");
|
|
560
|
+
}
|
|
561
|
+
return parts.join(" ");
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Generate recommendations
|
|
565
|
+
*/
|
|
566
|
+
generateRecommendations(results) {
|
|
567
|
+
const recommendations = [];
|
|
568
|
+
// Collect unique recommendations from all tools
|
|
569
|
+
const allRecs = new Set();
|
|
570
|
+
for (const result of results) {
|
|
571
|
+
for (const rec of result.recommendations) {
|
|
572
|
+
allRecs.add(rec);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// Prioritize destructive tool warnings
|
|
576
|
+
const destructiveRecs = Array.from(allRecs).filter((r) => r.includes("destructive"));
|
|
577
|
+
const otherRecs = Array.from(allRecs).filter((r) => !r.includes("destructive"));
|
|
578
|
+
if (destructiveRecs.length > 0) {
|
|
579
|
+
recommendations.push("PRIORITY: The following tools appear to perform destructive operations but lack proper destructiveHint annotation:");
|
|
580
|
+
recommendations.push(...destructiveRecs.slice(0, 5));
|
|
581
|
+
}
|
|
582
|
+
if (otherRecs.length > 0) {
|
|
583
|
+
recommendations.push(...otherRecs.slice(0, 5));
|
|
584
|
+
}
|
|
585
|
+
if (recommendations.length === 0) {
|
|
586
|
+
recommendations.push("All tools have proper annotations. No action required.");
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
recommendations.push("Reference: MCP Directory Policy #17 requires tools to have readOnlyHint and destructiveHint annotations.");
|
|
590
|
+
}
|
|
591
|
+
return recommendations;
|
|
592
|
+
}
|
|
593
|
+
}
|