@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.
@@ -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":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAyBtC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,eAAgB,SAAQ,YAAY;IACvC,SAAS,GAAG,IAAI,GAAG,EAAqC,CAAC;IACzD,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAC;IAChD,WAAW,GAAG,IAAI,GAAG,EAAmC,CAAC;IAEjE;;OAEG;IACH,WAAW;QACT,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,KAAsB,EAAE,QAA0B;QAC3D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,MAKlB;QACC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAE1D,MAAM,MAAM,GAAmB;YAC7B,KAAK,EAAE,MAAM,CAAC,aAAa;YAC3B,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,eAAe;YACpE,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YAC9E,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;QAEF,oBAAoB;QACpB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAEnD,2BAA2B;QAC3B,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAED,kCAAkC;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,KAAsB;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,KAAsB;QAClC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,KAAsB;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAsB;QAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9B,oDAAoD;QACpD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC;IAED;;OAEG;IACH,UAAU;QACR,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,KAAsB;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAEzC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YACxE,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC;IAC9C,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAsB,EACtB,UAKI,EAAE;IAEN,MAAM,EACJ,KAAK,GAAG,EAAE,EACV,cAAc,GAAG,IAAI,EACrB,SAAS,GAAG,IAAI,EAChB,WAAW,GAAG,IAAI,GACnB,GAAG,OAAO,CAAC;IAEZ,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAE7B,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;IAE3D,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,IAAI,IAAI,UAAU,GAAG,CAAC;IAC9B,CAAC;IAED,IAAI,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC;IACnD,CAAC;IAED,IAAI,WAAW,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,EAAE,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,GAAG,CAAC;IACvB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,gBAAgB,GAAG,OAAO,GAAG,EAAE,CAAC;IACtC,OAAO,GAAG,OAAO,KAAK,gBAAgB,GAAG,CAAC;AAC5C,CAAC;AAED,qBAAqB;AACrB,IAAI,eAAe,GAA2B,IAAI,CAAC;AAEnD;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,eAAe,EAAE,CAAC;QACpB,eAAe,CAAC,UAAU,EAAE,CAAC;QAC7B,eAAe,CAAC,kBAAkB,EAAE,CAAC;IACvC,CAAC;IACD,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC"}
@@ -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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"}