@defai.digital/ax-cli 3.14.17 → 3.15.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/README.md +17 -2
- package/ax.config.json +1 -1
- package/dist/commands/setup.js +24 -16
- package/dist/commands/setup.js.map +1 -1
- package/dist/llm/tools.d.ts +1 -0
- package/dist/llm/tools.js +11 -1
- package/dist/llm/tools.js.map +1 -1
- package/dist/mcp/cancellation.d.ts +169 -0
- package/dist/mcp/cancellation.js +237 -0
- package/dist/mcp/cancellation.js.map +1 -0
- package/dist/mcp/client-v2.d.ts +106 -2
- package/dist/mcp/client-v2.js +297 -5
- package/dist/mcp/client-v2.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -0
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/content-length-transport.d.ts +2 -0
- package/dist/mcp/content-length-transport.js +4 -1
- package/dist/mcp/content-length-transport.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/progress.d.ts +147 -0
- package/dist/mcp/progress.js +210 -0
- package/dist/mcp/progress.js.map +1 -0
- package/dist/mcp/schema-validator.d.ts +77 -0
- package/dist/mcp/schema-validator.js +125 -0
- package/dist/mcp/schema-validator.js.map +1 -0
- package/dist/mcp/subscriptions.d.ts +164 -0
- package/dist/mcp/subscriptions.js +242 -0
- package/dist/mcp/subscriptions.js.map +1 -0
- package/dist/mcp/transports.d.ts +3 -0
- package/dist/mcp/transports.js +11 -2
- package/dist/mcp/transports.js.map +1 -1
- package/dist/schemas/settings-schemas.js +3 -0
- package/dist/schemas/settings-schemas.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Progress Notifications
|
|
3
|
+
*
|
|
4
|
+
* Handles progress tracking for long-running MCP operations.
|
|
5
|
+
* MCP Specification: notifications/progress
|
|
6
|
+
*
|
|
7
|
+
* @module mcp/progress
|
|
8
|
+
*/
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
/**
|
|
11
|
+
* Progress update from MCP server
|
|
12
|
+
*/
|
|
13
|
+
export interface ProgressUpdate {
|
|
14
|
+
/** Progress token identifying the operation */
|
|
15
|
+
token: string | number;
|
|
16
|
+
/** Progress value (0.0 - 1.0) */
|
|
17
|
+
progress: number;
|
|
18
|
+
/** Total number of items (optional) */
|
|
19
|
+
total?: number;
|
|
20
|
+
/** Current item number (optional) */
|
|
21
|
+
current?: number;
|
|
22
|
+
/** Status message (optional) */
|
|
23
|
+
message?: string;
|
|
24
|
+
/** Timestamp of this update */
|
|
25
|
+
timestamp: Date;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Progress callback function type
|
|
29
|
+
*/
|
|
30
|
+
export type ProgressCallback = (update: ProgressUpdate) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Progress tracker for MCP operations
|
|
33
|
+
*
|
|
34
|
+
* Manages progress tokens and dispatches progress updates to registered callbacks.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const tracker = new ProgressTracker();
|
|
39
|
+
* const token = tracker.createToken();
|
|
40
|
+
*
|
|
41
|
+
* tracker.onProgress(token, (update) => {
|
|
42
|
+
* console.log(`Progress: ${Math.floor(update.progress * 100)}%`);
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // When notification arrives from server:
|
|
46
|
+
* tracker.handleNotification({ progressToken: token, progress: 0.5 });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare class ProgressTracker extends EventEmitter {
|
|
50
|
+
private callbacks;
|
|
51
|
+
private startTimes;
|
|
52
|
+
private lastUpdates;
|
|
53
|
+
/**
|
|
54
|
+
* Create a unique progress token
|
|
55
|
+
*/
|
|
56
|
+
createToken(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Register a progress callback for a token
|
|
59
|
+
*
|
|
60
|
+
* @param token - Progress token
|
|
61
|
+
* @param callback - Function to call on progress updates
|
|
62
|
+
*/
|
|
63
|
+
onProgress(token: string | number, callback: ProgressCallback): void;
|
|
64
|
+
/**
|
|
65
|
+
* Handle a progress notification from the server
|
|
66
|
+
*
|
|
67
|
+
* @param params - Notification parameters
|
|
68
|
+
*/
|
|
69
|
+
handleNotification(params: {
|
|
70
|
+
progressToken: string | number;
|
|
71
|
+
progress: number;
|
|
72
|
+
total?: number;
|
|
73
|
+
message?: string;
|
|
74
|
+
}): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get elapsed time for a progress token
|
|
77
|
+
*
|
|
78
|
+
* @param token - Progress token
|
|
79
|
+
* @returns Elapsed time in milliseconds, or undefined if token not found
|
|
80
|
+
*/
|
|
81
|
+
getElapsedTime(token: string | number): number | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Get the last progress update for a token
|
|
84
|
+
*
|
|
85
|
+
* @param token - Progress token
|
|
86
|
+
* @returns Last progress update, or undefined if none
|
|
87
|
+
*/
|
|
88
|
+
getLastUpdate(token: string | number): ProgressUpdate | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Check if a token is being tracked
|
|
91
|
+
*
|
|
92
|
+
* @param token - Progress token
|
|
93
|
+
* @returns true if token is registered
|
|
94
|
+
*/
|
|
95
|
+
isTracking(token: string | number): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get all active progress tokens
|
|
98
|
+
*
|
|
99
|
+
* @returns Array of active tokens
|
|
100
|
+
*/
|
|
101
|
+
getActiveTokens(): (string | number)[];
|
|
102
|
+
/**
|
|
103
|
+
* Cleanup a completed progress tracking
|
|
104
|
+
*
|
|
105
|
+
* @param token - Progress token to cleanup
|
|
106
|
+
*/
|
|
107
|
+
cleanup(token: string | number): void;
|
|
108
|
+
/**
|
|
109
|
+
* Cleanup all progress tracking
|
|
110
|
+
*/
|
|
111
|
+
cleanupAll(): void;
|
|
112
|
+
/**
|
|
113
|
+
* Estimate remaining time based on progress
|
|
114
|
+
*
|
|
115
|
+
* @param token - Progress token
|
|
116
|
+
* @returns Estimated remaining time in ms, or undefined if cannot estimate
|
|
117
|
+
*/
|
|
118
|
+
estimateRemainingTime(token: string | number): number | undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Format progress for display
|
|
122
|
+
*
|
|
123
|
+
* @param update - Progress update
|
|
124
|
+
* @param options - Formatting options
|
|
125
|
+
* @returns Formatted progress string
|
|
126
|
+
*/
|
|
127
|
+
export declare function formatProgress(update: ProgressUpdate, options?: {
|
|
128
|
+
width?: number;
|
|
129
|
+
showPercentage?: boolean;
|
|
130
|
+
showCount?: boolean;
|
|
131
|
+
showMessage?: boolean;
|
|
132
|
+
}): string;
|
|
133
|
+
/**
|
|
134
|
+
* Format elapsed time for display
|
|
135
|
+
*
|
|
136
|
+
* @param ms - Time in milliseconds
|
|
137
|
+
* @returns Formatted time string
|
|
138
|
+
*/
|
|
139
|
+
export declare function formatElapsedTime(ms: number): string;
|
|
140
|
+
/**
|
|
141
|
+
* Get the singleton progress tracker instance
|
|
142
|
+
*/
|
|
143
|
+
export declare function getProgressTracker(): ProgressTracker;
|
|
144
|
+
/**
|
|
145
|
+
* Reset the progress tracker (for testing)
|
|
146
|
+
*/
|
|
147
|
+
export declare function resetProgressTracker(): void;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Progress Notifications
|
|
3
|
+
*
|
|
4
|
+
* Handles progress tracking for long-running MCP operations.
|
|
5
|
+
* MCP Specification: notifications/progress
|
|
6
|
+
*
|
|
7
|
+
* @module mcp/progress
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
/**
|
|
12
|
+
* Progress tracker for MCP operations
|
|
13
|
+
*
|
|
14
|
+
* Manages progress tokens and dispatches progress updates to registered callbacks.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const tracker = new ProgressTracker();
|
|
19
|
+
* const token = tracker.createToken();
|
|
20
|
+
*
|
|
21
|
+
* tracker.onProgress(token, (update) => {
|
|
22
|
+
* console.log(`Progress: ${Math.floor(update.progress * 100)}%`);
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // When notification arrives from server:
|
|
26
|
+
* tracker.handleNotification({ progressToken: token, progress: 0.5 });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class ProgressTracker extends EventEmitter {
|
|
30
|
+
callbacks = new Map();
|
|
31
|
+
startTimes = new Map();
|
|
32
|
+
lastUpdates = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Create a unique progress token
|
|
35
|
+
*/
|
|
36
|
+
createToken() {
|
|
37
|
+
return randomUUID();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Register a progress callback for a token
|
|
41
|
+
*
|
|
42
|
+
* @param token - Progress token
|
|
43
|
+
* @param callback - Function to call on progress updates
|
|
44
|
+
*/
|
|
45
|
+
onProgress(token, callback) {
|
|
46
|
+
this.callbacks.set(token, callback);
|
|
47
|
+
this.startTimes.set(token, Date.now());
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Handle a progress notification from the server
|
|
51
|
+
*
|
|
52
|
+
* @param params - Notification parameters
|
|
53
|
+
*/
|
|
54
|
+
handleNotification(params) {
|
|
55
|
+
const callback = this.callbacks.get(params.progressToken);
|
|
56
|
+
const update = {
|
|
57
|
+
token: params.progressToken,
|
|
58
|
+
progress: Math.max(0, Math.min(1, params.progress)), // Clamp to 0-1
|
|
59
|
+
total: params.total,
|
|
60
|
+
current: params.total ? Math.floor(params.progress * params.total) : undefined,
|
|
61
|
+
message: params.message,
|
|
62
|
+
timestamp: new Date(),
|
|
63
|
+
};
|
|
64
|
+
// Store last update
|
|
65
|
+
this.lastUpdates.set(params.progressToken, update);
|
|
66
|
+
// Call registered callback
|
|
67
|
+
if (callback) {
|
|
68
|
+
callback(update);
|
|
69
|
+
}
|
|
70
|
+
// Emit event for global listeners
|
|
71
|
+
this.emit('progress', update);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get elapsed time for a progress token
|
|
75
|
+
*
|
|
76
|
+
* @param token - Progress token
|
|
77
|
+
* @returns Elapsed time in milliseconds, or undefined if token not found
|
|
78
|
+
*/
|
|
79
|
+
getElapsedTime(token) {
|
|
80
|
+
const start = this.startTimes.get(token);
|
|
81
|
+
return start ? Date.now() - start : undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the last progress update for a token
|
|
85
|
+
*
|
|
86
|
+
* @param token - Progress token
|
|
87
|
+
* @returns Last progress update, or undefined if none
|
|
88
|
+
*/
|
|
89
|
+
getLastUpdate(token) {
|
|
90
|
+
return this.lastUpdates.get(token);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if a token is being tracked
|
|
94
|
+
*
|
|
95
|
+
* @param token - Progress token
|
|
96
|
+
* @returns true if token is registered
|
|
97
|
+
*/
|
|
98
|
+
isTracking(token) {
|
|
99
|
+
return this.callbacks.has(token);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all active progress tokens
|
|
103
|
+
*
|
|
104
|
+
* @returns Array of active tokens
|
|
105
|
+
*/
|
|
106
|
+
getActiveTokens() {
|
|
107
|
+
return Array.from(this.callbacks.keys());
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Cleanup a completed progress tracking
|
|
111
|
+
*
|
|
112
|
+
* @param token - Progress token to cleanup
|
|
113
|
+
*/
|
|
114
|
+
cleanup(token) {
|
|
115
|
+
this.callbacks.delete(token);
|
|
116
|
+
this.startTimes.delete(token);
|
|
117
|
+
// Keep last update briefly for final status queries
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
this.lastUpdates.delete(token);
|
|
120
|
+
}, 5000);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Cleanup all progress tracking
|
|
124
|
+
*/
|
|
125
|
+
cleanupAll() {
|
|
126
|
+
const tokens = this.getActiveTokens();
|
|
127
|
+
for (const token of tokens) {
|
|
128
|
+
this.cleanup(token);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Estimate remaining time based on progress
|
|
133
|
+
*
|
|
134
|
+
* @param token - Progress token
|
|
135
|
+
* @returns Estimated remaining time in ms, or undefined if cannot estimate
|
|
136
|
+
*/
|
|
137
|
+
estimateRemainingTime(token) {
|
|
138
|
+
const elapsed = this.getElapsedTime(token);
|
|
139
|
+
const update = this.getLastUpdate(token);
|
|
140
|
+
if (!elapsed || !update || update.progress <= 0 || update.progress >= 1) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const totalEstimate = elapsed / update.progress;
|
|
144
|
+
return Math.max(0, totalEstimate - elapsed);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Format progress for display
|
|
149
|
+
*
|
|
150
|
+
* @param update - Progress update
|
|
151
|
+
* @param options - Formatting options
|
|
152
|
+
* @returns Formatted progress string
|
|
153
|
+
*/
|
|
154
|
+
export function formatProgress(update, options = {}) {
|
|
155
|
+
const { width = 30, showPercentage = true, showCount = true, showMessage = true, } = options;
|
|
156
|
+
const percentage = Math.floor(update.progress * 100);
|
|
157
|
+
const filled = Math.floor(update.progress * width);
|
|
158
|
+
const empty = width - filled;
|
|
159
|
+
let result = `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
|
|
160
|
+
if (showPercentage) {
|
|
161
|
+
result += ` ${percentage}%`;
|
|
162
|
+
}
|
|
163
|
+
if (showCount && update.total !== undefined && update.current !== undefined) {
|
|
164
|
+
result += ` (${update.current}/${update.total})`;
|
|
165
|
+
}
|
|
166
|
+
if (showMessage && update.message) {
|
|
167
|
+
result += `\n${update.message}`;
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Format elapsed time for display
|
|
173
|
+
*
|
|
174
|
+
* @param ms - Time in milliseconds
|
|
175
|
+
* @returns Formatted time string
|
|
176
|
+
*/
|
|
177
|
+
export function formatElapsedTime(ms) {
|
|
178
|
+
if (ms < 1000) {
|
|
179
|
+
return `${ms}ms`;
|
|
180
|
+
}
|
|
181
|
+
const seconds = Math.floor(ms / 1000);
|
|
182
|
+
if (seconds < 60) {
|
|
183
|
+
return `${seconds}s`;
|
|
184
|
+
}
|
|
185
|
+
const minutes = Math.floor(seconds / 60);
|
|
186
|
+
const remainingSeconds = seconds % 60;
|
|
187
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
188
|
+
}
|
|
189
|
+
// Singleton instance
|
|
190
|
+
let progressTracker = null;
|
|
191
|
+
/**
|
|
192
|
+
* Get the singleton progress tracker instance
|
|
193
|
+
*/
|
|
194
|
+
export function getProgressTracker() {
|
|
195
|
+
if (!progressTracker) {
|
|
196
|
+
progressTracker = new ProgressTracker();
|
|
197
|
+
}
|
|
198
|
+
return progressTracker;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Reset the progress tracker (for testing)
|
|
202
|
+
*/
|
|
203
|
+
export function resetProgressTracker() {
|
|
204
|
+
if (progressTracker) {
|
|
205
|
+
progressTracker.cleanupAll();
|
|
206
|
+
progressTracker.removeAllListeners();
|
|
207
|
+
}
|
|
208
|
+
progressTracker = null;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/mcp/progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Output Schema Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates tool outputs against their declared JSON schemas using Ajv.
|
|
5
|
+
* Uses the MCP SDK's AjvJsonSchemaValidator for consistency.
|
|
6
|
+
*
|
|
7
|
+
* MCP Specification: Tool Output Schemas (2025-06-18)
|
|
8
|
+
*
|
|
9
|
+
* @module mcp/schema-validator
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Schema validation result status
|
|
13
|
+
*/
|
|
14
|
+
export type SchemaValidationStatus = 'valid' | 'invalid' | 'no-schema';
|
|
15
|
+
/**
|
|
16
|
+
* Schema validation result
|
|
17
|
+
*/
|
|
18
|
+
export interface SchemaValidationResult {
|
|
19
|
+
/** Validation status */
|
|
20
|
+
status: SchemaValidationStatus;
|
|
21
|
+
/** Validation errors (if status is 'invalid') */
|
|
22
|
+
errors?: string[];
|
|
23
|
+
/** The schema that was used for validation */
|
|
24
|
+
schema?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Tool Output Schema Validator
|
|
28
|
+
*
|
|
29
|
+
* Validates MCP tool outputs against their declared JSON schemas.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const validator = new ToolOutputValidator();
|
|
34
|
+
*
|
|
35
|
+
* // Validate tool output
|
|
36
|
+
* const result = validator.validate(outputSchema, toolOutput);
|
|
37
|
+
*
|
|
38
|
+
* if (result.status === 'invalid') {
|
|
39
|
+
* console.warn('Tool output validation failed:', result.errors);
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class ToolOutputValidator {
|
|
44
|
+
private validator;
|
|
45
|
+
constructor();
|
|
46
|
+
/**
|
|
47
|
+
* Validate tool output against schema
|
|
48
|
+
*
|
|
49
|
+
* @param schema - JSON schema to validate against (or undefined if no schema)
|
|
50
|
+
* @param output - Tool output to validate
|
|
51
|
+
* @returns Validation result
|
|
52
|
+
*/
|
|
53
|
+
validate(schema: unknown, output: unknown): SchemaValidationResult;
|
|
54
|
+
/**
|
|
55
|
+
* Validate tool output content array
|
|
56
|
+
*
|
|
57
|
+
* MCP tool results return an array of content items.
|
|
58
|
+
* This method validates the extracted content.
|
|
59
|
+
*
|
|
60
|
+
* @param schema - JSON schema
|
|
61
|
+
* @param content - MCP tool result content array
|
|
62
|
+
* @returns Validation result
|
|
63
|
+
*/
|
|
64
|
+
validateContent(schema: unknown, content: Array<{
|
|
65
|
+
type: string;
|
|
66
|
+
text?: string;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}>): SchemaValidationResult;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the singleton validator instance
|
|
72
|
+
*/
|
|
73
|
+
export declare function getToolOutputValidator(): ToolOutputValidator;
|
|
74
|
+
/**
|
|
75
|
+
* Reset the validator (for testing)
|
|
76
|
+
*/
|
|
77
|
+
export declare function resetToolOutputValidator(): void;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Output Schema Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates tool outputs against their declared JSON schemas using Ajv.
|
|
5
|
+
* Uses the MCP SDK's AjvJsonSchemaValidator for consistency.
|
|
6
|
+
*
|
|
7
|
+
* MCP Specification: Tool Output Schemas (2025-06-18)
|
|
8
|
+
*
|
|
9
|
+
* @module mcp/schema-validator
|
|
10
|
+
*/
|
|
11
|
+
import { AjvJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/ajv';
|
|
12
|
+
/**
|
|
13
|
+
* Tool Output Schema Validator
|
|
14
|
+
*
|
|
15
|
+
* Validates MCP tool outputs against their declared JSON schemas.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const validator = new ToolOutputValidator();
|
|
20
|
+
*
|
|
21
|
+
* // Validate tool output
|
|
22
|
+
* const result = validator.validate(outputSchema, toolOutput);
|
|
23
|
+
*
|
|
24
|
+
* if (result.status === 'invalid') {
|
|
25
|
+
* console.warn('Tool output validation failed:', result.errors);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class ToolOutputValidator {
|
|
30
|
+
validator;
|
|
31
|
+
constructor() {
|
|
32
|
+
this.validator = new AjvJsonSchemaValidator();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate tool output against schema
|
|
36
|
+
*
|
|
37
|
+
* @param schema - JSON schema to validate against (or undefined if no schema)
|
|
38
|
+
* @param output - Tool output to validate
|
|
39
|
+
* @returns Validation result
|
|
40
|
+
*/
|
|
41
|
+
validate(schema, output) {
|
|
42
|
+
// No schema defined
|
|
43
|
+
if (schema === undefined || schema === null) {
|
|
44
|
+
return { status: 'no-schema' };
|
|
45
|
+
}
|
|
46
|
+
// Empty schema (matches anything)
|
|
47
|
+
if (typeof schema === 'object' && Object.keys(schema).length === 0) {
|
|
48
|
+
return { status: 'valid', schema };
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
// Use the SDK's validator - getValidator returns a function
|
|
52
|
+
const validateFn = this.validator.getValidator(schema);
|
|
53
|
+
const result = validateFn(output);
|
|
54
|
+
if (result.valid) {
|
|
55
|
+
return { status: 'valid', schema };
|
|
56
|
+
}
|
|
57
|
+
// Extract error message
|
|
58
|
+
return {
|
|
59
|
+
status: 'invalid',
|
|
60
|
+
errors: result.errorMessage ? [result.errorMessage] : ['Validation failed'],
|
|
61
|
+
schema,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Schema compilation error
|
|
66
|
+
return {
|
|
67
|
+
status: 'invalid',
|
|
68
|
+
errors: [`Schema compilation error: ${error instanceof Error ? error.message : String(error)}`],
|
|
69
|
+
schema,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate tool output content array
|
|
75
|
+
*
|
|
76
|
+
* MCP tool results return an array of content items.
|
|
77
|
+
* This method validates the extracted content.
|
|
78
|
+
*
|
|
79
|
+
* @param schema - JSON schema
|
|
80
|
+
* @param content - MCP tool result content array
|
|
81
|
+
* @returns Validation result
|
|
82
|
+
*/
|
|
83
|
+
validateContent(schema, content) {
|
|
84
|
+
if (schema === undefined || schema === null) {
|
|
85
|
+
return { status: 'no-schema' };
|
|
86
|
+
}
|
|
87
|
+
// Extract text content and try to parse as JSON
|
|
88
|
+
const textContent = content
|
|
89
|
+
.filter((item) => item.type === 'text' && item.text)
|
|
90
|
+
.map((item) => item.text)
|
|
91
|
+
.join('');
|
|
92
|
+
if (!textContent) {
|
|
93
|
+
// No text content to validate
|
|
94
|
+
return { status: 'valid', schema };
|
|
95
|
+
}
|
|
96
|
+
// Try to parse as JSON
|
|
97
|
+
let parsedOutput;
|
|
98
|
+
try {
|
|
99
|
+
parsedOutput = JSON.parse(textContent);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Not JSON - validate as string if schema allows
|
|
103
|
+
parsedOutput = textContent;
|
|
104
|
+
}
|
|
105
|
+
return this.validate(schema, parsedOutput);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Singleton instance
|
|
109
|
+
let validatorInstance = null;
|
|
110
|
+
/**
|
|
111
|
+
* Get the singleton validator instance
|
|
112
|
+
*/
|
|
113
|
+
export function getToolOutputValidator() {
|
|
114
|
+
if (!validatorInstance) {
|
|
115
|
+
validatorInstance = new ToolOutputValidator();
|
|
116
|
+
}
|
|
117
|
+
return validatorInstance;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reset the validator (for testing)
|
|
121
|
+
*/
|
|
122
|
+
export function resetToolOutputValidator() {
|
|
123
|
+
validatorInstance = null;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=schema-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../src/mcp/schema-validator.ts"],"names":[],"mappings":"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"}
|