Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/mcp/vscode-node/mcpToolCallingTools.tsx
13401 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 * as vscode from 'vscode';
7
import { JsonSchema } from '../../../platform/configuration/common/jsonSchema';
8
import { CancellationError } from '../../../util/vs/base/common/errors';
9
10
export class McpPickRef {
11
public _inner?: { type: 'pick'; value: vscode.QuickPick<vscode.QuickPickItem> } | { type: 'input'; value: vscode.InputBox };
12
private _isDisposed = false;
13
14
public readonly picks: {
15
id: string;
16
title: string;
17
choice: string;
18
}[] = [];
19
20
constructor(private _inputBarrier: Promise<void>) {
21
this._inputBarrier.then(() => {
22
23
if (!this._inner && !this._isDisposed) {
24
this.getPick().show();
25
this.reset(); // mark as "thinking"
26
}
27
});
28
}
29
30
public async pick() {
31
await this._inputBarrier;
32
const pick = this.getPick();
33
pick.busy = false;
34
return pick;
35
}
36
37
public async input() {
38
await this._inputBarrier;
39
const input = this.getInput();
40
input.busy = false;
41
return input;
42
}
43
44
public reset() {
45
if (!this._inner) {
46
return;
47
}
48
49
if (this._inner.type === 'pick') {
50
this._inner.value.items = [];
51
} else {
52
this._inner.value.value = '';
53
}
54
55
this._inner.value.title = '🤔';
56
this._inner.value.placeholder = 'Thinking...';
57
this._inner.value.busy = true;
58
}
59
60
public dispose() {
61
this._inner?.value.dispose();
62
this._isDisposed = true;
63
}
64
65
private getInput() {
66
if (this._inner?.type !== 'input') {
67
this._inner?.value.dispose();
68
69
const input = vscode.window.createInputBox();
70
input.ignoreFocusOut = true;
71
this._inner = { type: 'input', value: input };
72
}
73
74
return this._inner.value;
75
}
76
77
private getPick() {
78
if (this._inner?.type !== 'pick') {
79
this._inner?.value.dispose();
80
81
const pick = vscode.window.createQuickPick();
82
pick.ignoreFocusOut = true;
83
this._inner = { type: 'pick', value: pick };
84
}
85
86
return this._inner.value;
87
}
88
}
89
90
interface IQuickInputToolArgs {
91
id: string;
92
title: string;
93
placeholder?: string;
94
value?: string;
95
}
96
97
export class QuickInputTool {
98
public static readonly ID = 'getInput';
99
public static readonly description = 'Prompts the user for a short string input.';
100
public static readonly schema: JsonSchema = {
101
type: 'object',
102
properties: {
103
id: {
104
type: 'string',
105
description: 'An alphanumeric identifier for the input.',
106
},
107
title: {
108
type: 'string',
109
description: 'The title of the input box.',
110
},
111
placeholder: {
112
type: 'string',
113
description: 'The placeholder text for the input box.',
114
},
115
value: {
116
type: 'string',
117
description: 'The default value of the input box.',
118
},
119
},
120
required: ['title', 'id'],
121
};
122
123
public static async invoke(ref: McpPickRef, args: IQuickInputToolArgs): Promise<vscode.LanguageModelToolResult> {
124
const input = await ref.input();
125
input.title = args.title;
126
input.placeholder = args.placeholder;
127
if (args.value) {
128
input.value = args.value;
129
}
130
input.ignoreFocusOut = true;
131
132
const result = await new Promise<string | undefined>((resolve) => {
133
input.onDidAccept(() => {
134
const value = input.value;
135
resolve(value);
136
});
137
138
input.onDidHide(() => {
139
resolve(undefined);
140
});
141
142
input.show();
143
});
144
145
ref.reset();
146
147
if (result === undefined) {
148
throw new CancellationError();
149
}
150
151
ref.picks.push({ id: args.id, title: args.title, choice: result });
152
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`${args.title}: ${result}`)]);
153
}
154
}
155
156
interface IQuickPickToolArgs {
157
title: string;
158
placeholder?: string;
159
canPickMany?: boolean;
160
choices: { label: string; description?: string }[];
161
}
162
163
export class QuickPickTool {
164
public static readonly ID = 'getChoice';
165
public static readonly description = 'Prompts the user to select from a list of choices. It returns the label or labels of the choices that were selected';
166
public static readonly schema: JsonSchema = {
167
type: 'object',
168
properties: {
169
title: {
170
type: 'string',
171
description: 'The title of the pick box.',
172
},
173
placeholder: {
174
type: 'string',
175
description: 'The placeholder text for the pick box.',
176
},
177
canPickMany: {
178
type: 'boolean',
179
description: 'If true, the user can select multiple choices.',
180
},
181
choices: {
182
type: 'array',
183
items: {
184
type: 'object',
185
properties: {
186
label: { type: 'string', description: 'The primary label of the choice of the choice.' },
187
description: { type: 'string', description: 'A brief extra description.' },
188
}
189
},
190
minItems: 1,
191
},
192
},
193
required: ['title', 'choices'],
194
};
195
196
public static async invoke(ref: McpPickRef, args: IQuickPickToolArgs): Promise<vscode.LanguageModelToolResult> {
197
const pick = await ref.pick();
198
pick.title = args.title;
199
pick.placeholder = args.placeholder;
200
pick.items = args.choices;
201
pick.canSelectMany = args.canPickMany ?? false;
202
pick.ignoreFocusOut = true;
203
204
let result = await new Promise<string | string[] | undefined>((resolve) => {
205
pick.onDidAccept(() => {
206
const value = args.canPickMany ? pick.selectedItems.map(i => i.label) : pick.selectedItems[0]?.label;
207
resolve(value);
208
});
209
210
pick.onDidHide(() => {
211
resolve(undefined);
212
});
213
214
pick.show();
215
});
216
217
ref.reset();
218
219
if (result === undefined) {
220
throw new CancellationError();
221
}
222
if (Array.isArray(result)) {
223
result = '- ' + result.join('\n- ');
224
}
225
226
return new vscode.LanguageModelToolResult([new vscode.LanguageModelTextPart(`${args.title}: ${result}`)]);
227
}
228
}
229
230