@codingame/monaco-vscode-mcp-service-override 23.2.2 → 24.1.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/index.js +19 -19
- package/package.json +2 -15
- package/vscode/src/vs/platform/mcp/common/allowedMcpServersService.d.ts +1 -1
- package/vscode/src/vs/platform/mcp/common/allowedMcpServersService.js +2 -2
- package/vscode/src/vs/platform/mcp/common/mcpGalleryService.d.ts +2 -2
- package/vscode/src/vs/platform/mcp/common/mcpGalleryService.js +4 -8
- package/vscode/src/vs/platform/mcp/common/mcpManagementIpc.d.ts +2 -2
- package/vscode/src/vs/platform/mcp/common/mcpManagementService.d.ts +2 -2
- package/vscode/src/vs/platform/mcp/common/mcpManagementService.js +3 -3
- package/vscode/src/vs/platform/mcp/common/mcpResourceScannerService.d.ts +2 -2
- package/vscode/src/vs/platform/mcp/common/mcpResourceScannerService.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcp.contribution.js +8 -8
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpAddContextContribution.d.ts +3 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpAddContextContribution.js +43 -27
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpCommands.js +70 -70
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpElicitationService.d.ts +10 -4
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpElicitationService.js +198 -35
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.js +21 -21
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpMigration.js +9 -9
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpResourceQuickAccess.d.ts +24 -5
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpResourceQuickAccess.js +178 -51
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerActions.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerActions.js +35 -35
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditor.d.ts +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditor.js +37 -37
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditorInput.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerIcons.js +5 -5
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerWidgets.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerWidgets.js +7 -7
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServersView.d.ts +3 -3
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServersView.js +26 -26
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpWorkbenchService.d.ts +3 -3
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpWorkbenchService.js +12 -12
- package/vscode/src/vs/workbench/contrib/mcp/browser/openPanelChatAndGetWidget.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/browser/openPanelChatAndGetWidget.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.js +5 -5
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/installedMcpServersDiscovery.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/workspaceMcpDiscoveryAdapter.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpContextKeys.js +4 -4
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpDevMode.js +4 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpLanguageModelToolContribution.js +20 -14
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpRegistry.js +14 -14
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.d.ts +3 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.js +19 -3
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingLog.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingService.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingService.js +21 -20
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServer.d.ts +6 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServer.js +79 -47
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerConnection.d.ts +3 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerConnection.js +8 -6
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.d.ts +47 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.js +229 -14
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpService.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpTaskManager.d.ts +68 -0
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpTaskManager.js +168 -0
- package/vscode/src/vs/workbench/services/authentication/browser/authenticationMcpService.js +10 -10
- package/vscode/src/vs/workbench/services/mcp/browser/mcpGalleryManifestService.d.ts +17 -2
- package/vscode/src/vs/workbench/services/mcp/browser/mcpGalleryManifestService.js +60 -10
- package/vscode/src/vs/workbench/services/mcp/common/mcpWorkbenchManagementService.d.ts +1 -1
- package/vscode/src/vs/workbench/services/mcp/common/mcpWorkbenchManagementService.js +2 -2
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
|
|
2
2
|
import { equals } from '@codingame/monaco-vscode-api/vscode/vs/base/common/arrays';
|
|
3
3
|
import { assertNever } from '@codingame/monaco-vscode-api/vscode/vs/base/common/assert';
|
|
4
|
-
import { IntervalTimer, DeferredPromise } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
5
|
-
import { CancellationToken } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
4
|
+
import { IntervalTimer, DeferredPromise, disposableTimeout } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
5
|
+
import { CancellationToken, CancellationTokenSource } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
6
6
|
import { CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
7
7
|
import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
8
8
|
import { Iterable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/iterator';
|
|
9
|
-
import { Disposable, DisposableStore } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
9
|
+
import { Disposable, DisposableStore, toDisposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
10
10
|
import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
|
|
11
11
|
import { LogLevel, canLog, log } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log';
|
|
12
12
|
import { IProductService } from '@codingame/monaco-vscode-api/vscode/vs/platform/product/common/productService.service';
|
|
13
13
|
import { McpConnectionState, MpcResponseError, McpError } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
|
|
14
|
+
import { isTaskResult } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypesUtils';
|
|
14
15
|
import { MCP } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/modelContextProtocol';
|
|
15
16
|
import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
|
|
17
|
+
import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
|
|
18
|
+
import { transaction } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/transaction';
|
|
19
|
+
import { ObservablePromise } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/utils/promise';
|
|
16
20
|
|
|
17
21
|
class McpServerRequestHandler extends Disposable {
|
|
18
22
|
set roots(roots) {
|
|
@@ -50,7 +54,15 @@ class McpServerRequestHandler extends Disposable {
|
|
|
50
54
|
capabilities: {
|
|
51
55
|
roots: { listChanged: true },
|
|
52
56
|
sampling: opts.createMessageRequestHandler ? {} : undefined,
|
|
53
|
-
elicitation: opts.elicitationRequestHandler ? {} : undefined,
|
|
57
|
+
elicitation: opts.elicitationRequestHandler ? { form: {}, url: {} } : undefined,
|
|
58
|
+
tasks: {
|
|
59
|
+
list: {},
|
|
60
|
+
cancel: {},
|
|
61
|
+
requests: {
|
|
62
|
+
sampling: opts.createMessageRequestHandler ? { createMessage: {} } : undefined,
|
|
63
|
+
elicitation: opts.elicitationRequestHandler ? { create: {} } : undefined,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
54
66
|
},
|
|
55
67
|
clientInfo: {
|
|
56
68
|
name: productService.nameLong,
|
|
@@ -74,7 +86,7 @@ class McpServerRequestHandler extends Disposable {
|
|
|
74
86
|
store.dispose();
|
|
75
87
|
}
|
|
76
88
|
}
|
|
77
|
-
constructor({ launch, logger, createMessageRequestHandler, elicitationRequestHandler, requestLogLevel = LogLevel.Debug, }) {
|
|
89
|
+
constructor({ launch, logger, createMessageRequestHandler, elicitationRequestHandler, requestLogLevel = LogLevel.Debug, taskManager, }) {
|
|
78
90
|
super();
|
|
79
91
|
this._nextRequestId = 1;
|
|
80
92
|
this._pendingRequests = ( new Map());
|
|
@@ -84,6 +96,8 @@ class McpServerRequestHandler extends Disposable {
|
|
|
84
96
|
this.onDidReceiveCancelledNotification = this._onDidReceiveCancelledNotification.event;
|
|
85
97
|
this._onDidReceiveProgressNotification = this._register(( new Emitter()));
|
|
86
98
|
this.onDidReceiveProgressNotification = this._onDidReceiveProgressNotification.event;
|
|
99
|
+
this._onDidReceiveElicitationCompleteNotification = this._register(( new Emitter()));
|
|
100
|
+
this.onDidReceiveElicitationCompleteNotification = this._onDidReceiveElicitationCompleteNotification.event;
|
|
87
101
|
this._onDidChangeResourceList = this._register(( new Emitter()));
|
|
88
102
|
this.onDidChangeResourceList = this._onDidChangeResourceList.event;
|
|
89
103
|
this._onDidUpdateResource = this._register(( new Emitter()));
|
|
@@ -97,6 +111,16 @@ class McpServerRequestHandler extends Disposable {
|
|
|
97
111
|
this._requestLogLevel = requestLogLevel;
|
|
98
112
|
this._createMessageRequestHandler = createMessageRequestHandler;
|
|
99
113
|
this._elicitationRequestHandler = elicitationRequestHandler;
|
|
114
|
+
this._taskManager = taskManager;
|
|
115
|
+
this._taskManager.setHandler(this);
|
|
116
|
+
this._register(this._taskManager.onDidUpdateTask(task => {
|
|
117
|
+
this.send({
|
|
118
|
+
jsonrpc: MCP.JSONRPC_VERSION,
|
|
119
|
+
method: 'notifications/tasks/status',
|
|
120
|
+
params: task
|
|
121
|
+
});
|
|
122
|
+
}));
|
|
123
|
+
this._register(toDisposable(() => this._taskManager.setHandler(undefined)));
|
|
100
124
|
this._register(launch.onDidReceiveMessage(message => this.handleMessage(message)));
|
|
101
125
|
this._register(autorun(reader => {
|
|
102
126
|
const state = launch.state.read(reader).state;
|
|
@@ -201,10 +225,38 @@ class McpServerRequestHandler extends Disposable {
|
|
|
201
225
|
response = this.handleRootsList(request);
|
|
202
226
|
}
|
|
203
227
|
else if (request.method === 'sampling/createMessage' && this._createMessageRequestHandler) {
|
|
204
|
-
|
|
228
|
+
if (request.params.task) {
|
|
229
|
+
const taskResult = this._taskManager.createTask(request.params.task.ttl ?? null, (token) => this._createMessageRequestHandler(request.params, token));
|
|
230
|
+
taskResult._meta ??= {};
|
|
231
|
+
taskResult._meta['io.modelcontextprotocol/related-task'] = { taskId: taskResult.task.taskId };
|
|
232
|
+
response = taskResult;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
response = await this._createMessageRequestHandler(request.params);
|
|
236
|
+
}
|
|
205
237
|
}
|
|
206
238
|
else if (request.method === 'elicitation/create' && this._elicitationRequestHandler) {
|
|
207
|
-
|
|
239
|
+
if (request.params.task) {
|
|
240
|
+
const taskResult = this._taskManager.createTask(request.params.task.ttl ?? null, (token) => this._elicitationRequestHandler(request.params, token));
|
|
241
|
+
taskResult._meta ??= {};
|
|
242
|
+
taskResult._meta['io.modelcontextprotocol/related-task'] = { taskId: taskResult.task.taskId };
|
|
243
|
+
response = taskResult;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
response = await this._elicitationRequestHandler(request.params);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (request.method === 'tasks/get') {
|
|
250
|
+
response = this._taskManager.getTask(request.params.taskId);
|
|
251
|
+
}
|
|
252
|
+
else if (request.method === 'tasks/result') {
|
|
253
|
+
response = await this._taskManager.getTaskResult(request.params.taskId);
|
|
254
|
+
}
|
|
255
|
+
else if (request.method === 'tasks/cancel') {
|
|
256
|
+
response = this._taskManager.cancelTask(request.params.taskId);
|
|
257
|
+
}
|
|
258
|
+
else if (request.method === 'tasks/list') {
|
|
259
|
+
response = this._taskManager.listTasks();
|
|
208
260
|
}
|
|
209
261
|
else {
|
|
210
262
|
throw McpError.methodNotFound(request.method);
|
|
@@ -250,13 +302,21 @@ class McpServerRequestHandler extends Disposable {
|
|
|
250
302
|
case 'notifications/prompts/list_changed':
|
|
251
303
|
this._onDidChangePromptList.fire();
|
|
252
304
|
return;
|
|
305
|
+
case 'notifications/elicitation/complete':
|
|
306
|
+
this._onDidReceiveElicitationCompleteNotification.fire(request);
|
|
307
|
+
return;
|
|
308
|
+
case 'notifications/tasks/status':
|
|
309
|
+
this._taskManager.getClientTask(request.params.taskId)?.onDidUpdateState(request.params);
|
|
310
|
+
return;
|
|
253
311
|
}
|
|
254
312
|
}
|
|
255
313
|
handleCancelledNotification(request) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
314
|
+
if (request.params.requestId) {
|
|
315
|
+
const pendingRequest = this._pendingRequests.get(request.params.requestId);
|
|
316
|
+
if (pendingRequest) {
|
|
317
|
+
this._pendingRequests.delete(request.params.requestId);
|
|
318
|
+
pendingRequest.promise.cancel();
|
|
319
|
+
}
|
|
260
320
|
}
|
|
261
321
|
}
|
|
262
322
|
handleLoggingNotification(request) {
|
|
@@ -350,8 +410,17 @@ class McpServerRequestHandler extends Disposable {
|
|
|
350
410
|
listTools(params, token) {
|
|
351
411
|
return Iterable.asyncToArrayFlat(this.sendRequestPaginated('tools/list', result => result.tools, params, token));
|
|
352
412
|
}
|
|
353
|
-
callTool(params, token) {
|
|
354
|
-
|
|
413
|
+
async callTool(params, token) {
|
|
414
|
+
const response = await this.sendRequest({ method: 'tools/call', params }, token);
|
|
415
|
+
if (isTaskResult(response)) {
|
|
416
|
+
const task = ( new McpTask(response.task, token));
|
|
417
|
+
this._taskManager.adoptClientTask(task);
|
|
418
|
+
task.setHandler(this);
|
|
419
|
+
return task.result.finally(() => {
|
|
420
|
+
this._taskManager.abandonClientTask(task.id);
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
return response;
|
|
355
424
|
}
|
|
356
425
|
setLevel(params, token) {
|
|
357
426
|
return this.sendRequest({ method: 'logging/setLevel', params }, token);
|
|
@@ -359,6 +428,152 @@ class McpServerRequestHandler extends Disposable {
|
|
|
359
428
|
complete(params, token) {
|
|
360
429
|
return this.sendRequest({ method: 'completion/complete', params }, token);
|
|
361
430
|
}
|
|
431
|
+
getTask(params, token) {
|
|
432
|
+
return this.sendRequest({ method: 'tasks/get', params }, token);
|
|
433
|
+
}
|
|
434
|
+
getTaskResult(params, token) {
|
|
435
|
+
return this.sendRequest({ method: 'tasks/result', params }, token);
|
|
436
|
+
}
|
|
437
|
+
cancelTask(params, token) {
|
|
438
|
+
return this.sendRequest({ method: 'tasks/cancel', params }, token);
|
|
439
|
+
}
|
|
440
|
+
listTasks(params, token) {
|
|
441
|
+
return Iterable.asyncToArrayFlat(this.sendRequestPaginated('tasks/list', result => result.tasks, params, token));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function isTaskInTerminalState(task) {
|
|
445
|
+
return task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled';
|
|
446
|
+
}
|
|
447
|
+
class McpTask extends Disposable {
|
|
448
|
+
get result() {
|
|
449
|
+
return this.promise.p;
|
|
450
|
+
}
|
|
451
|
+
get id() {
|
|
452
|
+
return this._task.taskId;
|
|
453
|
+
}
|
|
454
|
+
constructor(_task, _token = CancellationToken.None) {
|
|
455
|
+
super();
|
|
456
|
+
this._task = _task;
|
|
457
|
+
this.promise = ( new DeferredPromise());
|
|
458
|
+
this._handler = observableValue('mcpTaskHandler', undefined);
|
|
459
|
+
const expiresAt = _task.ttl ? (Date.now() + _task.ttl) : undefined;
|
|
460
|
+
this._lastTaskState = observableValue('lastTaskState', this._task);
|
|
461
|
+
const store = this._register(( new DisposableStore()));
|
|
462
|
+
if (_token.isCancellationRequested) {
|
|
463
|
+
this._lastTaskState.set({ ...this._task, status: 'cancelled' }, undefined);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
store.add(_token.onCancellationRequested(() => {
|
|
467
|
+
const current = this._lastTaskState.get();
|
|
468
|
+
if (!isTaskInTerminalState(current)) {
|
|
469
|
+
this._lastTaskState.set({ ...current, status: 'cancelled' }, undefined);
|
|
470
|
+
}
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
if (expiresAt) {
|
|
474
|
+
const ttlTimeout = expiresAt - Date.now();
|
|
475
|
+
if (ttlTimeout <= 0) {
|
|
476
|
+
this._lastTaskState.set({ ...this._task, status: 'cancelled', statusMessage: 'Task timed out.' }, undefined);
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
store.add(disposableTimeout(() => {
|
|
480
|
+
const current = this._lastTaskState.get();
|
|
481
|
+
if (!isTaskInTerminalState(current)) {
|
|
482
|
+
this._lastTaskState.set({ ...current, status: 'cancelled', statusMessage: 'Task timed out.' }, undefined);
|
|
483
|
+
}
|
|
484
|
+
}, ttlTimeout));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const inputRequiredLookup = observableValue('activeResultLookup', undefined);
|
|
488
|
+
store.add(autorun(reader => {
|
|
489
|
+
const current = this._lastTaskState.read(reader);
|
|
490
|
+
if (isTaskInTerminalState(current)) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const lookup = inputRequiredLookup.read(reader);
|
|
494
|
+
if (lookup) {
|
|
495
|
+
const result = lookup.promiseResult.read(reader);
|
|
496
|
+
return transaction(tx => {
|
|
497
|
+
if (!result) {
|
|
498
|
+
}
|
|
499
|
+
else if (result.data) {
|
|
500
|
+
inputRequiredLookup.set(undefined, tx);
|
|
501
|
+
this._lastTaskState.set(result.data, tx);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
inputRequiredLookup.set(undefined, tx);
|
|
505
|
+
if (result.error instanceof McpError && result.error.code === MCP.INVALID_PARAMS) {
|
|
506
|
+
this._lastTaskState.set({ ...current, status: 'cancelled' }, undefined);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
this._lastTaskState.set({ ...current, status: 'working' }, undefined);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
const handler = this._handler.read(reader);
|
|
515
|
+
if (!handler) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const pollInterval = _task.pollInterval ?? 2000;
|
|
519
|
+
const cts = ( new CancellationTokenSource(_token));
|
|
520
|
+
reader.store.add(toDisposable(() => cts.dispose(true)));
|
|
521
|
+
reader.store.add(disposableTimeout(() => {
|
|
522
|
+
handler.getTask({ taskId: current.taskId }, cts.token)
|
|
523
|
+
.catch((e) => {
|
|
524
|
+
if (e instanceof McpError && e.code === MCP.INVALID_PARAMS) {
|
|
525
|
+
return { ...current, status: 'cancelled' };
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
return { ...current };
|
|
529
|
+
}
|
|
530
|
+
})
|
|
531
|
+
.then(r => {
|
|
532
|
+
if (r && !cts.token.isCancellationRequested) {
|
|
533
|
+
this._lastTaskState.set(r, undefined);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}, pollInterval));
|
|
537
|
+
}));
|
|
538
|
+
const lastStatus = ( this._lastTaskState.map(task => task.status));
|
|
539
|
+
store.add(autorun(reader => {
|
|
540
|
+
const status = lastStatus.read(reader);
|
|
541
|
+
if (status === 'failed') {
|
|
542
|
+
const current = this._lastTaskState.read(undefined);
|
|
543
|
+
this.promise.error(( new Error(
|
|
544
|
+
`Task ${current.taskId} failed: ${current.statusMessage ?? 'unknown error'}`
|
|
545
|
+
)));
|
|
546
|
+
store.dispose();
|
|
547
|
+
}
|
|
548
|
+
else if (status === 'cancelled') {
|
|
549
|
+
this.promise.cancel();
|
|
550
|
+
store.dispose();
|
|
551
|
+
}
|
|
552
|
+
else if (status === 'input_required') {
|
|
553
|
+
const handler = this._handler.read(reader);
|
|
554
|
+
if (handler) {
|
|
555
|
+
const current = this._lastTaskState.read(undefined);
|
|
556
|
+
const cts = ( new CancellationTokenSource(_token));
|
|
557
|
+
reader.store.add(toDisposable(() => cts.dispose(true)));
|
|
558
|
+
inputRequiredLookup.set(( new ObservablePromise(handler.getTask({ taskId: current.taskId }, cts.token))), undefined);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else if (status === 'completed') {
|
|
562
|
+
const handler = this._handler.read(reader);
|
|
563
|
+
if (handler) {
|
|
564
|
+
this.promise.settleWith(handler.getTaskResult({ taskId: _task.taskId }, _token));
|
|
565
|
+
store.dispose();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else ;
|
|
569
|
+
}));
|
|
570
|
+
}
|
|
571
|
+
onDidUpdateState(task) {
|
|
572
|
+
this._lastTaskState.set(task, undefined);
|
|
573
|
+
}
|
|
574
|
+
setHandler(handler) {
|
|
575
|
+
this._handler.set(handler, undefined);
|
|
576
|
+
}
|
|
362
577
|
}
|
|
363
578
|
function mapLogLevelToMcp(logLevel) {
|
|
364
579
|
switch (logLevel) {
|
|
@@ -379,4 +594,4 @@ function mapLogLevelToMcp(logLevel) {
|
|
|
379
594
|
}
|
|
380
595
|
}
|
|
381
596
|
|
|
382
|
-
export { McpServerRequestHandler };
|
|
597
|
+
export { McpServerRequestHandler, McpTask };
|
|
@@ -7,12 +7,12 @@ import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/in
|
|
|
7
7
|
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
|
|
8
8
|
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
|
|
9
9
|
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
10
|
-
import { mcpAutoStartConfig, McpAutoStartValue } from '@codingame/monaco-vscode-
|
|
10
|
+
import { mcpAutoStartConfig, McpAutoStartValue } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpManagement';
|
|
11
11
|
import { StorageScope } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage';
|
|
12
12
|
import { IMcpRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service';
|
|
13
13
|
import { McpServerMetadataCache, McpServer } from './mcpServer.js';
|
|
14
14
|
import { IAutostartResult, McpConnectionState, McpServerCacheState, McpStartServerInteraction, UserInteractionRequiredError, McpServerDefinition, McpToolName } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
|
|
15
|
-
import { startServerAndWaitForLiveTools } from '@codingame/monaco-vscode-
|
|
15
|
+
import { startServerAndWaitForLiveTools } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypesUtils';
|
|
16
16
|
import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
|
|
17
17
|
import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
|
|
18
18
|
import { transaction } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/transaction';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { CancellationToken } from "@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation";
|
|
2
|
+
import { Disposable, IDisposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
|
|
3
|
+
import type { McpServerRequestHandler } from "./mcpServerRequestHandler.js";
|
|
4
|
+
import { MCP } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/modelContextProtocol";
|
|
5
|
+
export interface IMcpTaskInternal extends IDisposable {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
onDidUpdateState(task: MCP.Task): void;
|
|
8
|
+
setHandler(handler: McpServerRequestHandler | undefined): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Manages in-memory task state for server-side MCP tasks (sampling and elicitation).
|
|
12
|
+
* Also tracks client-side tasks to survive handler reconnections.
|
|
13
|
+
* Lifecycle is tied to the McpServer instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare class McpTaskManager extends Disposable {
|
|
16
|
+
private readonly _serverTasks;
|
|
17
|
+
private readonly _clientTasks;
|
|
18
|
+
private readonly _onDidUpdateTask;
|
|
19
|
+
readonly onDidUpdateTask: import("@codingame/monaco-vscode-api/vscode/vs/base/common/event").Event<MCP.Task>;
|
|
20
|
+
/**
|
|
21
|
+
* Attach a new handler to this task manager.
|
|
22
|
+
* Updates all client tasks to use the new handler.
|
|
23
|
+
*/
|
|
24
|
+
setHandler(handler: McpServerRequestHandler | undefined): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get a client task by ID for status notification handling.
|
|
27
|
+
*/
|
|
28
|
+
getClientTask(taskId: string): IMcpTaskInternal | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Track a new client task.
|
|
31
|
+
*/
|
|
32
|
+
adoptClientTask(task: IMcpTaskInternal): void;
|
|
33
|
+
/**
|
|
34
|
+
* Untracks a client task.
|
|
35
|
+
*/
|
|
36
|
+
abandonClientTask(taskId: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Create a new task and execute it asynchronously.
|
|
39
|
+
* Returns the task immediately while execution continues in the background.
|
|
40
|
+
*/
|
|
41
|
+
createTask<TResult extends MCP.Result>(ttl: number | null, executor: (token: CancellationToken) => Promise<TResult>): MCP.CreateTaskResult;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a task asynchronously and update its state.
|
|
44
|
+
*/
|
|
45
|
+
private _executeTask;
|
|
46
|
+
/**
|
|
47
|
+
* Update task status and optionally store result or error.
|
|
48
|
+
*/
|
|
49
|
+
private _updateTaskStatus;
|
|
50
|
+
/**
|
|
51
|
+
* Get the current state of a task.
|
|
52
|
+
* Returns an error if the task doesn't exist or has expired.
|
|
53
|
+
*/
|
|
54
|
+
getTask(taskId: string): MCP.GetTaskResult;
|
|
55
|
+
/**
|
|
56
|
+
* Get the result of a completed task.
|
|
57
|
+
* Blocks until the task completes if it's still in progress.
|
|
58
|
+
*/
|
|
59
|
+
getTaskResult(taskId: string): Promise<MCP.GetTaskPayloadResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Cancel a task.
|
|
62
|
+
*/
|
|
63
|
+
cancelTask(taskId: string): MCP.CancelTaskResult;
|
|
64
|
+
/**
|
|
65
|
+
* List all tasks.
|
|
66
|
+
*/
|
|
67
|
+
listTasks(): MCP.ListTasksResult;
|
|
68
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
|
|
2
|
+
import { disposableTimeout } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
3
|
+
import { CancellationTokenSource } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
4
|
+
import { CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
5
|
+
import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
6
|
+
import { Disposable, DisposableMap, DisposableStore, toDisposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
7
|
+
import { generateUuid } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uuid';
|
|
8
|
+
import { McpError } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
|
|
9
|
+
import { MCP } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/modelContextProtocol';
|
|
10
|
+
|
|
11
|
+
class McpTaskManager extends Disposable {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this._serverTasks = this._register(( new DisposableMap()));
|
|
15
|
+
this._clientTasks = this._register(( new DisposableMap()));
|
|
16
|
+
this._onDidUpdateTask = this._register(( new Emitter()));
|
|
17
|
+
this.onDidUpdateTask = this._onDidUpdateTask.event;
|
|
18
|
+
}
|
|
19
|
+
setHandler(handler) {
|
|
20
|
+
for (const task of ( this._clientTasks.values())) {
|
|
21
|
+
task.setHandler(handler);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
getClientTask(taskId) {
|
|
25
|
+
return this._clientTasks.get(taskId);
|
|
26
|
+
}
|
|
27
|
+
adoptClientTask(task) {
|
|
28
|
+
this._clientTasks.set(task.id, task);
|
|
29
|
+
}
|
|
30
|
+
abandonClientTask(taskId) {
|
|
31
|
+
this._clientTasks.deleteAndDispose(taskId);
|
|
32
|
+
}
|
|
33
|
+
createTask(ttl, executor) {
|
|
34
|
+
const taskId = generateUuid();
|
|
35
|
+
const createdAt = ( new Date()).toISOString();
|
|
36
|
+
const createdAtTime = Date.now();
|
|
37
|
+
const task = {
|
|
38
|
+
taskId,
|
|
39
|
+
status: 'working',
|
|
40
|
+
createdAt,
|
|
41
|
+
ttl,
|
|
42
|
+
pollInterval: 1000,
|
|
43
|
+
};
|
|
44
|
+
const store = ( new DisposableStore());
|
|
45
|
+
const cts = ( new CancellationTokenSource());
|
|
46
|
+
store.add(toDisposable(() => cts.dispose(true)));
|
|
47
|
+
const executionPromise = this._executeTask(taskId, executor, cts.token);
|
|
48
|
+
if (ttl) {
|
|
49
|
+
store.add(disposableTimeout(() => this._serverTasks.deleteAndDispose(taskId), ttl));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
executionPromise.finally(() => {
|
|
53
|
+
const timeout = this._register(disposableTimeout(() => {
|
|
54
|
+
this._serverTasks.deleteAndDispose(taskId);
|
|
55
|
+
this._store.delete(timeout);
|
|
56
|
+
}, 60_000));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
this._serverTasks.set(taskId, {
|
|
60
|
+
task,
|
|
61
|
+
cts,
|
|
62
|
+
dispose: () => store.dispose(),
|
|
63
|
+
createdAtTime,
|
|
64
|
+
executionPromise,
|
|
65
|
+
});
|
|
66
|
+
return { task };
|
|
67
|
+
}
|
|
68
|
+
async _executeTask(taskId, executor, token) {
|
|
69
|
+
try {
|
|
70
|
+
const result = await executor(token);
|
|
71
|
+
this._updateTaskStatus(taskId, 'completed', undefined, result);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof CancellationError) {
|
|
75
|
+
this._updateTaskStatus(taskId, 'cancelled', 'Task was cancelled by the client');
|
|
76
|
+
}
|
|
77
|
+
else if (error instanceof McpError) {
|
|
78
|
+
this._updateTaskStatus(taskId, 'failed', error.message, undefined, {
|
|
79
|
+
code: error.code,
|
|
80
|
+
message: error.message,
|
|
81
|
+
data: error.data,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (error instanceof Error) {
|
|
85
|
+
this._updateTaskStatus(taskId, 'failed', error.message, undefined, {
|
|
86
|
+
code: MCP.INTERNAL_ERROR,
|
|
87
|
+
message: error.message,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this._updateTaskStatus(taskId, 'failed', 'Unknown error', undefined, {
|
|
92
|
+
code: MCP.INTERNAL_ERROR,
|
|
93
|
+
message: 'Unknown error',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
_updateTaskStatus(taskId, status, statusMessage, result, error) {
|
|
99
|
+
const entry = this._serverTasks.get(taskId);
|
|
100
|
+
if (!entry) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
entry.task.status = status;
|
|
104
|
+
if (statusMessage !== undefined) {
|
|
105
|
+
entry.task.statusMessage = statusMessage;
|
|
106
|
+
}
|
|
107
|
+
if (result !== undefined) {
|
|
108
|
+
entry.result = result;
|
|
109
|
+
}
|
|
110
|
+
if (error !== undefined) {
|
|
111
|
+
entry.error = error;
|
|
112
|
+
}
|
|
113
|
+
this._onDidUpdateTask.fire({ ...entry.task });
|
|
114
|
+
}
|
|
115
|
+
getTask(taskId) {
|
|
116
|
+
const entry = this._serverTasks.get(taskId);
|
|
117
|
+
if (!entry) {
|
|
118
|
+
throw ( new McpError(MCP.INVALID_PARAMS, `Task not found: ${taskId}`));
|
|
119
|
+
}
|
|
120
|
+
return { ...entry.task };
|
|
121
|
+
}
|
|
122
|
+
async getTaskResult(taskId) {
|
|
123
|
+
const entry = this._serverTasks.get(taskId);
|
|
124
|
+
if (!entry) {
|
|
125
|
+
throw ( new McpError(MCP.INVALID_PARAMS, `Task not found: ${taskId}`));
|
|
126
|
+
}
|
|
127
|
+
if (entry.task.status === 'working' || entry.task.status === 'input_required') {
|
|
128
|
+
await entry.executionPromise;
|
|
129
|
+
}
|
|
130
|
+
const updatedEntry = this._serverTasks.get(taskId);
|
|
131
|
+
if (!updatedEntry) {
|
|
132
|
+
throw ( new McpError(MCP.INVALID_PARAMS, `Task not found: ${taskId}`));
|
|
133
|
+
}
|
|
134
|
+
if (updatedEntry.error) {
|
|
135
|
+
throw ( new McpError(
|
|
136
|
+
updatedEntry.error.code,
|
|
137
|
+
updatedEntry.error.message,
|
|
138
|
+
updatedEntry.error.data
|
|
139
|
+
));
|
|
140
|
+
}
|
|
141
|
+
if (!updatedEntry.result) {
|
|
142
|
+
throw ( new McpError(MCP.INTERNAL_ERROR, 'Task completed but no result available'));
|
|
143
|
+
}
|
|
144
|
+
return updatedEntry.result;
|
|
145
|
+
}
|
|
146
|
+
cancelTask(taskId) {
|
|
147
|
+
const entry = this._serverTasks.get(taskId);
|
|
148
|
+
if (!entry) {
|
|
149
|
+
throw ( new McpError(MCP.INVALID_PARAMS, `Task not found: ${taskId}`));
|
|
150
|
+
}
|
|
151
|
+
if (entry.task.status === 'completed' || entry.task.status === 'failed' || entry.task.status === 'cancelled') {
|
|
152
|
+
throw ( new McpError(MCP.INVALID_PARAMS, `Cannot cancel task in ${entry.task.status} status`));
|
|
153
|
+
}
|
|
154
|
+
entry.task.status = 'cancelled';
|
|
155
|
+
entry.task.statusMessage = 'Task was cancelled by the client';
|
|
156
|
+
entry.cts.cancel();
|
|
157
|
+
return { ...entry.task };
|
|
158
|
+
}
|
|
159
|
+
listTasks() {
|
|
160
|
+
const tasks = [];
|
|
161
|
+
for (const entry of ( this._serverTasks.values())) {
|
|
162
|
+
tasks.push({ ...entry.task });
|
|
163
|
+
}
|
|
164
|
+
return { tasks };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { McpTaskManager };
|
|
@@ -11,7 +11,7 @@ import '@codingame/monaco-vscode-api/vscode/vs/platform/notification/common/noti
|
|
|
11
11
|
import { IQuickInputService } from '@codingame/monaco-vscode-api/vscode/vs/platform/quickinput/common/quickInput.service';
|
|
12
12
|
import { StorageScope, StorageTarget } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage';
|
|
13
13
|
import { IStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service';
|
|
14
|
-
import { NumberBadge } from '@codingame/monaco-vscode-
|
|
14
|
+
import { NumberBadge } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/activity/common/activity';
|
|
15
15
|
import { IActivityService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/activity/common/activity.service';
|
|
16
16
|
import { IAuthenticationMcpAccessService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpAccessService.service';
|
|
17
17
|
import { IAuthenticationMcpUsageService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpUsageService.service';
|
|
@@ -112,7 +112,7 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
112
112
|
numberOfRequests += ( Object.keys(accessRequest)).length;
|
|
113
113
|
});
|
|
114
114
|
if (numberOfRequests > 0) {
|
|
115
|
-
const badge = ( new NumberBadge(numberOfRequests, () => ( localize(
|
|
115
|
+
const badge = ( new NumberBadge(numberOfRequests, () => ( localize(13669, "Sign in requested"))));
|
|
116
116
|
this._accountBadgeDisposable.value = this.activityService.showAccountsActivity({ badge });
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -173,7 +173,7 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
173
173
|
const { result } = await this.dialogService.prompt({
|
|
174
174
|
type: Severity.Info,
|
|
175
175
|
message: ( localize(
|
|
176
|
-
|
|
176
|
+
13670,
|
|
177
177
|
"The MCP server '{0}' wants to access the {1} account '{2}'.",
|
|
178
178
|
mcpServerName,
|
|
179
179
|
provider.label,
|
|
@@ -181,11 +181,11 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
181
181
|
)),
|
|
182
182
|
buttons: [
|
|
183
183
|
{
|
|
184
|
-
label: ( localize(
|
|
184
|
+
label: ( localize(13671, "&&Allow")),
|
|
185
185
|
run: () => SessionPromptChoice.Allow
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
|
-
label: ( localize(
|
|
188
|
+
label: ( localize(13672, "&&Deny")),
|
|
189
189
|
run: () => SessionPromptChoice.Deny
|
|
190
190
|
}
|
|
191
191
|
],
|
|
@@ -221,16 +221,16 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
221
221
|
items.push({ label: account.label, account });
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
|
-
items.push({ label: ( localize(
|
|
224
|
+
items.push({ label: ( localize(13673, "Sign in to another account")) });
|
|
225
225
|
quickPick.items = items;
|
|
226
226
|
quickPick.title = ( localize(
|
|
227
|
-
|
|
227
|
+
13674,
|
|
228
228
|
"The MCP server '{0}' wants to access a {1} account",
|
|
229
229
|
mcpServerName,
|
|
230
230
|
this._authenticationService.getProvider(providerId).label
|
|
231
231
|
));
|
|
232
232
|
quickPick.placeholder = ( localize(
|
|
233
|
-
|
|
233
|
+
13675,
|
|
234
234
|
"Select an account for '{0}' to use or Esc to cancel",
|
|
235
235
|
mcpServerName
|
|
236
236
|
));
|
|
@@ -302,7 +302,7 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
302
302
|
group: '3_accessRequests',
|
|
303
303
|
command: {
|
|
304
304
|
id: `${providerId}${mcpServerId}Access`,
|
|
305
|
-
title: ( localize(
|
|
305
|
+
title: ( localize(13676, "Grant access to {0} for {1}... (1)", provider.label, mcpServerName))
|
|
306
306
|
}
|
|
307
307
|
}));
|
|
308
308
|
const accessCommand = CommandsRegistry.registerCommand({
|
|
@@ -346,7 +346,7 @@ let AuthenticationMcpService = class AuthenticationMcpService extends Disposable
|
|
|
346
346
|
group: '2_signInRequests',
|
|
347
347
|
command: {
|
|
348
348
|
id: commandId,
|
|
349
|
-
title: ( localize(
|
|
349
|
+
title: ( localize(13677, "Sign in with {0} to use {1} (1)", provider.label, mcpServerName))
|
|
350
350
|
}
|
|
351
351
|
}));
|
|
352
352
|
const signInCommand = CommandsRegistry.registerCommand({
|