@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,237 @@
1
+ /**
2
+ * MCP Cancellation Support
3
+ *
4
+ * Enables cancellation of long-running MCP operations.
5
+ * MCP Specification: notifications/cancelled
6
+ *
7
+ * @module mcp/cancellation
8
+ */
9
+ import { EventEmitter } from 'events';
10
+ /**
11
+ * Cancellation Manager
12
+ *
13
+ * Manages cancellable requests and coordinates cancellation with MCP servers.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const manager = new CancellationManager();
18
+ *
19
+ * // Register a request
20
+ * const abortController = new AbortController();
21
+ * manager.register({
22
+ * id: 'req-1',
23
+ * serverName,
24
+ * toolName,
25
+ * startedAt: new Date(),
26
+ * abortController
27
+ * });
28
+ *
29
+ * // Cancel when needed
30
+ * await manager.cancel('req-1', 'User requested cancellation');
31
+ * ```
32
+ */
33
+ export class CancellationManager extends EventEmitter {
34
+ activeRequests = new Map();
35
+ cancelledIds = new Set();
36
+ sendNotification = null;
37
+ /**
38
+ * Set the notification sender function
39
+ * Called by MCPManagerV2 to wire up the notification mechanism
40
+ */
41
+ setSendNotification(fn) {
42
+ this.sendNotification = fn;
43
+ }
44
+ /**
45
+ * Register a cancellable request
46
+ *
47
+ * @param request - Request information
48
+ */
49
+ register(request) {
50
+ this.activeRequests.set(request.id, request);
51
+ this.emit('requestRegistered', request);
52
+ }
53
+ /**
54
+ * Cancel a specific request
55
+ *
56
+ * @param requestId - ID of the request to cancel
57
+ * @param reason - Optional reason for cancellation
58
+ * @returns Promise that resolves when cancellation is processed
59
+ */
60
+ async cancel(requestId, reason) {
61
+ const request = this.activeRequests.get(requestId);
62
+ if (!request) {
63
+ // Already completed or doesn't exist
64
+ return {
65
+ success: false,
66
+ requestId,
67
+ reason: 'Request not found or already completed',
68
+ };
69
+ }
70
+ // Mark as cancelled
71
+ this.cancelledIds.add(requestId);
72
+ // Abort the local request
73
+ request.abortController.abort();
74
+ // Send cancellation notification to server
75
+ if (this.sendNotification) {
76
+ try {
77
+ await this.sendNotification(request.serverName, requestId, reason ?? 'User cancelled');
78
+ }
79
+ catch (error) {
80
+ // Log but don't fail - the local abort is what matters
81
+ console.warn(`Failed to send cancellation notification: ${error}`);
82
+ }
83
+ }
84
+ // Emit cancellation event
85
+ this.emit('requestCancelled', request, reason);
86
+ // Cleanup
87
+ this.cleanup(requestId);
88
+ return {
89
+ success: true,
90
+ requestId,
91
+ reason,
92
+ };
93
+ }
94
+ /**
95
+ * Cancel all active requests
96
+ *
97
+ * @param reason - Optional reason for cancellation
98
+ * @returns Promise that resolves when all cancellations are processed
99
+ */
100
+ async cancelAll(reason) {
101
+ const requests = Array.from(this.activeRequests.values());
102
+ const results = await Promise.all(requests.map((req) => this.cancel(req.id, reason)));
103
+ return results;
104
+ }
105
+ /**
106
+ * Cancel all requests for a specific server
107
+ *
108
+ * @param serverName - Server to cancel requests for
109
+ * @param reason - Optional reason for cancellation
110
+ * @returns Promise that resolves when cancellations are processed
111
+ */
112
+ async cancelByServer(serverName, reason) {
113
+ const requests = Array.from(this.activeRequests.values()).filter((req) => req.serverName === serverName);
114
+ const results = await Promise.all(requests.map((req) => this.cancel(req.id, reason)));
115
+ return results;
116
+ }
117
+ /**
118
+ * Get all active requests
119
+ *
120
+ * @returns Array of active requests
121
+ */
122
+ getActiveRequests() {
123
+ return Array.from(this.activeRequests.values());
124
+ }
125
+ /**
126
+ * Get the most recent active request
127
+ *
128
+ * @returns Most recent request, or undefined if none
129
+ */
130
+ getMostRecentRequest() {
131
+ const requests = this.getActiveRequests();
132
+ if (requests.length === 0) {
133
+ return undefined;
134
+ }
135
+ return requests.reduce((latest, req) => req.startedAt > latest.startedAt ? req : latest);
136
+ }
137
+ /**
138
+ * Check if a request has been cancelled
139
+ *
140
+ * @param requestId - Request ID to check
141
+ * @returns true if request was cancelled
142
+ */
143
+ isCancelled(requestId) {
144
+ return this.cancelledIds.has(requestId);
145
+ }
146
+ /**
147
+ * Check if there are any active requests
148
+ *
149
+ * @returns true if there are active requests
150
+ */
151
+ hasActiveRequests() {
152
+ return this.activeRequests.size > 0;
153
+ }
154
+ /**
155
+ * Get the number of active requests
156
+ *
157
+ * @returns Number of active requests
158
+ */
159
+ getActiveRequestCount() {
160
+ return this.activeRequests.size;
161
+ }
162
+ /**
163
+ * Cleanup a completed or cancelled request
164
+ *
165
+ * @param requestId - Request ID to cleanup
166
+ */
167
+ cleanup(requestId) {
168
+ this.activeRequests.delete(requestId);
169
+ // Keep cancelled IDs briefly to handle race conditions
170
+ setTimeout(() => {
171
+ this.cancelledIds.delete(requestId);
172
+ }, 5000);
173
+ }
174
+ /**
175
+ * Cleanup all tracking
176
+ */
177
+ cleanupAll() {
178
+ for (const requestId of this.activeRequests.keys()) {
179
+ this.cleanup(requestId);
180
+ }
181
+ }
182
+ }
183
+ /**
184
+ * Error code for cancelled requests (JSON-RPC)
185
+ */
186
+ export const CANCELLED_ERROR_CODE = -32800;
187
+ /**
188
+ * Check if an error indicates a cancelled request
189
+ *
190
+ * @param error - Error to check
191
+ * @returns true if error indicates cancellation
192
+ */
193
+ export function isRequestCancelled(error) {
194
+ if (typeof error === 'object' && error !== null) {
195
+ const err = error;
196
+ if (err.code === CANCELLED_ERROR_CODE) {
197
+ return true;
198
+ }
199
+ if (err.message?.toLowerCase().includes('cancelled')) {
200
+ return true;
201
+ }
202
+ }
203
+ return false;
204
+ }
205
+ /**
206
+ * Create an abort error for cancelled requests
207
+ *
208
+ * @param reason - Cancellation reason
209
+ * @returns Error object
210
+ */
211
+ export function createCancellationError(reason) {
212
+ const error = new Error(reason ?? 'Request cancelled');
213
+ error.code = CANCELLED_ERROR_CODE;
214
+ return error;
215
+ }
216
+ // Singleton instance
217
+ let cancellationManager = null;
218
+ /**
219
+ * Get the singleton cancellation manager instance
220
+ */
221
+ export function getCancellationManager() {
222
+ if (!cancellationManager) {
223
+ cancellationManager = new CancellationManager();
224
+ }
225
+ return cancellationManager;
226
+ }
227
+ /**
228
+ * Reset the cancellation manager (for testing)
229
+ */
230
+ export function resetCancellationManager() {
231
+ if (cancellationManager) {
232
+ cancellationManager.cleanupAll();
233
+ cancellationManager.removeAllListeners();
234
+ }
235
+ cancellationManager = null;
236
+ }
237
+ //# sourceMappingURL=cancellation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancellation.js","sourceRoot":"","sources":["../../src/mcp/cancellation.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,21 @@ import { MCPTransport, TransportType } from "./transports.js";
17
17
  import type { MCPServerConfig, MCPTransportConfig } from "../schemas/settings-schemas.js";
18
18
  import { Result } from "./type-safety.js";
19
19
  import { ServerName, ToolName, createServerName } from "./type-safety.js";
20
+ import { type ProgressUpdate, type ProgressCallback } from "./progress.js";
21
+ import { type CancellableRequest, type CancellationResult } from "./cancellation.js";
22
+ import { type ResourceSubscription } from "./subscriptions.js";
23
+ import { type SchemaValidationResult } from "./schema-validator.js";
24
+ export type { ProgressUpdate, ProgressCallback };
25
+ export type { CancellableRequest, CancellationResult };
26
+ export type { ResourceSubscription };
27
+ export type { SchemaValidationResult };
28
+ /**
29
+ * Extended tool result with schema validation
30
+ */
31
+ export interface ValidatedToolResult extends CallToolResult {
32
+ /** Schema validation result (if tool has outputSchema) */
33
+ schemaValidation?: SchemaValidationResult;
34
+ }
20
35
  export type { MCPServerConfig, MCPTransportConfig, ServerName, ToolName };
21
36
  /**
22
37
  * Connection State Machine
@@ -107,6 +122,7 @@ export interface MCPTool {
107
122
  name: ToolName;
108
123
  description: string;
109
124
  inputSchema: any;
125
+ outputSchema?: any;
110
126
  serverName: ServerName;
111
127
  }
112
128
  /**
@@ -174,9 +190,97 @@ export declare class MCPManagerV2 extends EventEmitter {
174
190
  */
175
191
  private disconnectTransport;
176
192
  /**
177
- * Call MCP tool with type safety
193
+ * Call MCP tool with type safety and optional schema validation
194
+ *
195
+ * @param toolName - The tool to call
196
+ * @param arguments_ - Tool arguments
197
+ * @param options - Optional settings for validation
198
+ * @returns Result containing the tool result with optional schema validation
199
+ */
200
+ callTool(toolName: ToolName, arguments_: Record<string, unknown> | null | undefined, options?: {
201
+ validateOutput?: boolean;
202
+ }): Promise<Result<ValidatedToolResult, Error>>;
203
+ /**
204
+ * Call MCP tool with progress tracking
205
+ *
206
+ * MCP Specification: Supports notifications/progress for long-running operations.
207
+ *
208
+ * @param toolName - The tool to call
209
+ * @param arguments_ - Tool arguments
210
+ * @param options - Progress tracking options
211
+ * @returns Result containing the tool result or error
212
+ */
213
+ callToolWithProgress(toolName: ToolName, arguments_: Record<string, unknown> | null | undefined, options?: {
214
+ onProgress?: ProgressCallback;
215
+ }): Promise<Result<CallToolResult, Error>>;
216
+ /**
217
+ * Call MCP tool with cancellation support
218
+ *
219
+ * MCP Specification: Supports notifications/cancelled for aborting operations.
220
+ *
221
+ * @param toolName - The tool to call
222
+ * @param arguments_ - Tool arguments
223
+ * @returns Result containing the tool result, cancellation status, or error
224
+ */
225
+ callToolCancellable(toolName: ToolName, arguments_: Record<string, unknown> | null | undefined): Promise<Result<CallToolResult & {
226
+ isCancelled?: boolean;
227
+ cancelReason?: string;
228
+ }, Error>>;
229
+ /**
230
+ * Cancel the most recent active request
231
+ *
232
+ * @param reason - Optional reason for cancellation
233
+ * @returns Cancellation result
234
+ */
235
+ cancelCurrentRequest(reason?: string): Promise<CancellationResult | undefined>;
236
+ /**
237
+ * Cancel all active requests
238
+ *
239
+ * @param reason - Optional reason for cancellation
240
+ * @returns Array of cancellation results
241
+ */
242
+ cancelAllRequests(reason?: string): Promise<CancellationResult[]>;
243
+ /**
244
+ * Check if there are any active cancellable requests
245
+ */
246
+ hasActiveRequests(): boolean;
247
+ /**
248
+ * Get the count of active cancellable requests
249
+ */
250
+ getActiveRequestCount(): number;
251
+ /**
252
+ * Subscribe to a resource
253
+ *
254
+ * @param serverName - Server providing the resource
255
+ * @param uri - Resource URI to subscribe to
256
+ * @returns Result indicating success or error
257
+ */
258
+ subscribeResource(serverName: ServerName, uri: string): Promise<Result<void, Error>>;
259
+ /**
260
+ * Unsubscribe from a resource
261
+ *
262
+ * @param serverName - Server providing the resource
263
+ * @param uri - Resource URI to unsubscribe from
264
+ * @returns Result indicating success or error
265
+ */
266
+ unsubscribeResource(serverName: ServerName, uri: string): Promise<Result<void, Error>>;
267
+ /**
268
+ * Get all active resource subscriptions
269
+ */
270
+ getResourceSubscriptions(): ResourceSubscription[];
271
+ /**
272
+ * Check if subscribed to a resource
273
+ */
274
+ isSubscribedToResource(serverName: ServerName, uri: string): boolean;
275
+ /**
276
+ * Set up notification handlers for MCP server
277
+ *
278
+ * Handles:
279
+ * - notifications/progress - Progress updates for long-running operations
280
+ * - notifications/resources/updated - Resource change notifications
281
+ * - notifications/resources/list_changed - Resource list changes
178
282
  */
179
- callTool(toolName: ToolName, arguments_: Record<string, unknown> | null | undefined): Promise<Result<CallToolResult, Error>>;
283
+ private setupNotificationHandlers;
180
284
  /**
181
285
  * Truncate text to fit within token limit
182
286
  * UNICODE FIX: Uses grapheme clusters