Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/test/mcp/src/automationTools/windows.ts
5260 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 { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
7
import { z } from 'zod';
8
import { ApplicationService } from '../application';
9
10
/**
11
* Create a standardized text response for window tools
12
*/
13
function textResponse(text: string) {
14
return {
15
content: [{ type: 'text' as const, text }]
16
};
17
}
18
19
/**
20
* Window Management Tools for multi-window support.
21
* These tools provide Playwright-based window interactions through the automation driver.
22
*/
23
export function applyWindowTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
24
const tools: RegisteredTool[] = [];
25
26
tools.push(server.tool(
27
'vscode_automation_list_windows',
28
'List all open VS Code windows with their index and URL',
29
async () => {
30
const app = await appService.getOrCreateApplication();
31
const windowInfo = app.code.driver.getWindowsInfo();
32
return textResponse(`Open windows (${windowInfo.length}):\n${JSON.stringify(windowInfo, null, 2)}`);
33
}
34
));
35
36
tools.push(server.tool(
37
'vscode_automation_switch_window',
38
'Switch to a different VS Code window by index or URL pattern (e.g., "agent.html")',
39
{
40
indexOrUrl: z.union([z.number(), z.string()]).describe('Window index (0-based) or URL pattern to match (e.g., "agent.html", "workbench")')
41
},
42
async ({ indexOrUrl }) => {
43
const app = await appService.getOrCreateApplication();
44
const switched = app.code.driver.switchToWindow(indexOrUrl);
45
46
if (switched) {
47
return textResponse(`Switched to window (URL: ${switched.url()})`);
48
}
49
50
const windowInfo = app.code.driver.getWindowsInfo();
51
const availableWindows = windowInfo.map(w => ` ${w.index}: ${w.url}`).join('\n');
52
return textResponse(`Failed to switch window. Window not found for: ${indexOrUrl}\n\nAvailable windows:\n${availableWindows}`);
53
}
54
));
55
56
tools.push(server.tool(
57
'vscode_automation_get_current_window',
58
'Get information about the currently active window',
59
async () => {
60
const app = await appService.getOrCreateApplication();
61
const driver = app.code.driver;
62
const windowInfo = driver.getWindowsInfo();
63
const current = windowInfo.find(w => w.isCurrent);
64
return textResponse(`Current window:\nIndex: ${current?.index ?? -1}\nURL: ${current?.url ?? 'Unknown'}`);
65
}
66
));
67
68
tools.push(server.tool(
69
'vscode_automation_window_screenshot',
70
'Take a screenshot of the current window (respects the window set by vscode_automation_switch_window)',
71
{
72
fullPage: z.boolean().optional().describe('When true, takes a screenshot of the full scrollable page')
73
},
74
async ({ fullPage }) => {
75
const app = await appService.getOrCreateApplication();
76
const driver = app.code.driver;
77
const screenshotBuffer = await driver.screenshotBuffer(fullPage ?? false);
78
const url = driver.currentPage.url();
79
80
return {
81
content: [
82
{ type: 'text' as const, text: `Screenshot (URL: ${url})` },
83
{ type: 'image' as const, data: screenshotBuffer.toString('base64'), mimeType: 'image/png' }
84
]
85
};
86
}
87
));
88
89
tools.push(server.tool(
90
'vscode_automation_window_snapshot',
91
'Capture accessibility snapshot of the current window. Returns the page structure with element references that can be used for interactions.',
92
async () => {
93
const app = await appService.getOrCreateApplication();
94
const driver = app.code.driver;
95
const snapshot = await driver.getAccessibilitySnapshot();
96
const url = driver.currentPage.url();
97
98
return textResponse(`Page snapshot (URL: ${url}):\n\n${JSON.stringify(snapshot, null, 2)}`);
99
}
100
));
101
102
tools.push(server.tool(
103
'vscode_automation_window_click',
104
'Click on an element in the current window using a CSS selector',
105
{
106
selector: z.string().describe('CSS selector for the element to click'),
107
button: z.enum(['left', 'right', 'middle']).optional().describe('Mouse button to click'),
108
clickCount: z.number().optional().describe('Number of clicks (1 for single, 2 for double)')
109
},
110
async ({ selector, button, clickCount }) => {
111
const app = await appService.getOrCreateApplication();
112
const driver = app.code.driver;
113
await driver.clickSelector(selector, { button, clickCount });
114
return textResponse(`Clicked "${selector}"`);
115
}
116
));
117
118
tools.push(server.tool(
119
'vscode_automation_window_type',
120
'Type text into an element in the current window',
121
{
122
selector: z.string().describe('CSS selector for the element to type into'),
123
text: z.string().describe('Text to type'),
124
slowly: z.boolean().optional().describe('Whether to type one character at a time (useful for triggering key handlers)')
125
},
126
async ({ selector, text, slowly }) => {
127
const app = await appService.getOrCreateApplication();
128
const driver = app.code.driver;
129
await driver.typeText(selector, text, slowly ?? false);
130
return textResponse(`Typed "${text}" into "${selector}"`);
131
}
132
));
133
134
tools.push(server.tool(
135
'vscode_automation_window_evaluate',
136
'Evaluate JavaScript in the current window',
137
{
138
expression: z.string().describe('JavaScript expression to evaluate')
139
},
140
async ({ expression }) => {
141
const app = await appService.getOrCreateApplication();
142
const driver = app.code.driver;
143
const result = await driver.evaluateExpression(expression);
144
return textResponse(`Result:\n${JSON.stringify(result, null, 2)}`);
145
}
146
));
147
148
tools.push(server.tool(
149
'vscode_automation_window_locator',
150
'Get information about elements matching a selector in the current window',
151
{
152
selector: z.string().describe('CSS selector to find elements'),
153
action: z.enum(['count', 'textContent', 'innerHTML', 'boundingBox', 'isVisible']).optional().describe('Action to perform on matched elements')
154
},
155
async ({ selector, action }) => {
156
const app = await appService.getOrCreateApplication();
157
const driver = app.code.driver;
158
const result = await driver.getLocatorInfo(selector, action);
159
return textResponse(`Locator "${selector}":\n${JSON.stringify(result, null, 2)}`);
160
}
161
));
162
163
tools.push(server.tool(
164
'vscode_automation_window_wait_for_selector',
165
'Wait for an element to appear in the current window',
166
{
167
selector: z.string().describe('CSS selector to wait for'),
168
state: z.enum(['attached', 'detached', 'visible', 'hidden']).optional().describe('State to wait for'),
169
timeout: z.number().optional().describe('Timeout in milliseconds')
170
},
171
async ({ selector, state, timeout }) => {
172
const app = await appService.getOrCreateApplication();
173
const driver = app.code.driver;
174
await driver.waitForElement(selector, { state, timeout });
175
return textResponse(`Element "${selector}" is now ${state ?? 'visible'}`);
176
}
177
));
178
179
tools.push(server.tool(
180
'vscode_automation_window_hover',
181
'Hover over an element in the current window',
182
{
183
selector: z.string().describe('CSS selector for the element to hover over')
184
},
185
async ({ selector }) => {
186
const app = await appService.getOrCreateApplication();
187
const driver = app.code.driver;
188
await driver.hoverSelector(selector);
189
return textResponse(`Hovered over "${selector}"`);
190
}
191
));
192
193
tools.push(server.tool(
194
'vscode_automation_window_drag',
195
'Drag from one element to another in the current window',
196
{
197
sourceSelector: z.string().describe('CSS selector for the source element'),
198
targetSelector: z.string().describe('CSS selector for the target element')
199
},
200
async ({ sourceSelector, targetSelector }) => {
201
const app = await appService.getOrCreateApplication();
202
const driver = app.code.driver;
203
await driver.dragSelector(sourceSelector, targetSelector);
204
return textResponse(`Dragged from "${sourceSelector}" to "${targetSelector}"`);
205
}
206
));
207
208
tools.push(server.tool(
209
'vscode_automation_window_press_key',
210
'Press a key or key combination in the current window',
211
{
212
key: z.string().describe('Key to press (e.g., "Enter", "Tab", "Control+c", "Meta+v")')
213
},
214
async ({ key }) => {
215
const app = await appService.getOrCreateApplication();
216
const driver = app.code.driver;
217
await driver.pressKey(key);
218
return textResponse(`Pressed key "${key}"`);
219
}
220
));
221
222
tools.push(server.tool(
223
'vscode_automation_window_mouse_move',
224
'Move mouse to a specific position in the current window',
225
{
226
x: z.number().describe('X coordinate'),
227
y: z.number().describe('Y coordinate')
228
},
229
async ({ x, y }) => {
230
const app = await appService.getOrCreateApplication();
231
const driver = app.code.driver;
232
await driver.mouseMove(x, y);
233
return textResponse(`Moved mouse to (${x}, ${y})`);
234
}
235
));
236
237
tools.push(server.tool(
238
'vscode_automation_window_mouse_click',
239
'Click at a specific position in the current window',
240
{
241
x: z.number().describe('X coordinate'),
242
y: z.number().describe('Y coordinate'),
243
button: z.enum(['left', 'right', 'middle']).optional().describe('Mouse button to click'),
244
clickCount: z.number().optional().describe('Number of clicks (1 for single, 2 for double)')
245
},
246
async ({ x, y, button, clickCount }) => {
247
const app = await appService.getOrCreateApplication();
248
const driver = app.code.driver;
249
await driver.mouseClick(x, y, { button, clickCount });
250
return textResponse(`Clicked at (${x}, ${y})`);
251
}
252
));
253
254
tools.push(server.tool(
255
'vscode_automation_window_mouse_drag',
256
'Drag from one position to another in the current window',
257
{
258
startX: z.number().describe('Starting X coordinate'),
259
startY: z.number().describe('Starting Y coordinate'),
260
endX: z.number().describe('Ending X coordinate'),
261
endY: z.number().describe('Ending Y coordinate')
262
},
263
async ({ startX, startY, endX, endY }) => {
264
const app = await appService.getOrCreateApplication();
265
const driver = app.code.driver;
266
await driver.mouseDrag(startX, startY, endX, endY);
267
return textResponse(`Dragged from (${startX}, ${startY}) to (${endX}, ${endY})`);
268
}
269
));
270
271
tools.push(server.tool(
272
'vscode_automation_window_select_option',
273
'Select an option in a dropdown in the current window',
274
{
275
selector: z.string().describe('CSS selector for the select element'),
276
value: z.union([z.string(), z.array(z.string())]).describe('Value(s) to select')
277
},
278
async ({ selector, value }) => {
279
const app = await appService.getOrCreateApplication();
280
const driver = app.code.driver;
281
const selected = await driver.selectOption(selector, value);
282
return textResponse(`Selected "${selected.join(', ')}" in "${selector}"`);
283
}
284
));
285
286
tools.push(server.tool(
287
'vscode_automation_window_fill_form',
288
'Fill multiple form fields at once in the current window',
289
{
290
fields: z.array(z.object({
291
selector: z.string().describe('CSS selector for the form field'),
292
value: z.string().describe('Value to fill')
293
})).describe('Array of fields to fill')
294
},
295
async ({ fields }) => {
296
const app = await appService.getOrCreateApplication();
297
const driver = app.code.driver;
298
await driver.fillForm(fields);
299
return textResponse(`Filled ${fields.length} form field(s)`);
300
}
301
));
302
303
tools.push(server.tool(
304
'vscode_automation_window_console_messages',
305
'Get console messages from the current window',
306
async () => {
307
const app = await appService.getOrCreateApplication();
308
const driver = app.code.driver;
309
const messages = await driver.getConsoleMessages();
310
return textResponse(`Console messages (${messages.length}):\n${JSON.stringify(messages, null, 2)}`);
311
}
312
));
313
314
tools.push(server.tool(
315
'vscode_automation_window_wait_for_text',
316
'Wait for text to appear or disappear in the current window',
317
{
318
text: z.string().optional().describe('Text to wait for to appear'),
319
textGone: z.string().optional().describe('Text to wait for to disappear'),
320
timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)')
321
},
322
async ({ text, textGone, timeout }) => {
323
const app = await appService.getOrCreateApplication();
324
const driver = app.code.driver;
325
await driver.waitForText({ text, textGone, timeout });
326
return textResponse(`Waited for ${text ? `"${text}" to appear` : ''}${textGone ? `"${textGone}" to disappear` : ''}`);
327
}
328
));
329
330
tools.push(server.tool(
331
'vscode_automation_window_wait_for_time',
332
'Wait for a specified time in the current window',
333
{
334
seconds: z.number().describe('Time to wait in seconds')
335
},
336
async ({ seconds }) => {
337
const app = await appService.getOrCreateApplication();
338
const driver = app.code.driver;
339
await driver.waitForTime(seconds * 1000);
340
return textResponse(`Waited for ${seconds} second(s)`);
341
}
342
));
343
344
tools.push(server.tool(
345
'vscode_automation_window_verify_element_visible',
346
'Verify an element is visible in the current window',
347
{
348
selector: z.string().describe('CSS selector for the element to verify')
349
},
350
async ({ selector }) => {
351
const app = await appService.getOrCreateApplication();
352
const driver = app.code.driver;
353
const isVisible = await driver.verifyElementVisible(selector);
354
return textResponse(isVisible ? ` Element "${selector}" is visible` : ` Element "${selector}" is NOT visible`);
355
}
356
));
357
358
tools.push(server.tool(
359
'vscode_automation_window_verify_text_visible',
360
'Verify text is visible in the current window',
361
{
362
text: z.string().describe('Text to verify is visible')
363
},
364
async ({ text }) => {
365
const app = await appService.getOrCreateApplication();
366
const driver = app.code.driver;
367
const isVisible = await driver.verifyTextVisible(text);
368
return textResponse(isVisible ? ` Text "${text}" is visible` : ` Text "${text}" is NOT visible`);
369
}
370
));
371
372
tools.push(server.tool(
373
'vscode_automation_window_get_input_value',
374
'Get the value of an input element in the current window',
375
{
376
selector: z.string().describe('CSS selector for the input element')
377
},
378
async ({ selector }) => {
379
const app = await appService.getOrCreateApplication();
380
const driver = app.code.driver;
381
const value = await driver.getInputValue(selector);
382
return textResponse(`Input "${selector}" value: "${value}"`);
383
}
384
));
385
386
return tools;
387
}
388
389