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