@bryan-thompson/inspector-assessment 1.43.0 → 1.43.1
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/cli/package.json +1 -1
- package/client/dist/assets/{OAuthCallback-BEnTdTGR.js → OAuthCallback-ngu_aFUO.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-CTNSrDLW.js → OAuthDebugCallback-CsGYu8op.js} +1 -1
- package/client/dist/assets/{index-DEeUx8Bb.js → index-97IA_LWd.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/services/assessment/helpers/StaticAnnotationScanner.d.ts +137 -0
- package/client/lib/services/assessment/helpers/StaticAnnotationScanner.d.ts.map +1 -0
- package/client/lib/services/assessment/helpers/StaticAnnotationScanner.js +345 -0
- package/client/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/ToolAnnotationAssessor.js +13 -1
- package/client/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts +7 -1
- package/client/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/annotations/AlignmentChecker.js +27 -3
- package/client/package.json +1 -1
- package/package.json +3 -1
- package/server/package.json +1 -1
package/cli/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-
|
|
1
|
+
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-97IA_LWd.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-CTNSrDLW.js → OAuthDebugCallback-CsGYu8op.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-
|
|
1
|
+
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-97IA_LWd.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -16373,7 +16373,7 @@ object({
|
|
|
16373
16373
|
token_type_hint: string().optional()
|
|
16374
16374
|
}).strip();
|
|
16375
16375
|
const name = "@bryan-thompson/inspector-assessment-client";
|
|
16376
|
-
const version$1 = "1.43.
|
|
16376
|
+
const version$1 = "1.43.1";
|
|
16377
16377
|
const packageJson = {
|
|
16378
16378
|
name,
|
|
16379
16379
|
version: version$1
|
|
@@ -49456,7 +49456,7 @@ const useTheme = () => {
|
|
|
49456
49456
|
[theme, setThemeWithSideEffect]
|
|
49457
49457
|
);
|
|
49458
49458
|
};
|
|
49459
|
-
const version = "1.43.
|
|
49459
|
+
const version = "1.43.1";
|
|
49460
49460
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
49461
49461
|
createPopperScope
|
|
49462
49462
|
]);
|
|
@@ -52799,13 +52799,13 @@ const App = () => {
|
|
|
52799
52799
|
};
|
|
52800
52800
|
if (window.location.pathname === "/oauth/callback") {
|
|
52801
52801
|
const OAuthCallback = React.lazy(
|
|
52802
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
52802
|
+
() => __vitePreload(() => import("./OAuthCallback-ngu_aFUO.js"), true ? [] : void 0)
|
|
52803
52803
|
);
|
|
52804
52804
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
52805
52805
|
}
|
|
52806
52806
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
52807
52807
|
const OAuthDebugCallback = React.lazy(
|
|
52808
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
52808
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-CsGYu8op.js"), true ? [] : void 0)
|
|
52809
52809
|
);
|
|
52810
52810
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
52811
52811
|
}
|
package/client/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-97IA_LWd.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-BoUA5OL1.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Annotation Scanner
|
|
3
|
+
*
|
|
4
|
+
* Scans source code files for tool annotations using AST parsing.
|
|
5
|
+
* Detects annotations nested inside tool definition objects/arrays
|
|
6
|
+
* in ES module syntax that regex-based scanning would miss.
|
|
7
|
+
*
|
|
8
|
+
* Fixes Issue #192: Static annotation scanner misses nested annotations
|
|
9
|
+
* in ES module syntax like:
|
|
10
|
+
* const TOOLS = [{ name: 'x', annotations: { readOnlyHint: true } }];
|
|
11
|
+
*
|
|
12
|
+
* @module helpers/StaticAnnotationScanner
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Evidence from static annotation scanning
|
|
16
|
+
*/
|
|
17
|
+
export interface StaticAnnotationEvidence {
|
|
18
|
+
/** File path where annotation was found */
|
|
19
|
+
filePath: string;
|
|
20
|
+
/** Tool name associated with the annotation */
|
|
21
|
+
toolName: string;
|
|
22
|
+
/** Confidence level */
|
|
23
|
+
confidence: "high" | "medium" | "low";
|
|
24
|
+
/** Description of how the annotation was found */
|
|
25
|
+
detail: string;
|
|
26
|
+
/** Line number in source file */
|
|
27
|
+
lineNumber?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extracted annotation from source code
|
|
31
|
+
*/
|
|
32
|
+
export interface StaticAnnotation {
|
|
33
|
+
toolName: string;
|
|
34
|
+
readOnlyHint?: boolean;
|
|
35
|
+
destructiveHint?: boolean;
|
|
36
|
+
idempotentHint?: boolean;
|
|
37
|
+
openWorldHint?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Result of static annotation scanning
|
|
41
|
+
*/
|
|
42
|
+
export interface StaticAnnotationScanResult {
|
|
43
|
+
/** Map of tool name to extracted annotations */
|
|
44
|
+
annotations: Map<string, StaticAnnotation>;
|
|
45
|
+
/** Overall confidence of the scan */
|
|
46
|
+
confidence: "high" | "medium" | "low";
|
|
47
|
+
/** Evidence collected during scanning */
|
|
48
|
+
evidence: StaticAnnotationEvidence[];
|
|
49
|
+
/** Whether source code was scanned */
|
|
50
|
+
sourceCodeScanned: boolean;
|
|
51
|
+
/** Count of tools with annotations found */
|
|
52
|
+
annotatedToolCount: number;
|
|
53
|
+
/** Files that were scanned */
|
|
54
|
+
scannedFiles: string[];
|
|
55
|
+
/** Errors encountered during parsing */
|
|
56
|
+
parseErrors: Array<{
|
|
57
|
+
file: string;
|
|
58
|
+
error: string;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Scans source code for tool annotations using AST parsing.
|
|
63
|
+
*
|
|
64
|
+
* Detection approach:
|
|
65
|
+
* 1. Parse JS/TS files with acorn (ecmaVersion 2022, module syntax)
|
|
66
|
+
* 2. Walk AST looking for Property nodes with key 'annotations'
|
|
67
|
+
* 3. Extract annotation values (readOnlyHint, destructiveHint, etc.)
|
|
68
|
+
* 4. Find associated tool name from sibling 'name' property in parent object
|
|
69
|
+
*
|
|
70
|
+
* @public
|
|
71
|
+
*/
|
|
72
|
+
export declare class StaticAnnotationScanner {
|
|
73
|
+
/**
|
|
74
|
+
* File patterns to skip during source code scanning
|
|
75
|
+
* (same patterns as StdioTransportDetector for consistency)
|
|
76
|
+
*/
|
|
77
|
+
private readonly SKIP_FILE_PATTERNS;
|
|
78
|
+
/** Maximum file size for source scanning (500KB) */
|
|
79
|
+
private readonly MAX_FILE_SIZE;
|
|
80
|
+
/** File extensions to scan */
|
|
81
|
+
private readonly SCANNABLE_EXTENSIONS;
|
|
82
|
+
/**
|
|
83
|
+
* Scan source files for tool annotations.
|
|
84
|
+
*
|
|
85
|
+
* @param sourceCodeFiles - Map of file paths to content
|
|
86
|
+
* @returns Static annotation scan results
|
|
87
|
+
*/
|
|
88
|
+
scan(sourceCodeFiles?: Map<string, string>): StaticAnnotationScanResult;
|
|
89
|
+
/**
|
|
90
|
+
* Parse a single file for tool annotations.
|
|
91
|
+
*
|
|
92
|
+
* @param filePath - File path for error reporting
|
|
93
|
+
* @param content - File content to parse
|
|
94
|
+
* @returns Array of extracted annotations with line numbers
|
|
95
|
+
*/
|
|
96
|
+
private parseFile;
|
|
97
|
+
/**
|
|
98
|
+
* Check if a property node is an 'annotations' property.
|
|
99
|
+
*/
|
|
100
|
+
private isAnnotationsProperty;
|
|
101
|
+
/**
|
|
102
|
+
* Extract annotation values from an ObjectExpression node.
|
|
103
|
+
*/
|
|
104
|
+
private extractAnnotationValues;
|
|
105
|
+
/**
|
|
106
|
+
* Get the string name of a property key.
|
|
107
|
+
*/
|
|
108
|
+
private getPropertyKeyName;
|
|
109
|
+
/**
|
|
110
|
+
* Find the tool name from ancestor context.
|
|
111
|
+
* Looks for a sibling 'name' property in the parent ObjectExpression.
|
|
112
|
+
*/
|
|
113
|
+
private findToolNameFromContext;
|
|
114
|
+
/**
|
|
115
|
+
* Strip common TypeScript syntax for basic parsing.
|
|
116
|
+
* This is a simple approach - just removes type annotations to allow JS parsing.
|
|
117
|
+
*/
|
|
118
|
+
private stripTypeScript;
|
|
119
|
+
/**
|
|
120
|
+
* Check if file should be skipped during scanning.
|
|
121
|
+
*/
|
|
122
|
+
private shouldSkipFile;
|
|
123
|
+
/**
|
|
124
|
+
* Check if file has a scannable extension.
|
|
125
|
+
*/
|
|
126
|
+
private isScannableFile;
|
|
127
|
+
/**
|
|
128
|
+
* Compute overall confidence from collected evidence.
|
|
129
|
+
*
|
|
130
|
+
* Confidence rules:
|
|
131
|
+
* - High: 2+ annotations found with explicit 'annotations' objects
|
|
132
|
+
* - Medium: 1 annotation found
|
|
133
|
+
* - Low: No annotations found
|
|
134
|
+
*/
|
|
135
|
+
private computeConfidence;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=StaticAnnotationScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaticAnnotationScanner.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/helpers/StaticAnnotationScanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,gDAAgD;IAChD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,qCAAqC;IACrC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,yCAAyC;IACzC,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,sCAAsC;IACtC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,8BAA8B;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,wCAAwC;IACxC,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AA2CD;;;;;;;;;;GAUG;AACH,qBAAa,uBAAuB;IAClC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAejC;IAEF,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IAEzC,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAOnC;IAEF;;;;;OAKG;IACH,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,0BAA0B;IAmEvE;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAuEjB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA8C/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA4B/B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkBvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;CAsB1B"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Annotation Scanner
|
|
3
|
+
*
|
|
4
|
+
* Scans source code files for tool annotations using AST parsing.
|
|
5
|
+
* Detects annotations nested inside tool definition objects/arrays
|
|
6
|
+
* in ES module syntax that regex-based scanning would miss.
|
|
7
|
+
*
|
|
8
|
+
* Fixes Issue #192: Static annotation scanner misses nested annotations
|
|
9
|
+
* in ES module syntax like:
|
|
10
|
+
* const TOOLS = [{ name: 'x', annotations: { readOnlyHint: true } }];
|
|
11
|
+
*
|
|
12
|
+
* @module helpers/StaticAnnotationScanner
|
|
13
|
+
*/
|
|
14
|
+
import * as acorn from "acorn";
|
|
15
|
+
import * as walk from "acorn-walk";
|
|
16
|
+
/**
|
|
17
|
+
* Scans source code for tool annotations using AST parsing.
|
|
18
|
+
*
|
|
19
|
+
* Detection approach:
|
|
20
|
+
* 1. Parse JS/TS files with acorn (ecmaVersion 2022, module syntax)
|
|
21
|
+
* 2. Walk AST looking for Property nodes with key 'annotations'
|
|
22
|
+
* 3. Extract annotation values (readOnlyHint, destructiveHint, etc.)
|
|
23
|
+
* 4. Find associated tool name from sibling 'name' property in parent object
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export class StaticAnnotationScanner {
|
|
28
|
+
/**
|
|
29
|
+
* File patterns to skip during source code scanning
|
|
30
|
+
* (same patterns as StdioTransportDetector for consistency)
|
|
31
|
+
*/
|
|
32
|
+
SKIP_FILE_PATTERNS = [
|
|
33
|
+
/node_modules/i,
|
|
34
|
+
/\.test\.(ts|js|tsx|jsx|py)$/i,
|
|
35
|
+
/\.spec\.(ts|js|tsx|jsx|py)$/i,
|
|
36
|
+
/\.d\.ts$/i,
|
|
37
|
+
/package-lock\.json$/i,
|
|
38
|
+
/yarn\.lock$/i,
|
|
39
|
+
/\.map$/i,
|
|
40
|
+
/\.git\//i,
|
|
41
|
+
/dist\//i,
|
|
42
|
+
/build\//i,
|
|
43
|
+
/__tests__\//i,
|
|
44
|
+
/__mocks__\//i,
|
|
45
|
+
/__pycache__\//i,
|
|
46
|
+
/\.pytest_cache\//i,
|
|
47
|
+
];
|
|
48
|
+
/** Maximum file size for source scanning (500KB) */
|
|
49
|
+
MAX_FILE_SIZE = 500_000;
|
|
50
|
+
/** File extensions to scan */
|
|
51
|
+
SCANNABLE_EXTENSIONS = [
|
|
52
|
+
".js",
|
|
53
|
+
".ts",
|
|
54
|
+
".mjs",
|
|
55
|
+
".cjs",
|
|
56
|
+
".tsx",
|
|
57
|
+
".jsx",
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Scan source files for tool annotations.
|
|
61
|
+
*
|
|
62
|
+
* @param sourceCodeFiles - Map of file paths to content
|
|
63
|
+
* @returns Static annotation scan results
|
|
64
|
+
*/
|
|
65
|
+
scan(sourceCodeFiles) {
|
|
66
|
+
const annotations = new Map();
|
|
67
|
+
const evidence = [];
|
|
68
|
+
const scannedFiles = [];
|
|
69
|
+
const parseErrors = [];
|
|
70
|
+
if (!sourceCodeFiles || sourceCodeFiles.size === 0) {
|
|
71
|
+
return {
|
|
72
|
+
annotations,
|
|
73
|
+
confidence: "low",
|
|
74
|
+
evidence,
|
|
75
|
+
sourceCodeScanned: false,
|
|
76
|
+
annotatedToolCount: 0,
|
|
77
|
+
scannedFiles,
|
|
78
|
+
parseErrors,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
sourceCodeFiles.forEach((content, filePath) => {
|
|
82
|
+
// Skip files that shouldn't be scanned
|
|
83
|
+
if (this.shouldSkipFile(filePath))
|
|
84
|
+
return;
|
|
85
|
+
// Skip oversized files
|
|
86
|
+
if (content.length > this.MAX_FILE_SIZE)
|
|
87
|
+
return;
|
|
88
|
+
// Only scan JS/TS files
|
|
89
|
+
if (!this.isScannableFile(filePath))
|
|
90
|
+
return;
|
|
91
|
+
scannedFiles.push(filePath);
|
|
92
|
+
try {
|
|
93
|
+
const fileAnnotations = this.parseFile(filePath, content);
|
|
94
|
+
for (const ann of fileAnnotations) {
|
|
95
|
+
// Store annotation (later occurrences override earlier)
|
|
96
|
+
annotations.set(ann.toolName, ann);
|
|
97
|
+
// Record evidence
|
|
98
|
+
evidence.push({
|
|
99
|
+
filePath,
|
|
100
|
+
toolName: ann.toolName,
|
|
101
|
+
confidence: "high",
|
|
102
|
+
detail: `Found annotations object in tool definition`,
|
|
103
|
+
lineNumber: ann.lineNumber,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
parseErrors.push({
|
|
109
|
+
file: filePath,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const confidence = this.computeConfidence(evidence);
|
|
115
|
+
return {
|
|
116
|
+
annotations,
|
|
117
|
+
confidence,
|
|
118
|
+
evidence,
|
|
119
|
+
sourceCodeScanned: scannedFiles.length > 0,
|
|
120
|
+
annotatedToolCount: annotations.size,
|
|
121
|
+
scannedFiles,
|
|
122
|
+
parseErrors,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Parse a single file for tool annotations.
|
|
127
|
+
*
|
|
128
|
+
* @param filePath - File path for error reporting
|
|
129
|
+
* @param content - File content to parse
|
|
130
|
+
* @returns Array of extracted annotations with line numbers
|
|
131
|
+
*/
|
|
132
|
+
parseFile(filePath, content) {
|
|
133
|
+
const results = [];
|
|
134
|
+
// Try to parse as ES module first, fall back to script
|
|
135
|
+
let ast;
|
|
136
|
+
try {
|
|
137
|
+
ast = acorn.parse(content, {
|
|
138
|
+
ecmaVersion: 2022,
|
|
139
|
+
sourceType: "module",
|
|
140
|
+
locations: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Try as script (CommonJS)
|
|
145
|
+
try {
|
|
146
|
+
ast = acorn.parse(content, {
|
|
147
|
+
ecmaVersion: 2022,
|
|
148
|
+
sourceType: "script",
|
|
149
|
+
locations: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (scriptError) {
|
|
153
|
+
// For TypeScript files, try stripping type annotations
|
|
154
|
+
// (basic approach - strip common TS patterns)
|
|
155
|
+
const strippedContent = this.stripTypeScript(content);
|
|
156
|
+
try {
|
|
157
|
+
ast = acorn.parse(strippedContent, {
|
|
158
|
+
ecmaVersion: 2022,
|
|
159
|
+
sourceType: "module",
|
|
160
|
+
locations: true,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
throw new Error(`Failed to parse ${filePath}: ${scriptError instanceof Error ? scriptError.message : String(scriptError)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Walk the AST with ancestor tracking
|
|
169
|
+
walk.ancestor(ast, {
|
|
170
|
+
Property: (node, ancestors) => {
|
|
171
|
+
const prop = node;
|
|
172
|
+
// Check if this is an 'annotations' property
|
|
173
|
+
if (!this.isAnnotationsProperty(prop))
|
|
174
|
+
return;
|
|
175
|
+
// Value must be an object expression
|
|
176
|
+
if (prop.value.type !== "ObjectExpression")
|
|
177
|
+
return;
|
|
178
|
+
const annotationValues = this.extractAnnotationValues(prop.value);
|
|
179
|
+
if (!annotationValues)
|
|
180
|
+
return;
|
|
181
|
+
// Find the tool name from parent context
|
|
182
|
+
const toolName = this.findToolNameFromContext(ancestors);
|
|
183
|
+
if (!toolName)
|
|
184
|
+
return;
|
|
185
|
+
results.push({
|
|
186
|
+
toolName,
|
|
187
|
+
...annotationValues,
|
|
188
|
+
lineNumber: prop.loc?.start.line,
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
return results;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Check if a property node is an 'annotations' property.
|
|
196
|
+
*/
|
|
197
|
+
isAnnotationsProperty(prop) {
|
|
198
|
+
// Handle Identifier key: annotations: {...}
|
|
199
|
+
if (prop.key.type === "Identifier") {
|
|
200
|
+
return prop.key.name === "annotations";
|
|
201
|
+
}
|
|
202
|
+
// Handle Literal key: 'annotations': {...} or "annotations": {...}
|
|
203
|
+
if (prop.key.type === "Literal") {
|
|
204
|
+
return prop.key.value === "annotations";
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Extract annotation values from an ObjectExpression node.
|
|
210
|
+
*/
|
|
211
|
+
extractAnnotationValues(obj) {
|
|
212
|
+
const result = {};
|
|
213
|
+
let hasAnyAnnotation = false;
|
|
214
|
+
for (const prop of obj.properties) {
|
|
215
|
+
if (prop.type !== "Property")
|
|
216
|
+
continue;
|
|
217
|
+
const propNode = prop;
|
|
218
|
+
const keyName = this.getPropertyKeyName(propNode);
|
|
219
|
+
if (!keyName)
|
|
220
|
+
continue;
|
|
221
|
+
// Only extract boolean values
|
|
222
|
+
if (propNode.value.type !== "Literal")
|
|
223
|
+
continue;
|
|
224
|
+
const literalValue = propNode.value.value;
|
|
225
|
+
if (typeof literalValue !== "boolean")
|
|
226
|
+
continue;
|
|
227
|
+
// Map property names (handle both *Hint and non-suffixed)
|
|
228
|
+
switch (keyName) {
|
|
229
|
+
case "readOnlyHint":
|
|
230
|
+
case "readOnly":
|
|
231
|
+
result.readOnlyHint = literalValue;
|
|
232
|
+
hasAnyAnnotation = true;
|
|
233
|
+
break;
|
|
234
|
+
case "destructiveHint":
|
|
235
|
+
case "destructive":
|
|
236
|
+
result.destructiveHint = literalValue;
|
|
237
|
+
hasAnyAnnotation = true;
|
|
238
|
+
break;
|
|
239
|
+
case "idempotentHint":
|
|
240
|
+
case "idempotent":
|
|
241
|
+
result.idempotentHint = literalValue;
|
|
242
|
+
hasAnyAnnotation = true;
|
|
243
|
+
break;
|
|
244
|
+
case "openWorldHint":
|
|
245
|
+
case "openWorld":
|
|
246
|
+
result.openWorldHint = literalValue;
|
|
247
|
+
hasAnyAnnotation = true;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return hasAnyAnnotation ? result : null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get the string name of a property key.
|
|
255
|
+
*/
|
|
256
|
+
getPropertyKeyName(prop) {
|
|
257
|
+
if (prop.key.type === "Identifier") {
|
|
258
|
+
return prop.key.name;
|
|
259
|
+
}
|
|
260
|
+
if (prop.key.type === "Literal" && typeof prop.key.value === "string") {
|
|
261
|
+
return prop.key.value;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Find the tool name from ancestor context.
|
|
267
|
+
* Looks for a sibling 'name' property in the parent ObjectExpression.
|
|
268
|
+
*/
|
|
269
|
+
findToolNameFromContext(ancestors) {
|
|
270
|
+
// Walk up ancestors looking for ObjectExpression (the tool definition)
|
|
271
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
272
|
+
const ancestor = ancestors[i];
|
|
273
|
+
if (ancestor.type === "ObjectExpression") {
|
|
274
|
+
const objNode = ancestor;
|
|
275
|
+
// Look for sibling 'name' property
|
|
276
|
+
for (const prop of objNode.properties) {
|
|
277
|
+
if (prop.type !== "Property")
|
|
278
|
+
continue;
|
|
279
|
+
const propNode = prop;
|
|
280
|
+
const keyName = this.getPropertyKeyName(propNode);
|
|
281
|
+
if (keyName === "name" && propNode.value.type === "Literal") {
|
|
282
|
+
const nameValue = propNode.value.value;
|
|
283
|
+
if (typeof nameValue === "string") {
|
|
284
|
+
return nameValue;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Strip common TypeScript syntax for basic parsing.
|
|
294
|
+
* This is a simple approach - just removes type annotations to allow JS parsing.
|
|
295
|
+
*/
|
|
296
|
+
stripTypeScript(content) {
|
|
297
|
+
return (content
|
|
298
|
+
// Remove type imports: import type { X } from 'y'
|
|
299
|
+
.replace(/import\s+type\s+\{[^}]*\}\s+from\s+['"][^'"]+['"];?/g, "")
|
|
300
|
+
// Remove type annotations: : Type
|
|
301
|
+
.replace(/:\s*[A-Z][a-zA-Z0-9<>[\],\s|&]*(?=[\s,;)=\]}])/g, "")
|
|
302
|
+
// Remove interface declarations
|
|
303
|
+
.replace(/interface\s+\w+\s*(\{[^}]*\}|\{[\s\S]*?\n\})/g, "")
|
|
304
|
+
// Remove type declarations
|
|
305
|
+
.replace(/type\s+\w+\s*=\s*[^;]+;/g, "")
|
|
306
|
+
// Remove as Type assertions
|
|
307
|
+
.replace(/\s+as\s+[A-Z][a-zA-Z0-9<>[\],\s|&]*/g, "")
|
|
308
|
+
// Remove generic parameters on function calls
|
|
309
|
+
.replace(/<[A-Z][a-zA-Z0-9<>[\],\s|&]*>(?=\()/g, ""));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Check if file should be skipped during scanning.
|
|
313
|
+
*/
|
|
314
|
+
shouldSkipFile(filePath) {
|
|
315
|
+
return this.SKIP_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Check if file has a scannable extension.
|
|
319
|
+
*/
|
|
320
|
+
isScannableFile(filePath) {
|
|
321
|
+
return this.SCANNABLE_EXTENSIONS.some((ext) => filePath.toLowerCase().endsWith(ext));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Compute overall confidence from collected evidence.
|
|
325
|
+
*
|
|
326
|
+
* Confidence rules:
|
|
327
|
+
* - High: 2+ annotations found with explicit 'annotations' objects
|
|
328
|
+
* - Medium: 1 annotation found
|
|
329
|
+
* - Low: No annotations found
|
|
330
|
+
*/
|
|
331
|
+
computeConfidence(evidence) {
|
|
332
|
+
if (evidence.length === 0) {
|
|
333
|
+
return "low";
|
|
334
|
+
}
|
|
335
|
+
// Count high-confidence evidence
|
|
336
|
+
const highConfCount = evidence.filter((e) => e.confidence === "high").length;
|
|
337
|
+
if (highConfCount >= 2) {
|
|
338
|
+
return "high";
|
|
339
|
+
}
|
|
340
|
+
if (highConfCount >= 1) {
|
|
341
|
+
return "medium";
|
|
342
|
+
}
|
|
343
|
+
return "low";
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolAnnotationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ToolAnnotationAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAG9B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAiBL,KAAK,4BAA4B,EAClC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ToolAnnotationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ToolAnnotationAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAG9B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAiBL,KAAK,4BAA4B,EAClC,MAAM,eAAe,CAAC;AAQvB;;;GAGG;AACH,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAExC;;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;AAED,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,kBAAkB,CAAC,CAA2B;gBAE1C,MAAM,EAAE,uBAAuB;IAK3C;;OAEG;IACH,qBAAqB,IAAI,wBAAwB,GAAG,SAAS;IAI7D;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAK7C;;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;CA8TxE"}
|
|
@@ -36,6 +36,8 @@ emitAnnotationEvents,
|
|
|
36
36
|
enhanceWithClaudeInference, createPatternBasedInference, } from "./annotations/index.js";
|
|
37
37
|
// Issue #207: Runtime annotation verification
|
|
38
38
|
import { verifyRuntimeAnnotations } from "../helpers/RuntimeAnnotationVerifier.js";
|
|
39
|
+
// Issue #192: Static source code annotation scanning
|
|
40
|
+
import { StaticAnnotationScanner } from "../helpers/StaticAnnotationScanner.js";
|
|
39
41
|
export class ToolAnnotationAssessor extends BaseAssessor {
|
|
40
42
|
claudeBridge;
|
|
41
43
|
compiledPatterns;
|
|
@@ -108,6 +110,15 @@ export class ToolAnnotationAssessor extends BaseAssessor {
|
|
|
108
110
|
this.logger.debug(`Tool ${detail.toolName}: annotations found at ${detail.location} (${detail.foundHints.join(", ")})`);
|
|
109
111
|
}
|
|
110
112
|
}
|
|
113
|
+
// Issue #192: Scan source code for static annotations
|
|
114
|
+
const staticScanner = new StaticAnnotationScanner();
|
|
115
|
+
const staticScanResult = staticScanner.scan(context.sourceCodeFiles);
|
|
116
|
+
if (staticScanResult.sourceCodeScanned) {
|
|
117
|
+
this.logger.info(`Static annotation scan: ${staticScanResult.annotatedToolCount}/${context.tools.length} tools have annotations in source code (${staticScanResult.scannedFiles.length} files scanned)`);
|
|
118
|
+
if (staticScanResult.parseErrors.length > 0) {
|
|
119
|
+
this.logger.debug(`Static scan parse errors: ${staticScanResult.parseErrors.length} files failed to parse`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
111
122
|
// Issue #57: Detect server architecture
|
|
112
123
|
const architectureContext = {
|
|
113
124
|
tools: context.tools.map((t) => ({
|
|
@@ -138,7 +149,8 @@ export class ToolAnnotationAssessor extends BaseAssessor {
|
|
|
138
149
|
for (const tool of context.tools) {
|
|
139
150
|
this.testCount++;
|
|
140
151
|
// Use extracted assessSingleTool function
|
|
141
|
-
|
|
152
|
+
// Issue #192: Pass static annotations for fallback when MCP annotations missing
|
|
153
|
+
const result = assessSingleTool(tool, this.compiledPatterns, this.persistenceContext, staticScanResult.annotations);
|
|
142
154
|
// Enhance with Claude inference if available
|
|
143
155
|
if (useClaudeInference) {
|
|
144
156
|
const enhancedResult = await enhanceWithClaudeInference(tool, result, this.claudeBridge, this.logger);
|
|
@@ -8,6 +8,7 @@ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
|
8
8
|
import type { ToolAnnotationResult, AssessmentStatus, ToolParamProgress, AnnotationSource } from "../../../../lib/assessmentTypes.js";
|
|
9
9
|
import type { CompiledPatterns, ServerPersistenceContext } from "../../config/annotationPatterns.js";
|
|
10
10
|
import { type PoisoningScanResult } from "./DescriptionPoisoningDetector.js";
|
|
11
|
+
import type { StaticAnnotation } from "../../helpers/StaticAnnotationScanner.js";
|
|
11
12
|
/**
|
|
12
13
|
* Extracted annotation structure from a tool
|
|
13
14
|
*/
|
|
@@ -66,8 +67,13 @@ export declare function extractToolParams(schema: unknown): ToolParamProgress[];
|
|
|
66
67
|
export declare function scanInputSchemaDescriptions(tool: Tool): PoisoningScanResult;
|
|
67
68
|
/**
|
|
68
69
|
* Assess a single tool's annotations
|
|
70
|
+
*
|
|
71
|
+
* @param tool - The tool to assess
|
|
72
|
+
* @param compiledPatterns - Compiled name patterns for behavior inference
|
|
73
|
+
* @param persistenceContext - Server persistence context
|
|
74
|
+
* @param staticAnnotations - Optional map of static annotations from source code (Issue #192)
|
|
69
75
|
*/
|
|
70
|
-
export declare function assessSingleTool(tool: Tool, compiledPatterns: CompiledPatterns, persistenceContext?: ServerPersistenceContext): ToolAnnotationResult;
|
|
76
|
+
export declare function assessSingleTool(tool: Tool, compiledPatterns: CompiledPatterns, persistenceContext?: ServerPersistenceContext, staticAnnotations?: Map<string, StaticAnnotation>): ToolAnnotationResult;
|
|
71
77
|
/**
|
|
72
78
|
* Determine overall status based on tool results
|
|
73
79
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlignmentChecker.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/annotations/AlignmentChecker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAEhB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACV,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"AlignmentChecker.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/annotations/AlignmentChecker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAEhB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACV,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAuF9E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,kBAAkB,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AA0CD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE7D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,oBAAoB,CAkNnE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,GACT,oBAAoB,CAAC,kBAAkB,CAAC,CA6D1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAqBtE;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,CAmD3E;AAqCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,gBAAgB,EAAE,gBAAgB,EAClC,kBAAkB,CAAC,EAAE,wBAAwB,EAC7C,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAChD,oBAAoB,CAgLtB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,UAAU,EAAE,MAAM,GACjB,gBAAgB,CAoClB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,oBAAoB,EAAE,EAC/B,UAAU,EAAE,MAAM,GACjB,sBAAsB,CA2BxB"}
|
|
@@ -324,13 +324,37 @@ function mergePoisoningScanResults(primary, secondary) {
|
|
|
324
324
|
}
|
|
325
325
|
/**
|
|
326
326
|
* Assess a single tool's annotations
|
|
327
|
+
*
|
|
328
|
+
* @param tool - The tool to assess
|
|
329
|
+
* @param compiledPatterns - Compiled name patterns for behavior inference
|
|
330
|
+
* @param persistenceContext - Server persistence context
|
|
331
|
+
* @param staticAnnotations - Optional map of static annotations from source code (Issue #192)
|
|
327
332
|
*/
|
|
328
|
-
export function assessSingleTool(tool, compiledPatterns, persistenceContext) {
|
|
333
|
+
export function assessSingleTool(tool, compiledPatterns, persistenceContext, staticAnnotations) {
|
|
329
334
|
const issues = [];
|
|
330
335
|
const recommendations = [];
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
// First try runtime annotation extraction
|
|
337
|
+
let annotations = extractAnnotations(tool);
|
|
338
|
+
let hasAnnotations = annotations.readOnlyHint !== undefined ||
|
|
333
339
|
annotations.destructiveHint !== undefined;
|
|
340
|
+
// Issue #192: Fall back to static source code annotations if no runtime annotations
|
|
341
|
+
if (!hasAnnotations && staticAnnotations?.has(tool.name)) {
|
|
342
|
+
const staticAnn = staticAnnotations.get(tool.name);
|
|
343
|
+
const hasStaticAnnotations = staticAnn.readOnlyHint !== undefined ||
|
|
344
|
+
staticAnn.destructiveHint !== undefined;
|
|
345
|
+
if (hasStaticAnnotations) {
|
|
346
|
+
annotations = {
|
|
347
|
+
readOnlyHint: staticAnn.readOnlyHint,
|
|
348
|
+
destructiveHint: staticAnn.destructiveHint,
|
|
349
|
+
idempotentHint: staticAnn.idempotentHint,
|
|
350
|
+
openWorldHint: staticAnn.openWorldHint,
|
|
351
|
+
title: annotations.title,
|
|
352
|
+
description: annotations.description,
|
|
353
|
+
source: "source-code",
|
|
354
|
+
};
|
|
355
|
+
hasAnnotations = true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
334
358
|
const inferredBehavior = inferBehavior(tool.name, tool.description, compiledPatterns, persistenceContext);
|
|
335
359
|
let alignmentStatus = "ALIGNED";
|
|
336
360
|
if (!hasAnnotations) {
|
package/client/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.1",
|
|
4
4
|
"description": "Client-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.1",
|
|
4
4
|
"description": "Enhanced MCP Inspector with comprehensive assessment capabilities for server validation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|
|
@@ -132,6 +132,8 @@
|
|
|
132
132
|
"dependencies": {
|
|
133
133
|
"@modelcontextprotocol/conformance": "^0.1.9",
|
|
134
134
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
135
|
+
"acorn": "^8.14.0",
|
|
136
|
+
"acorn-walk": "^8.3.4",
|
|
135
137
|
"commander": "^14.0.2",
|
|
136
138
|
"node-fetch": "^3.3.2",
|
|
137
139
|
"open": "^10.2.0",
|
package/server/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-server",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.1",
|
|
4
4
|
"description": "Server-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|