Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/mcp/browser/mcpAddContextContribution.ts
5262 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 { CancellationToken } from '../../../../base/common/cancellation.js';
7
import { Codicon } from '../../../../base/common/codicons.js';
8
import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
9
import { autorun, derived } from '../../../../base/common/observable.js';
10
import { localize } from '../../../../nls.js';
11
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
12
import { IWorkbenchContribution } from '../../../common/contributions.js';
13
import { ChatContextPick, IChatContextPickService } from '../../chat/browser/attachments/chatContextPickService.js';
14
import { IMcpService, McpCapability } from '../common/mcpTypes.js';
15
import { McpResourcePickHelper } from './mcpResourceQuickAccess.js';
16
17
export class McpAddContextContribution extends Disposable implements IWorkbenchContribution {
18
private readonly _addContextMenu = this._register(new MutableDisposable());
19
constructor(
20
@IChatContextPickService private readonly _chatContextPickService: IChatContextPickService,
21
@IInstantiationService private readonly _instantiationService: IInstantiationService,
22
@IMcpService mcpService: IMcpService
23
) {
24
super();
25
26
const hasServersWithResources = derived(reader => {
27
let enabled = false;
28
for (const server of mcpService.servers.read(reader)) {
29
const cap = server.capabilities.read(undefined);
30
if (cap === undefined) {
31
enabled = true; // until we know more
32
} else if (cap & McpCapability.Resources) {
33
enabled = true;
34
break;
35
}
36
}
37
38
return enabled;
39
});
40
41
this._register(autorun(reader => {
42
const enabled = hasServersWithResources.read(reader);
43
if (enabled && !this._addContextMenu.value) {
44
this._registerAddContextMenu();
45
} else {
46
this._addContextMenu.clear();
47
}
48
}));
49
}
50
51
private _registerAddContextMenu() {
52
this._addContextMenu.value = this._chatContextPickService.registerChatContextItem({
53
type: 'pickerPick',
54
label: localize('mcp.addContext', "MCP Resources..."),
55
icon: Codicon.mcp,
56
isEnabled(widget) {
57
return !!widget.attachmentCapabilities.supportsMCPAttachments;
58
},
59
asPicker: () => {
60
const helper = this._instantiationService.createInstance(McpResourcePickHelper);
61
return {
62
placeholder: localize('mcp.addContext.placeholder', "Select MCP Resource..."),
63
picks: (_query, token) => this._getResourcePicks(token, helper),
64
goBack: () => {
65
return helper.navigateBack();
66
},
67
dispose: () => {
68
helper.dispose();
69
}
70
};
71
},
72
});
73
}
74
75
private _getResourcePicks(token: CancellationToken, helper: McpResourcePickHelper) {
76
const picksObservable = helper.getPicks(token);
77
78
return derived(this, reader => {
79
80
const pickItems = picksObservable.read(reader);
81
const picks: ChatContextPick[] = [];
82
83
for (const [server, resources] of pickItems.picks) {
84
if (resources.length === 0) {
85
continue;
86
}
87
picks.push(McpResourcePickHelper.sep(server));
88
for (const resource of resources) {
89
picks.push({
90
...McpResourcePickHelper.item(resource),
91
asAttachment: () => helper.toAttachment(resource, server)
92
});
93
}
94
}
95
return { picks, busy: pickItems.isBusy };
96
});
97
}
98
}
99
100