@codingame/monaco-vscode-mcp-service-override 37.1.0 → 37.2.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
CHANGED
|
@@ -29,6 +29,7 @@ import { IWorkbenchMcpGatewayService } from '@codingame/monaco-vscode-api/vscode
|
|
|
29
29
|
import { BrowserMcpGatewayService } from './vscode/src/vs/workbench/contrib/mcp/browser/mcpGatewayService.js';
|
|
30
30
|
import './vscode/src/vs/workbench/contrib/mcp/browser/mcp.contribution.js';
|
|
31
31
|
import './vscode/src/vs/workbench/contrib/mcp/browser/mcp.view.contribution.js';
|
|
32
|
+
import './vscode/src/vs/workbench/api/browser/mainThreadMcp.js';
|
|
32
33
|
|
|
33
34
|
function getServiceOverride() {
|
|
34
35
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codingame/monaco-vscode-mcp-service-override",
|
|
3
|
-
"version": "37.
|
|
3
|
+
"version": "37.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "VSCode public API plugged on the monaco editor - mcp service-override",
|
|
6
6
|
"keywords": [],
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@codingame/monaco-vscode-api": "37.
|
|
18
|
+
"@codingame/monaco-vscode-api": "37.2.0"
|
|
19
19
|
},
|
|
20
20
|
"main": "index.js",
|
|
21
21
|
"module": "index.js",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Disposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
|
|
2
|
+
import { URI, UriComponents } from "@codingame/monaco-vscode-api/vscode/vs/base/common/uri";
|
|
3
|
+
import { IContextKeyService } from "@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service";
|
|
4
|
+
import { IConfigurationService } from "@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service";
|
|
5
|
+
import { IDialogService } from "@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service";
|
|
6
|
+
import { LogLevel } from "@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log";
|
|
7
|
+
import { ITelemetryService } from "@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service";
|
|
8
|
+
import { ISecretStorageService } from "@codingame/monaco-vscode-api/vscode/vs/platform/secrets/common/secrets.service";
|
|
9
|
+
import { IWorkbenchMcpGatewayService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpGatewayService.service";
|
|
10
|
+
import { IMcpRegistry } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service";
|
|
11
|
+
import { McpCollectionDefinition, McpConnectionState, McpServerDefinition } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes";
|
|
12
|
+
import { IAuthenticationMcpAccessService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpAccessService.service";
|
|
13
|
+
import { IAuthenticationMcpService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpService.service";
|
|
14
|
+
import { IAuthenticationMcpUsageService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpUsageService.service";
|
|
15
|
+
import { IAuthenticationService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/authentication.service";
|
|
16
|
+
import { IDynamicAuthenticationProviderStorageService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/dynamicAuthenticationProviderStorage.service";
|
|
17
|
+
import { IExtensionService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service";
|
|
18
|
+
import { IExtHostContext } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extHostCustomers";
|
|
19
|
+
import { IMcpAuthenticationDetails, IMcpAuthenticationOptions, IAuthMetadataSource, MainThreadMcpShape } from "@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol";
|
|
20
|
+
export declare class MainThreadMcp extends Disposable implements MainThreadMcpShape {
|
|
21
|
+
private readonly _extHostContext;
|
|
22
|
+
private readonly _mcpRegistry;
|
|
23
|
+
private readonly dialogService;
|
|
24
|
+
private readonly _authenticationService;
|
|
25
|
+
private readonly authenticationMcpServersService;
|
|
26
|
+
private readonly authenticationMCPServerAccessService;
|
|
27
|
+
private readonly authenticationMCPServerUsageService;
|
|
28
|
+
private readonly _dynamicAuthenticationProviderStorageService;
|
|
29
|
+
private readonly _extensionService;
|
|
30
|
+
private readonly _contextKeyService;
|
|
31
|
+
private readonly _telemetryService;
|
|
32
|
+
private readonly _mcpGatewayService;
|
|
33
|
+
private readonly _configurationService;
|
|
34
|
+
private readonly _secretStorageService;
|
|
35
|
+
private _serverIdCounter;
|
|
36
|
+
private readonly _servers;
|
|
37
|
+
private readonly _serverDefinitions;
|
|
38
|
+
private readonly _serverAuthTracking;
|
|
39
|
+
private readonly _proxy;
|
|
40
|
+
private readonly _collectionDefinitions;
|
|
41
|
+
private readonly _gateways;
|
|
42
|
+
constructor(_extHostContext: IExtHostContext, _mcpRegistry: IMcpRegistry, dialogService: IDialogService, _authenticationService: IAuthenticationService, authenticationMcpServersService: IAuthenticationMcpService, authenticationMCPServerAccessService: IAuthenticationMcpAccessService, authenticationMCPServerUsageService: IAuthenticationMcpUsageService, _dynamicAuthenticationProviderStorageService: IDynamicAuthenticationProviderStorageService, _extensionService: IExtensionService, _contextKeyService: IContextKeyService, _telemetryService: ITelemetryService, _mcpGatewayService: IWorkbenchMcpGatewayService, _configurationService: IConfigurationService, _secretStorageService: ISecretStorageService);
|
|
43
|
+
private _publishServerDefinitions;
|
|
44
|
+
$upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, serversDto: McpServerDefinition.Serialized[]): void;
|
|
45
|
+
$deleteMcpCollection(collectionId: string): void;
|
|
46
|
+
$onDidChangeState(id: number, update: McpConnectionState): void;
|
|
47
|
+
$onDidPublishLog(id: number, level: LogLevel, log: string): void;
|
|
48
|
+
$onDidReceiveMessage(id: number, message: string): void;
|
|
49
|
+
$getTokenForProviderId(id: number, providerId: string, scopes: string[], options?: IMcpAuthenticationOptions): Promise<string | undefined>;
|
|
50
|
+
$getTokenFromServerMetadata(id: number, authDetails: IMcpAuthenticationDetails, { errorOnUserInteraction, forceNewRegistration, clientId }?: IMcpAuthenticationOptions): Promise<string | undefined>;
|
|
51
|
+
private _ensureXaaIssuer;
|
|
52
|
+
private _getSessionForProvider;
|
|
53
|
+
private continueWithIncorrectAccountPrompt;
|
|
54
|
+
private _onDidChangeAuthSessions;
|
|
55
|
+
$logMcpAuthSetup(data: IAuthMetadataSource): void;
|
|
56
|
+
$startMcpGateway(chatSessionResource?: UriComponents): Promise<{
|
|
57
|
+
servers: {
|
|
58
|
+
label: string;
|
|
59
|
+
address: URI;
|
|
60
|
+
}[];
|
|
61
|
+
gatewayId: string;
|
|
62
|
+
} | undefined>;
|
|
63
|
+
$disposeMcpGateway(gatewayId: string): void;
|
|
64
|
+
private loginPrompt;
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The context needed to re-acquire a token for a tracked MCP server, captured when the
|
|
69
|
+
* session was first established. The tracker holds this opaquely and replays it verbatim on
|
|
70
|
+
* re-validation so the silent token request targets the same authority / resource / audience
|
|
71
|
+
* that the original sign-in used. Dropping the authorization server here would let the provider
|
|
72
|
+
* fall back to a default authority (e.g. the Microsoft provider's `organizations` tenant) and
|
|
73
|
+
* request a token against the wrong tenant.
|
|
74
|
+
*/
|
|
75
|
+
export interface IMcpServerAuthContext {
|
|
76
|
+
readonly authorizationServer?: URI;
|
|
77
|
+
readonly clientId?: string;
|
|
78
|
+
readonly resource?: string;
|
|
79
|
+
readonly audience?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Tracks which MCP servers are using which authentication providers.
|
|
83
|
+
* Organized by provider ID for efficient lookup when auth sessions change.
|
|
84
|
+
*/
|
|
85
|
+
export declare class McpServerAuthTracker {
|
|
86
|
+
private readonly _tracking;
|
|
87
|
+
/**
|
|
88
|
+
* Track authentication for a server with a specific provider.
|
|
89
|
+
* Replaces any existing tracking for this server/provider combination.
|
|
90
|
+
*/
|
|
91
|
+
track(providerId: string, serverId: number, scopes: string[], context: IMcpServerAuthContext): void;
|
|
92
|
+
/**
|
|
93
|
+
* Remove all authentication tracking for a server across all providers.
|
|
94
|
+
*/
|
|
95
|
+
untrack(serverId: number): void;
|
|
96
|
+
/**
|
|
97
|
+
* Get all servers using a specific authentication provider.
|
|
98
|
+
*/
|
|
99
|
+
get(providerId: string): ReadonlyArray<{
|
|
100
|
+
serverId: number;
|
|
101
|
+
scopes: string[];
|
|
102
|
+
context: IMcpServerAuthContext;
|
|
103
|
+
}> | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Clear all tracking data.
|
|
106
|
+
*/
|
|
107
|
+
clear(): void;
|
|
108
|
+
}
|
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
|
|
2
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
3
|
+
import { mapFindFirst } from '@codingame/monaco-vscode-api/vscode/vs/base/common/arraysFind';
|
|
4
|
+
import { RunOnceScheduler, disposableTimeout } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
5
|
+
import { CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
6
|
+
import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
7
|
+
import { Disposable, DisposableMap, DisposableStore, MutableDisposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
8
|
+
import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
|
|
9
|
+
import Severity from '@codingame/monaco-vscode-api/vscode/vs/base/common/severity';
|
|
10
|
+
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
|
|
11
|
+
import { generateUuid } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uuid';
|
|
12
|
+
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
|
|
13
|
+
import { ContextKeyExpr } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey';
|
|
14
|
+
import { IContextKeyService } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service';
|
|
15
|
+
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
|
|
16
|
+
import { IDialogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service';
|
|
17
|
+
import { ExtensionIdentifier } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensions/common/extensions';
|
|
18
|
+
import { LogLevel } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log';
|
|
19
|
+
import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
|
|
20
|
+
import { ISecretStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/secrets/common/secrets.service';
|
|
21
|
+
import { IWorkbenchMcpGatewayService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpGatewayService.service';
|
|
22
|
+
import { IMcpRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service';
|
|
23
|
+
import { McpServerLaunch, McpServerTransportType, McpServerDefinition, McpServerTrust, McpCollectionSortOrder, McpCollectionProvenance, extensionPrefixedIdentifier, McpConnectionState, mcpOAuthClientSecretStorageKey, UserInteractionRequiredError } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
|
|
24
|
+
import { mcpEnterpriseManagedAuthIdpSection } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpConfiguration';
|
|
25
|
+
import { IAuthenticationMcpAccessService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpAccessService.service';
|
|
26
|
+
import { IAuthenticationMcpService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpService.service';
|
|
27
|
+
import { IAuthenticationMcpUsageService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpUsageService.service';
|
|
28
|
+
import { IAuthenticationService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/authentication.service';
|
|
29
|
+
import { IDynamicAuthenticationProviderStorageService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/dynamicAuthenticationProviderStorage.service';
|
|
30
|
+
import { ExtensionHostKind, extensionHostKindToString } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionHostKind';
|
|
31
|
+
import { IExtensionService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service';
|
|
32
|
+
import { extHostNamedCustomer } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extHostCustomers';
|
|
33
|
+
import { ExtHostContext, MainContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol';
|
|
34
|
+
import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
|
|
35
|
+
import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
|
|
36
|
+
|
|
37
|
+
let MainThreadMcp = class MainThreadMcp extends Disposable {
|
|
38
|
+
constructor(
|
|
39
|
+
_extHostContext,
|
|
40
|
+
_mcpRegistry,
|
|
41
|
+
dialogService,
|
|
42
|
+
_authenticationService,
|
|
43
|
+
authenticationMcpServersService,
|
|
44
|
+
authenticationMCPServerAccessService,
|
|
45
|
+
authenticationMCPServerUsageService,
|
|
46
|
+
_dynamicAuthenticationProviderStorageService,
|
|
47
|
+
_extensionService,
|
|
48
|
+
_contextKeyService,
|
|
49
|
+
_telemetryService,
|
|
50
|
+
_mcpGatewayService,
|
|
51
|
+
_configurationService,
|
|
52
|
+
_secretStorageService
|
|
53
|
+
) {
|
|
54
|
+
super();
|
|
55
|
+
this._extHostContext = _extHostContext;
|
|
56
|
+
this._mcpRegistry = _mcpRegistry;
|
|
57
|
+
this.dialogService = dialogService;
|
|
58
|
+
this._authenticationService = _authenticationService;
|
|
59
|
+
this.authenticationMcpServersService = authenticationMcpServersService;
|
|
60
|
+
this.authenticationMCPServerAccessService = authenticationMCPServerAccessService;
|
|
61
|
+
this.authenticationMCPServerUsageService = authenticationMCPServerUsageService;
|
|
62
|
+
this._dynamicAuthenticationProviderStorageService = _dynamicAuthenticationProviderStorageService;
|
|
63
|
+
this._extensionService = _extensionService;
|
|
64
|
+
this._contextKeyService = _contextKeyService;
|
|
65
|
+
this._telemetryService = _telemetryService;
|
|
66
|
+
this._mcpGatewayService = _mcpGatewayService;
|
|
67
|
+
this._configurationService = _configurationService;
|
|
68
|
+
this._secretStorageService = _secretStorageService;
|
|
69
|
+
this._serverIdCounter = 0;
|
|
70
|
+
this._servers = ( new Map());
|
|
71
|
+
this._serverDefinitions = ( new Map());
|
|
72
|
+
this._serverAuthTracking = ( new McpServerAuthTracker());
|
|
73
|
+
this._collectionDefinitions = this._register(( new DisposableMap()));
|
|
74
|
+
this._gateways = this._register(( new DisposableMap()));
|
|
75
|
+
this._register(
|
|
76
|
+
_authenticationService.onDidChangeSessions(e => this._onDidChangeAuthSessions(e.providerId, e.label))
|
|
77
|
+
);
|
|
78
|
+
const proxy = this._proxy = ( _extHostContext.getProxy(ExtHostContext.ExtHostMcp));
|
|
79
|
+
this._register(this._mcpRegistry.registerDelegate({
|
|
80
|
+
priority: _extHostContext.extensionHostKind === ExtensionHostKind.LocalWebWorker ? 0 : 1,
|
|
81
|
+
waitForInitialProviderPromises() {
|
|
82
|
+
return proxy.$waitForInitialCollectionProviders();
|
|
83
|
+
},
|
|
84
|
+
canStart(collection, serverDefinition) {
|
|
85
|
+
if (collection.remoteAuthority !== _extHostContext.remoteAuthority) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (serverDefinition.launch.type === McpServerTransportType.Stdio && _extHostContext.extensionHostKind === ExtensionHostKind.LocalWebWorker) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
async substituteVariables(serverDefinition, launch) {
|
|
94
|
+
const ser = await proxy.$substituteVariables(
|
|
95
|
+
serverDefinition.variableReplacement?.folder?.uri,
|
|
96
|
+
McpServerLaunch.toSerialized(launch)
|
|
97
|
+
);
|
|
98
|
+
return McpServerLaunch.fromSerialized(ser);
|
|
99
|
+
},
|
|
100
|
+
start: (_collection, serverDefiniton, resolveLaunch, options) => {
|
|
101
|
+
const id = ++this._serverIdCounter;
|
|
102
|
+
const launch = ( new ExtHostMcpServerLaunch(
|
|
103
|
+
_extHostContext.extensionHostKind,
|
|
104
|
+
() => proxy.$stopMcp(id),
|
|
105
|
+
msg => proxy.$sendMessage(id, JSON.stringify(msg))
|
|
106
|
+
));
|
|
107
|
+
this._servers.set(id, launch);
|
|
108
|
+
this._serverDefinitions.set(id, serverDefiniton);
|
|
109
|
+
proxy.$startMcp(id, {
|
|
110
|
+
launch: resolveLaunch,
|
|
111
|
+
defaultCwd: serverDefiniton.defaultCwd ?? serverDefiniton.variableReplacement?.folder?.uri,
|
|
112
|
+
errorOnUserInteraction: options?.errorOnUserInteraction
|
|
113
|
+
});
|
|
114
|
+
return launch;
|
|
115
|
+
}
|
|
116
|
+
}));
|
|
117
|
+
const onDidChangeMcpServerDefinitionsTrigger = this._register(( new RunOnceScheduler(() => this._publishServerDefinitions(), 500)));
|
|
118
|
+
this._register(autorun(reader => {
|
|
119
|
+
const collections = this._mcpRegistry.collections.read(reader);
|
|
120
|
+
for (const collection of collections) {
|
|
121
|
+
collection.serverDefinitions.read(reader);
|
|
122
|
+
}
|
|
123
|
+
if (!onDidChangeMcpServerDefinitionsTrigger.isScheduled()) {
|
|
124
|
+
onDidChangeMcpServerDefinitionsTrigger.schedule();
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
onDidChangeMcpServerDefinitionsTrigger.schedule();
|
|
128
|
+
}
|
|
129
|
+
_publishServerDefinitions() {
|
|
130
|
+
const collections = this._mcpRegistry.collections.get();
|
|
131
|
+
const allServers = [];
|
|
132
|
+
for (const collection of collections) {
|
|
133
|
+
const servers = collection.serverDefinitions.get();
|
|
134
|
+
for (const server of servers) {
|
|
135
|
+
allServers.push(McpServerDefinition.toSerialized(server));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this._proxy.$onDidChangeMcpServerDefinitions(allServers);
|
|
139
|
+
}
|
|
140
|
+
$upsertMcpCollection(collection, serversDto) {
|
|
141
|
+
const servers = ( serversDto.map(McpServerDefinition.fromSerialized));
|
|
142
|
+
const existing = this._collectionDefinitions.get(collection.id);
|
|
143
|
+
if (existing) {
|
|
144
|
+
existing.servers.set(servers, undefined);
|
|
145
|
+
} else {
|
|
146
|
+
const serverDefinitions = observableValue("mcpServers", servers);
|
|
147
|
+
const extensionId = ( new ExtensionIdentifier(collection.extensionId));
|
|
148
|
+
const store = ( new DisposableStore());
|
|
149
|
+
const handle = store.add(( new MutableDisposable()));
|
|
150
|
+
const register = () => {
|
|
151
|
+
handle.value ??= this._mcpRegistry.registerCollection({
|
|
152
|
+
...collection,
|
|
153
|
+
provenance: McpCollectionProvenance.Extension,
|
|
154
|
+
source: extensionId,
|
|
155
|
+
order: McpCollectionSortOrder.Extension,
|
|
156
|
+
resolveServerLanch: collection.canResolveLaunch ? (async def => {
|
|
157
|
+
const r = await this._proxy.$resolveMcpLaunch(collection.id, def.label);
|
|
158
|
+
return r ? McpServerLaunch.fromSerialized(r) : undefined;
|
|
159
|
+
}) : undefined,
|
|
160
|
+
trustBehavior: collection.isTrustedByDefault ? McpServerTrust.Kind.Trusted : McpServerTrust.Kind.TrustedOnNonce,
|
|
161
|
+
remoteAuthority: this._extHostContext.remoteAuthority,
|
|
162
|
+
serverDefinitions
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
const whenClauseStr = mapFindFirst(
|
|
166
|
+
this._extensionService.extensions,
|
|
167
|
+
e => ExtensionIdentifier.equals(extensionId, e.identifier) ? e.contributes?.mcpServerDefinitionProviders?.find(p => extensionPrefixedIdentifier(extensionId, p.id) === collection.id)?.when : undefined
|
|
168
|
+
);
|
|
169
|
+
const whenClause = whenClauseStr && ContextKeyExpr.deserialize(whenClauseStr);
|
|
170
|
+
if (!whenClause) {
|
|
171
|
+
register();
|
|
172
|
+
} else {
|
|
173
|
+
const evaluate = () => {
|
|
174
|
+
if (this._contextKeyService.contextMatchesRules(whenClause)) {
|
|
175
|
+
register();
|
|
176
|
+
} else {
|
|
177
|
+
handle.clear();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
store.add(this._contextKeyService.onDidChangeContext(evaluate));
|
|
181
|
+
evaluate();
|
|
182
|
+
}
|
|
183
|
+
this._collectionDefinitions.set(collection.id, {
|
|
184
|
+
servers: serverDefinitions,
|
|
185
|
+
dispose: () => store.dispose()
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
$deleteMcpCollection(collectionId) {
|
|
190
|
+
this._collectionDefinitions.deleteAndDispose(collectionId);
|
|
191
|
+
}
|
|
192
|
+
$onDidChangeState(id, update) {
|
|
193
|
+
const server = this._servers.get(id);
|
|
194
|
+
if (!server) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
server.state.set(update, undefined);
|
|
198
|
+
if (!McpConnectionState.isRunning(update)) {
|
|
199
|
+
server.dispose();
|
|
200
|
+
this._servers.delete(id);
|
|
201
|
+
this._serverDefinitions.delete(id);
|
|
202
|
+
this._serverAuthTracking.untrack(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
$onDidPublishLog(id, level, log) {
|
|
206
|
+
if (typeof level === "string") {
|
|
207
|
+
level = LogLevel.Info;
|
|
208
|
+
log = level;
|
|
209
|
+
}
|
|
210
|
+
this._servers.get(id)?.pushLog(level, log);
|
|
211
|
+
}
|
|
212
|
+
$onDidReceiveMessage(id, message) {
|
|
213
|
+
this._servers.get(id)?.pushMessage(message);
|
|
214
|
+
}
|
|
215
|
+
async $getTokenForProviderId(id, providerId, scopes, options = {}) {
|
|
216
|
+
const server = this._serverDefinitions.get(id);
|
|
217
|
+
if (!server) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
return this._getSessionForProvider(
|
|
221
|
+
id,
|
|
222
|
+
server,
|
|
223
|
+
providerId,
|
|
224
|
+
scopes,
|
|
225
|
+
undefined,
|
|
226
|
+
options.errorOnUserInteraction,
|
|
227
|
+
options.clientId
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
async $getTokenFromServerMetadata(
|
|
231
|
+
id,
|
|
232
|
+
authDetails,
|
|
233
|
+
{
|
|
234
|
+
errorOnUserInteraction,
|
|
235
|
+
forceNewRegistration,
|
|
236
|
+
clientId
|
|
237
|
+
} = {}
|
|
238
|
+
) {
|
|
239
|
+
const server = this._serverDefinitions.get(id);
|
|
240
|
+
if (!server) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
const authorizationServer = URI.revive(authDetails.authorizationServer);
|
|
244
|
+
const resourceServer = authDetails.resourceMetadata?.resource ? ( URI.parse(authDetails.resourceMetadata.resource)) : undefined;
|
|
245
|
+
const resolvedScopes = authDetails.scopes ?? authDetails.resourceMetadata?.scopes_supported ?? authDetails.authorizationServerMetadata.scopes_supported ?? [];
|
|
246
|
+
if (authDetails.enterpriseManaged) {
|
|
247
|
+
const resource = authDetails.resourceMetadata?.resource;
|
|
248
|
+
if (!resource) {
|
|
249
|
+
throw ( new Error(( localize(
|
|
250
|
+
3014,
|
|
251
|
+
"The enterprise-managed MCP server '{0}' did not advertise a protected-resource metadata document with a 'resource' identifier.",
|
|
252
|
+
server.label
|
|
253
|
+
))));
|
|
254
|
+
}
|
|
255
|
+
const resourceAuthServers = authDetails.resourceMetadata?.authorization_servers ?? [];
|
|
256
|
+
const audience = resourceAuthServers[0];
|
|
257
|
+
if (!audience) {
|
|
258
|
+
throw ( new Error(( localize(
|
|
259
|
+
3015,
|
|
260
|
+
"The enterprise-managed MCP server '{0}' did not advertise an `authorization_servers` entry in its protected-resource metadata.",
|
|
261
|
+
server.label
|
|
262
|
+
))));
|
|
263
|
+
}
|
|
264
|
+
const xaaScopes = authDetails.scopes ?? authDetails.resourceMetadata?.scopes_supported ?? [];
|
|
265
|
+
const issuer = this._ensureXaaIssuer();
|
|
266
|
+
const xaaProviderId = await this._authenticationService.createOrGetXaaProvider(issuer);
|
|
267
|
+
if (!xaaProviderId) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
const resourceClientId = clientId ?? authDetails.clientId;
|
|
271
|
+
let resourceClientSecret;
|
|
272
|
+
if (resourceClientId) {
|
|
273
|
+
try {
|
|
274
|
+
resourceClientSecret = await this._secretStorageService.get(mcpOAuthClientSecretStorageKey(resource, resourceClientId));
|
|
275
|
+
} catch {}
|
|
276
|
+
}
|
|
277
|
+
return this._getSessionForProvider(
|
|
278
|
+
id,
|
|
279
|
+
server,
|
|
280
|
+
xaaProviderId,
|
|
281
|
+
xaaScopes,
|
|
282
|
+
issuer,
|
|
283
|
+
errorOnUserInteraction,
|
|
284
|
+
resourceClientId,
|
|
285
|
+
resource,
|
|
286
|
+
audience,
|
|
287
|
+
resourceClientSecret
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
let providerId = await this._authenticationService.getOrActivateProviderIdForServer(authorizationServer, resourceServer);
|
|
291
|
+
const resolvedClientId = clientId ?? authDetails.clientId;
|
|
292
|
+
const mcpServerUrl = server.launch.type === McpServerTransportType.HTTP ? ( server.launch.uri.toString(true)) : undefined;
|
|
293
|
+
let clientSecret;
|
|
294
|
+
let didLookupClientSecret = false;
|
|
295
|
+
if (resolvedClientId && mcpServerUrl) {
|
|
296
|
+
try {
|
|
297
|
+
clientSecret = await this._secretStorageService.get(mcpOAuthClientSecretStorageKey(mcpServerUrl, resolvedClientId));
|
|
298
|
+
didLookupClientSecret = true;
|
|
299
|
+
} catch {}
|
|
300
|
+
}
|
|
301
|
+
if (didLookupClientSecret && providerId && !forceNewRegistration && this._authenticationService.isDynamicAuthenticationProvider(providerId)) {
|
|
302
|
+
const registered = await this._dynamicAuthenticationProviderStorageService.getClientRegistration(providerId);
|
|
303
|
+
if (registered && registered.clientSecret !== clientSecret) {
|
|
304
|
+
forceNewRegistration = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (forceNewRegistration && providerId) {
|
|
308
|
+
if (!this._authenticationService.isDynamicAuthenticationProvider(providerId)) {
|
|
309
|
+
throw ( new Error("Cannot force new registration for a non-dynamic authentication provider."));
|
|
310
|
+
}
|
|
311
|
+
this._authenticationService.unregisterAuthenticationProvider(providerId);
|
|
312
|
+
await this._dynamicAuthenticationProviderStorageService.removeDynamicProvider(providerId);
|
|
313
|
+
providerId = undefined;
|
|
314
|
+
}
|
|
315
|
+
if (!providerId) {
|
|
316
|
+
const provider = await this._authenticationService.createDynamicAuthenticationProvider(
|
|
317
|
+
authorizationServer,
|
|
318
|
+
authDetails.authorizationServerMetadata,
|
|
319
|
+
authDetails.resourceMetadata,
|
|
320
|
+
resolvedClientId,
|
|
321
|
+
clientSecret
|
|
322
|
+
);
|
|
323
|
+
if (!provider) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
providerId = provider.id;
|
|
327
|
+
}
|
|
328
|
+
return this._getSessionForProvider(
|
|
329
|
+
id,
|
|
330
|
+
server,
|
|
331
|
+
providerId,
|
|
332
|
+
resolvedScopes,
|
|
333
|
+
authorizationServer,
|
|
334
|
+
errorOnUserInteraction,
|
|
335
|
+
resolvedClientId,
|
|
336
|
+
authDetails.resourceMetadata?.resource,
|
|
337
|
+
undefined,
|
|
338
|
+
clientSecret
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
_ensureXaaIssuer() {
|
|
342
|
+
const config = this._configurationService.getValue(mcpEnterpriseManagedAuthIdpSection) ?? {};
|
|
343
|
+
const configuredIssuer = config.issuer?.trim();
|
|
344
|
+
if (!configuredIssuer) {
|
|
345
|
+
throw ( new Error(( localize(
|
|
346
|
+
3016,
|
|
347
|
+
"Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to be configured. Set it via enterprise policy (Windows Group Policy / macOS managed preferences / Linux `/etc/vscode/policy.json`) or, for local testing, by hand-editing `settings.json`."
|
|
348
|
+
))));
|
|
349
|
+
}
|
|
350
|
+
let parsed;
|
|
351
|
+
try {
|
|
352
|
+
parsed = ( URI.parse(configuredIssuer));
|
|
353
|
+
} catch {
|
|
354
|
+
throw ( new Error(( localize(
|
|
355
|
+
3017,
|
|
356
|
+
"Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to be a valid URL; got '{0}'.",
|
|
357
|
+
configuredIssuer
|
|
358
|
+
))));
|
|
359
|
+
}
|
|
360
|
+
if (parsed.scheme !== "https" && parsed.scheme !== "http") {
|
|
361
|
+
throw ( new Error(( localize(
|
|
362
|
+
3018,
|
|
363
|
+
"Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to use the `https` or `http` scheme; got '{0}'.",
|
|
364
|
+
configuredIssuer
|
|
365
|
+
))));
|
|
366
|
+
}
|
|
367
|
+
return parsed;
|
|
368
|
+
}
|
|
369
|
+
async _getSessionForProvider(
|
|
370
|
+
serverId,
|
|
371
|
+
server,
|
|
372
|
+
providerId,
|
|
373
|
+
scopes,
|
|
374
|
+
authorizationServer,
|
|
375
|
+
errorOnUserInteraction = false,
|
|
376
|
+
clientId,
|
|
377
|
+
resource,
|
|
378
|
+
audience,
|
|
379
|
+
clientSecret
|
|
380
|
+
) {
|
|
381
|
+
const authContext = {
|
|
382
|
+
authorizationServer,
|
|
383
|
+
clientId,
|
|
384
|
+
resource,
|
|
385
|
+
audience
|
|
386
|
+
};
|
|
387
|
+
const sessions = await this._authenticationService.getSessions(providerId, scopes, {
|
|
388
|
+
authorizationServer,
|
|
389
|
+
clientId,
|
|
390
|
+
clientSecret,
|
|
391
|
+
resource,
|
|
392
|
+
audience
|
|
393
|
+
}, true);
|
|
394
|
+
if (server.launch.type !== McpServerTransportType.HTTP) {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
const mcpServerUrl = ( server.launch.uri.toString(true));
|
|
398
|
+
const accountNamePreference = this.authenticationMcpServersService.getAccountPreference(server.id, providerId);
|
|
399
|
+
let matchingAccountPreferenceSession;
|
|
400
|
+
if (accountNamePreference) {
|
|
401
|
+
matchingAccountPreferenceSession = sessions.find(session => session.account.label === accountNamePreference);
|
|
402
|
+
}
|
|
403
|
+
const provider = this._authenticationService.getProvider(providerId);
|
|
404
|
+
let session;
|
|
405
|
+
if (sessions.length) {
|
|
406
|
+
if (matchingAccountPreferenceSession && this.authenticationMCPServerAccessService.isAccessAllowedForUrl(
|
|
407
|
+
providerId,
|
|
408
|
+
matchingAccountPreferenceSession.account.label,
|
|
409
|
+
server.id,
|
|
410
|
+
mcpServerUrl
|
|
411
|
+
)) {
|
|
412
|
+
this.authenticationMCPServerUsageService.addAccountUsage(
|
|
413
|
+
providerId,
|
|
414
|
+
matchingAccountPreferenceSession.account.label,
|
|
415
|
+
scopes,
|
|
416
|
+
server.id,
|
|
417
|
+
server.label
|
|
418
|
+
);
|
|
419
|
+
this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
|
|
420
|
+
return matchingAccountPreferenceSession.accessToken;
|
|
421
|
+
}
|
|
422
|
+
if (!provider.supportsMultipleAccounts && this.authenticationMCPServerAccessService.isAccessAllowedForUrl(providerId, sessions[0].account.label, server.id, mcpServerUrl)) {
|
|
423
|
+
this.authenticationMCPServerUsageService.addAccountUsage(providerId, sessions[0].account.label, scopes, server.id, server.label);
|
|
424
|
+
this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
|
|
425
|
+
return sessions[0].accessToken;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (errorOnUserInteraction) {
|
|
429
|
+
throw ( new UserInteractionRequiredError("authentication"));
|
|
430
|
+
}
|
|
431
|
+
const isAllowed = await this.loginPrompt(server.label, provider.label, false);
|
|
432
|
+
if (!isAllowed) {
|
|
433
|
+
throw ( new Error("User did not consent to login."));
|
|
434
|
+
}
|
|
435
|
+
if (sessions.length) {
|
|
436
|
+
if (provider.supportsMultipleAccounts && errorOnUserInteraction) {
|
|
437
|
+
throw ( new UserInteractionRequiredError("authentication"));
|
|
438
|
+
}
|
|
439
|
+
session = provider.supportsMultipleAccounts ? await this.authenticationMcpServersService.selectSession(providerId, server.id, server.label, scopes, sessions) : sessions[0];
|
|
440
|
+
} else {
|
|
441
|
+
if (errorOnUserInteraction) {
|
|
442
|
+
throw ( new UserInteractionRequiredError("authentication"));
|
|
443
|
+
}
|
|
444
|
+
const accountToCreate = matchingAccountPreferenceSession?.account;
|
|
445
|
+
do {
|
|
446
|
+
session = await this._authenticationService.createSession(providerId, scopes, {
|
|
447
|
+
activateImmediate: true,
|
|
448
|
+
account: accountToCreate,
|
|
449
|
+
authorizationServer,
|
|
450
|
+
clientId,
|
|
451
|
+
clientSecret,
|
|
452
|
+
resource,
|
|
453
|
+
audience
|
|
454
|
+
});
|
|
455
|
+
} while (accountToCreate && accountToCreate.label !== session.account.label && !(await this.continueWithIncorrectAccountPrompt(session.account.label, accountToCreate.label)));
|
|
456
|
+
}
|
|
457
|
+
this.authenticationMCPServerAccessService.updateAllowedMcpServers(providerId, session.account.label, [{
|
|
458
|
+
id: server.id,
|
|
459
|
+
name: server.label,
|
|
460
|
+
allowed: true,
|
|
461
|
+
url: mcpServerUrl
|
|
462
|
+
}]);
|
|
463
|
+
this.authenticationMcpServersService.updateAccountPreference(server.id, providerId, session.account);
|
|
464
|
+
this.authenticationMCPServerUsageService.addAccountUsage(providerId, session.account.label, scopes, server.id, server.label);
|
|
465
|
+
this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
|
|
466
|
+
return session.accessToken;
|
|
467
|
+
}
|
|
468
|
+
async continueWithIncorrectAccountPrompt(chosenAccountLabel, requestedAccountLabel) {
|
|
469
|
+
const result = await this.dialogService.prompt({
|
|
470
|
+
message: ( localize(3019, "Incorrect account detected")),
|
|
471
|
+
detail: ( localize(
|
|
472
|
+
3020,
|
|
473
|
+
"The chosen account, {0}, does not match the requested account, {1}.",
|
|
474
|
+
chosenAccountLabel,
|
|
475
|
+
requestedAccountLabel
|
|
476
|
+
)),
|
|
477
|
+
type: Severity.Warning,
|
|
478
|
+
cancelButton: true,
|
|
479
|
+
buttons: [{
|
|
480
|
+
label: ( localize(3021, "Keep {0}", chosenAccountLabel)),
|
|
481
|
+
run: () => chosenAccountLabel
|
|
482
|
+
}, {
|
|
483
|
+
label: ( localize(3022, "Login with {0}", requestedAccountLabel)),
|
|
484
|
+
run: () => requestedAccountLabel
|
|
485
|
+
}]
|
|
486
|
+
});
|
|
487
|
+
if (!result.result) {
|
|
488
|
+
throw ( new CancellationError());
|
|
489
|
+
}
|
|
490
|
+
return result.result === chosenAccountLabel;
|
|
491
|
+
}
|
|
492
|
+
async _onDidChangeAuthSessions(providerId, providerLabel) {
|
|
493
|
+
const serversUsingProvider = this._serverAuthTracking.get(providerId);
|
|
494
|
+
if (!serversUsingProvider) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
for (const {
|
|
498
|
+
serverId,
|
|
499
|
+
scopes,
|
|
500
|
+
context
|
|
501
|
+
} of serversUsingProvider) {
|
|
502
|
+
const server = this._servers.get(serverId);
|
|
503
|
+
const serverDefinition = this._serverDefinitions.get(serverId);
|
|
504
|
+
if (!server || !serverDefinition) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const state = server.state.get();
|
|
508
|
+
if (state.state !== McpConnectionState.Kind.Running) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
await this._getSessionForProvider(
|
|
513
|
+
serverId,
|
|
514
|
+
serverDefinition,
|
|
515
|
+
providerId,
|
|
516
|
+
scopes,
|
|
517
|
+
context.authorizationServer,
|
|
518
|
+
true,
|
|
519
|
+
context.clientId,
|
|
520
|
+
context.resource,
|
|
521
|
+
context.audience
|
|
522
|
+
);
|
|
523
|
+
} catch (e) {
|
|
524
|
+
if (UserInteractionRequiredError.is(e)) {
|
|
525
|
+
server.pushLog(LogLevel.Warning, ( localize(
|
|
526
|
+
3023,
|
|
527
|
+
"Authentication session for {0} removed, stopping server",
|
|
528
|
+
providerLabel
|
|
529
|
+
)));
|
|
530
|
+
server.stop();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
$logMcpAuthSetup(data) {
|
|
536
|
+
this._telemetryService.publicLog2("mcp/authSetup", data);
|
|
537
|
+
}
|
|
538
|
+
async $startMcpGateway(chatSessionResource) {
|
|
539
|
+
const result = await this._mcpGatewayService.createGateway(
|
|
540
|
+
this._extHostContext.extensionHostKind === ExtensionHostKind.Remote,
|
|
541
|
+
chatSessionResource ? URI.revive(chatSessionResource) : undefined
|
|
542
|
+
);
|
|
543
|
+
if (!result) {
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
if (this._store.isDisposed) {
|
|
547
|
+
result.dispose();
|
|
548
|
+
return undefined;
|
|
549
|
+
}
|
|
550
|
+
const gatewayId = generateUuid();
|
|
551
|
+
const store = ( new DisposableStore());
|
|
552
|
+
store.add(result);
|
|
553
|
+
store.add(result.onDidChangeServers(servers => {
|
|
554
|
+
this._proxy.$onDidChangeGatewayServers(gatewayId, ( servers.map(s => ({
|
|
555
|
+
label: s.label,
|
|
556
|
+
address: s.address
|
|
557
|
+
}))));
|
|
558
|
+
}));
|
|
559
|
+
this._gateways.set(gatewayId, store);
|
|
560
|
+
return {
|
|
561
|
+
servers: ( result.servers.map(s => ({
|
|
562
|
+
label: s.label,
|
|
563
|
+
address: s.address
|
|
564
|
+
}))),
|
|
565
|
+
gatewayId
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
$disposeMcpGateway(gatewayId) {
|
|
569
|
+
this._gateways.deleteAndDispose(gatewayId);
|
|
570
|
+
}
|
|
571
|
+
async loginPrompt(mcpLabel, providerLabel, recreatingSession) {
|
|
572
|
+
const message = recreatingSession ? ( localize(
|
|
573
|
+
3024,
|
|
574
|
+
"The MCP Server Definition '{0}' wants you to authenticate to {1}.",
|
|
575
|
+
mcpLabel,
|
|
576
|
+
providerLabel
|
|
577
|
+
)) : ( localize(
|
|
578
|
+
3025,
|
|
579
|
+
"The MCP Server Definition '{0}' wants to authenticate to {1}.",
|
|
580
|
+
mcpLabel,
|
|
581
|
+
providerLabel
|
|
582
|
+
));
|
|
583
|
+
const buttons = [{
|
|
584
|
+
label: ( localize(3026, "&&Allow")),
|
|
585
|
+
run() {
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
}];
|
|
589
|
+
const {
|
|
590
|
+
result
|
|
591
|
+
} = await this.dialogService.prompt({
|
|
592
|
+
type: Severity.Info,
|
|
593
|
+
message,
|
|
594
|
+
buttons,
|
|
595
|
+
cancelButton: true
|
|
596
|
+
});
|
|
597
|
+
return result ?? false;
|
|
598
|
+
}
|
|
599
|
+
dispose() {
|
|
600
|
+
for (const server of ( this._servers.values())) {
|
|
601
|
+
server.extHostDispose();
|
|
602
|
+
}
|
|
603
|
+
this._servers.clear();
|
|
604
|
+
this._serverDefinitions.clear();
|
|
605
|
+
this._serverAuthTracking.clear();
|
|
606
|
+
super.dispose();
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
MainThreadMcp = __decorate([extHostNamedCustomer(MainContext.MainThreadMcp), ( __param(1, IMcpRegistry)), ( __param(2, IDialogService)), ( __param(3, IAuthenticationService)), ( __param(4, IAuthenticationMcpService)), ( __param(5, IAuthenticationMcpAccessService)), ( __param(6, IAuthenticationMcpUsageService)), ( __param(7, IDynamicAuthenticationProviderStorageService)), ( __param(8, IExtensionService)), ( __param(9, IContextKeyService)), ( __param(10, ITelemetryService)), ( __param(11, IWorkbenchMcpGatewayService)), ( __param(12, IConfigurationService)), ( __param(13, ISecretStorageService))], MainThreadMcp);
|
|
610
|
+
class ExtHostMcpServerLaunch extends Disposable {
|
|
611
|
+
pushLog(level, message) {
|
|
612
|
+
this._onDidLog.fire({
|
|
613
|
+
message,
|
|
614
|
+
level
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
pushMessage(message) {
|
|
618
|
+
let parsed;
|
|
619
|
+
try {
|
|
620
|
+
parsed = JSON.parse(message);
|
|
621
|
+
} catch (e) {
|
|
622
|
+
this.pushLog(LogLevel.Warning, `Failed to parse message: ${JSON.stringify(message)}`);
|
|
623
|
+
}
|
|
624
|
+
if (parsed) {
|
|
625
|
+
if (Array.isArray(parsed)) {
|
|
626
|
+
parsed.forEach(p => this._onDidReceiveMessage.fire(p));
|
|
627
|
+
} else {
|
|
628
|
+
this._onDidReceiveMessage.fire(parsed);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
constructor(extHostKind, stop, send) {
|
|
633
|
+
super();
|
|
634
|
+
this.stop = stop;
|
|
635
|
+
this.send = send;
|
|
636
|
+
this.state = observableValue("mcpServerState", {
|
|
637
|
+
state: McpConnectionState.Kind.Starting
|
|
638
|
+
});
|
|
639
|
+
this._onDidLog = this._register(( new Emitter()));
|
|
640
|
+
this.onDidLog = this._onDidLog.event;
|
|
641
|
+
this._onDidReceiveMessage = this._register(( new Emitter()));
|
|
642
|
+
this.onDidReceiveMessage = this._onDidReceiveMessage.event;
|
|
643
|
+
this._register(disposableTimeout(() => {
|
|
644
|
+
this.pushLog(
|
|
645
|
+
LogLevel.Info,
|
|
646
|
+
`Starting server from ${extensionHostKindToString(extHostKind)} extension host`
|
|
647
|
+
);
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
extHostDispose() {
|
|
651
|
+
if (McpConnectionState.isRunning(this.state.get())) {
|
|
652
|
+
this.pushLog(LogLevel.Warning, "Extension host shut down, server will stop.");
|
|
653
|
+
this.state.set({
|
|
654
|
+
state: McpConnectionState.Kind.Stopped
|
|
655
|
+
}, undefined);
|
|
656
|
+
}
|
|
657
|
+
this.dispose();
|
|
658
|
+
}
|
|
659
|
+
dispose() {
|
|
660
|
+
if (McpConnectionState.isRunning(this.state.get())) {
|
|
661
|
+
this.stop();
|
|
662
|
+
}
|
|
663
|
+
super.dispose();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
class McpServerAuthTracker {
|
|
667
|
+
constructor() {
|
|
668
|
+
this._tracking = ( new Map());
|
|
669
|
+
}
|
|
670
|
+
track(providerId, serverId, scopes, context) {
|
|
671
|
+
const servers = this._tracking.get(providerId) || [];
|
|
672
|
+
const filtered = servers.filter(s => s.serverId !== serverId);
|
|
673
|
+
filtered.push({
|
|
674
|
+
serverId,
|
|
675
|
+
scopes,
|
|
676
|
+
context
|
|
677
|
+
});
|
|
678
|
+
this._tracking.set(providerId, filtered);
|
|
679
|
+
}
|
|
680
|
+
untrack(serverId) {
|
|
681
|
+
for (const [providerId, servers] of this._tracking.entries()) {
|
|
682
|
+
const filtered = servers.filter(s => s.serverId !== serverId);
|
|
683
|
+
if (filtered.length === 0) {
|
|
684
|
+
this._tracking.delete(providerId);
|
|
685
|
+
} else {
|
|
686
|
+
this._tracking.set(providerId, filtered);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
get(providerId) {
|
|
691
|
+
return this._tracking.get(providerId);
|
|
692
|
+
}
|
|
693
|
+
clear() {
|
|
694
|
+
this._tracking.clear();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export { MainThreadMcp, McpServerAuthTracker };
|