Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/mcp/common/mcpManagementIpc.ts
5257 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Emitter, Event } from '../../../base/common/event.js';
7
import { cloneAndChange } from '../../../base/common/objects.js';
8
import { URI, UriComponents } from '../../../base/common/uri.js';
9
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from '../../../base/common/uriIpc.js';
10
import { IChannel, IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
11
import { ILogService } from '../../log/common/log.js';
12
import { RemoteAgentConnectionContext } from '../../remote/common/remoteAgentEnvironment.js';
13
import { DidUninstallMcpServerEvent, IGalleryMcpServer, ILocalMcpServer, IMcpManagementService, IInstallableMcpServer, InstallMcpServerEvent, InstallMcpServerResult, InstallOptions, UninstallMcpServerEvent, UninstallOptions, IAllowedMcpServersService } from './mcpManagement.js';
14
import { AbstractMcpManagementService } from './mcpManagementService.js';
15
16
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
17
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined;
18
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined {
19
return uri ? URI.revive(transformer ? transformer.transformIncoming(uri) : uri) : undefined;
20
}
21
22
function transformIncomingServer(mcpServer: ILocalMcpServer, transformer: IURITransformer | null): ILocalMcpServer {
23
transformer = transformer ? transformer : DefaultURITransformer;
24
const manifest = mcpServer.manifest;
25
const transformed = transformAndReviveIncomingURIs({ ...mcpServer, ...{ manifest: undefined } }, transformer);
26
return { ...transformed, ...{ manifest } };
27
}
28
29
function transformIncomingOptions<O extends { mcpResource?: UriComponents }>(options: O | undefined, transformer: IURITransformer | null): O | undefined {
30
return options?.mcpResource ? transformAndReviveIncomingURIs(options, transformer ?? DefaultURITransformer) : options;
31
}
32
33
function transformOutgoingExtension(extension: ILocalMcpServer, transformer: IURITransformer | null): ILocalMcpServer {
34
return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoingURI(value) : undefined) : extension;
35
}
36
37
function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI {
38
return transformer ? transformer.transformOutgoingURI(uri) : uri;
39
}
40
41
export class McpManagementChannel<TContext = RemoteAgentConnectionContext | string> implements IServerChannel<TContext> {
42
readonly onInstallMcpServer: Event<InstallMcpServerEvent>;
43
readonly onDidInstallMcpServers: Event<readonly InstallMcpServerResult[]>;
44
readonly onDidUpdateMcpServers: Event<readonly InstallMcpServerResult[]>;
45
readonly onUninstallMcpServer: Event<UninstallMcpServerEvent>;
46
readonly onDidUninstallMcpServer: Event<DidUninstallMcpServerEvent>;
47
48
constructor(private service: IMcpManagementService, private getUriTransformer: (requestContext: TContext) => IURITransformer | null) {
49
this.onInstallMcpServer = Event.buffer(service.onInstallMcpServer, true);
50
this.onDidInstallMcpServers = Event.buffer(service.onDidInstallMcpServers, true);
51
this.onDidUpdateMcpServers = Event.buffer(service.onDidUpdateMcpServers, true);
52
this.onUninstallMcpServer = Event.buffer(service.onUninstallMcpServer, true);
53
this.onDidUninstallMcpServer = Event.buffer(service.onDidUninstallMcpServer, true);
54
}
55
56
listen<T>(context: TContext, event: string): Event<T> {
57
const uriTransformer = this.getUriTransformer(context);
58
switch (event) {
59
case 'onInstallMcpServer': {
60
return Event.map<InstallMcpServerEvent, InstallMcpServerEvent>(this.onInstallMcpServer, event => {
61
return { ...event, mcpResource: transformOutgoingURI(event.mcpResource, uriTransformer) };
62
}) as Event<T>;
63
}
64
case 'onDidInstallMcpServers': {
65
return Event.map<readonly InstallMcpServerResult[], readonly InstallMcpServerResult[]>(this.onDidInstallMcpServers, results =>
66
results.map(i => ({
67
...i,
68
local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,
69
mcpResource: transformOutgoingURI(i.mcpResource, uriTransformer)
70
}))) as Event<T>;
71
}
72
case 'onDidUpdateMcpServers': {
73
return Event.map<readonly InstallMcpServerResult[], readonly InstallMcpServerResult[]>(this.onDidUpdateMcpServers, results =>
74
results.map(i => ({
75
...i,
76
local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,
77
mcpResource: transformOutgoingURI(i.mcpResource, uriTransformer)
78
}))) as Event<T>;
79
}
80
case 'onUninstallMcpServer': {
81
return Event.map<UninstallMcpServerEvent, UninstallMcpServerEvent>(this.onUninstallMcpServer, event => {
82
return { ...event, mcpResource: transformOutgoingURI(event.mcpResource, uriTransformer) };
83
}) as Event<T>;
84
}
85
case 'onDidUninstallMcpServer': {
86
return Event.map<DidUninstallMcpServerEvent, DidUninstallMcpServerEvent>(this.onDidUninstallMcpServer, event => {
87
return { ...event, mcpResource: transformOutgoingURI(event.mcpResource, uriTransformer) };
88
}) as Event<T>;
89
}
90
}
91
92
throw new Error('Invalid listen');
93
}
94
95
async call<T>(context: TContext, command: string, args?: unknown): Promise<T> {
96
const uriTransformer: IURITransformer | null = this.getUriTransformer(context);
97
const argsArray = Array.isArray(args) ? args : [];
98
switch (command) {
99
case 'getInstalled': {
100
const mcpServers = await this.service.getInstalled(transformIncomingURI(argsArray[0], uriTransformer));
101
return mcpServers.map(e => transformOutgoingExtension(e, uriTransformer)) as T;
102
}
103
case 'install': {
104
return this.service.install(argsArray[0], transformIncomingOptions(argsArray[1], uriTransformer)) as T;
105
}
106
case 'installFromGallery': {
107
return this.service.installFromGallery(argsArray[0], transformIncomingOptions(argsArray[1], uriTransformer)) as T;
108
}
109
case 'uninstall': {
110
return this.service.uninstall(transformIncomingServer(argsArray[0], uriTransformer), transformIncomingOptions(argsArray[1], uriTransformer)) as T;
111
}
112
case 'updateMetadata': {
113
return this.service.updateMetadata(transformIncomingServer(argsArray[0], uriTransformer), argsArray[1], transformIncomingURI(argsArray[2], uriTransformer)) as T;
114
}
115
}
116
117
throw new Error('Invalid call');
118
}
119
}
120
121
export class McpManagementChannelClient extends AbstractMcpManagementService implements IMcpManagementService {
122
123
declare readonly _serviceBrand: undefined;
124
125
private readonly _onInstallMcpServer = this._register(new Emitter<InstallMcpServerEvent>());
126
get onInstallMcpServer() { return this._onInstallMcpServer.event; }
127
128
private readonly _onDidInstallMcpServers = this._register(new Emitter<readonly InstallMcpServerResult[]>());
129
get onDidInstallMcpServers() { return this._onDidInstallMcpServers.event; }
130
131
private readonly _onUninstallMcpServer = this._register(new Emitter<UninstallMcpServerEvent>());
132
get onUninstallMcpServer() { return this._onUninstallMcpServer.event; }
133
134
private readonly _onDidUninstallMcpServer = this._register(new Emitter<DidUninstallMcpServerEvent>());
135
get onDidUninstallMcpServer() { return this._onDidUninstallMcpServer.event; }
136
137
private readonly _onDidUpdateMcpServers = this._register(new Emitter<InstallMcpServerResult[]>());
138
get onDidUpdateMcpServers() { return this._onDidUpdateMcpServers.event; }
139
140
constructor(
141
private readonly channel: IChannel,
142
@IAllowedMcpServersService allowedMcpServersService: IAllowedMcpServersService,
143
@ILogService logService: ILogService
144
) {
145
super(allowedMcpServersService, logService);
146
this._register(this.channel.listen<InstallMcpServerEvent>('onInstallMcpServer')(e => this._onInstallMcpServer.fire(({ ...e, mcpResource: transformIncomingURI(e.mcpResource, null) }))));
147
this._register(this.channel.listen<readonly InstallMcpServerResult[]>('onDidInstallMcpServers')(results => this._onDidInstallMcpServers.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingServer(e.local, null) : e.local, mcpResource: transformIncomingURI(e.mcpResource, null) })))));
148
this._register(this.channel.listen<readonly InstallMcpServerResult[]>('onDidUpdateMcpServers')(results => this._onDidUpdateMcpServers.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingServer(e.local, null) : e.local, mcpResource: transformIncomingURI(e.mcpResource, null) })))));
149
this._register(this.channel.listen<UninstallMcpServerEvent>('onUninstallMcpServer')(e => this._onUninstallMcpServer.fire(({ ...e, mcpResource: transformIncomingURI(e.mcpResource, null) }))));
150
this._register(this.channel.listen<DidUninstallMcpServerEvent>('onDidUninstallMcpServer')(e => this._onDidUninstallMcpServer.fire(({ ...e, mcpResource: transformIncomingURI(e.mcpResource, null) }))));
151
}
152
153
install(server: IInstallableMcpServer, options?: InstallOptions): Promise<ILocalMcpServer> {
154
return Promise.resolve(this.channel.call<ILocalMcpServer>('install', [server, options])).then(local => transformIncomingServer(local, null));
155
}
156
157
installFromGallery(extension: IGalleryMcpServer, installOptions?: InstallOptions): Promise<ILocalMcpServer> {
158
return Promise.resolve(this.channel.call<ILocalMcpServer>('installFromGallery', [extension, installOptions])).then(local => transformIncomingServer(local, null));
159
}
160
161
uninstall(extension: ILocalMcpServer, options?: UninstallOptions): Promise<void> {
162
return Promise.resolve(this.channel.call<void>('uninstall', [extension, options]));
163
}
164
165
getInstalled(mcpResource?: URI): Promise<ILocalMcpServer[]> {
166
return Promise.resolve(this.channel.call<ILocalMcpServer[]>('getInstalled', [mcpResource]))
167
.then(servers => servers.map(server => transformIncomingServer(server, null)));
168
}
169
170
updateMetadata(local: ILocalMcpServer, gallery: IGalleryMcpServer, mcpResource?: URI): Promise<ILocalMcpServer> {
171
return Promise.resolve(this.channel.call<ILocalMcpServer>('updateMetadata', [local, gallery, mcpResource])).then(local => transformIncomingServer(local, null));
172
}
173
}
174
175