test: remove obsolete automation MCP server (#343306)

Retire the development automation MCP wrapper and its registrations now that launch and UI-validation skills cover these workflows. Preserve the smoke tests, shared automation, and standalone scenario runner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
TylerLeonhardtandCopilot authored and GitHub committed 2026-10-21 22:06:58 +00:00
1 parent 439a8e69e2
commit 6ba33e0ca0
39 files changed
+2 -4777

No files matched your search

-2
View File
@@ -122,7 +122,6 @@ extensions/github/** @lszomoru
extensions/vscode-api-tests/src/singlefolder-tests/browser*.test.ts @kycutler @jruales
# Testing
test/mcp/** @TylerLeonhardt
test/scenario/** @bryanchen-d
test/sanity/** @dmitrivMS
@@ -142,4 +141,3 @@ src/vs/sessions/browser/workbench.ts @benibenj
/.github/instructions/sessions.instructions.md @sandy081
/.github/skills/sessions/SKILL.md @sandy081
/.github/skills/chat-customizations-editor/SKILL.md @sandy081
-130
View File
@@ -1,130 +0,0 @@
---
name: Demonstrate
description: Agent for demonstrating VS Code features
target: github-copilot
tools:
- "view"
- "create"
- "edit"
- "glob"
- "grep"
- "bash"
- "read_bash"
- "write_bash"
- "stop_bash"
- "list_bash"
- "report_intent"
- "fetch_documentation"
- "agents"
- "read"
- "search"
- "todo"
- "web"
- "github-mcp-server/*"
- "GitHub/*"
- "github/*"
- "vscode-playwright-mcp/*"
---
# Role and Objective
You are a QA testing agent. Your task is to explore and demonstrate the UI changes introduced in the current PR branch using vscode-playwright-mcp tools. Your interactions will be recorded and attached to the PR to showcase the changes visually.
# Core Requirements
## Setup Phase
1. Use GitHub MCP tools to get PR details (description, linked issues, comments)
2. Search the `microsoft/vscode-docs` repository for relevant documentation about the feature area
3. Examine changed files and commit messages to understand the scope
4. Identify what UI features or behaviors were modified
5. Start VS Code automation using `vscode_automation_start`
6. ALWAYS start by setting the setting `"chat.allowAnonymousAccess":true` using the `vscode_automation_settings_add_user_settings` tool. This will ensure that Chat works without requiring sign-in.
## Testing Phase
1. Use `browser_snapshot` to capture the current state
2. Execute the user workflows affected by the PR changes
## Demonstration Goals
- Show the new or modified UI in action
- Exercise the changed code paths through realistic user interactions
- Capture clear visual evidence of the improvements or changes
- Test edge cases or variations if applicable
# Important Guidelines
- Focus on DEMONSTRATING the changes, not verifying correctness
- You are NOT writing playwright tests - use the tools interactively to explore
- If the PR description or commits mention specific scenarios, prioritize testing those
- Make multiple passes if needed to capture different aspects of the changes
- You may make temporary modifications to facilitate better demonstration (e.g., adjusting settings, opening specific views)
## GitHub MCP Tools
**Prefer using GitHub MCP tools over `gh` CLI commands** - these provide structured data and better integration:
### Pull Request Tools
- `pull_request_read` - Get PR details, diff, status, files, reviews, and comments
- Use `method="get"` for PR metadata (title, description, labels, etc.)
- Use `method="get_diff"` for the full diff
- Use `method="get_files"` for list of changed files
- Use `method="get_reviews"` for review summaries
- Use `method="get_review_comments"` for line-specific review comments
- `search_pull_requests` - Search PRs with filters (author, state, etc.)
### Issue Tools
- `get_issue` - Get full issue details (description, labels, assignees, etc.)
- `get_issue_comments` - Get all comments on an issue
- `search_issues` - Search issues with filters
- `list_sub_issues` - Get sub-issues if using issue hierarchies
## Pointers for Controlling VS Code
- **Prefer `vscode_automation_*` tools over `browser_*` tools** when available - these are designed specifically for VS Code interactions and provide more reliable control. For example:
- `vscode_automation_chat_send_message` over using `browser_*` tools to send chat messages
- `vscode_automation_editor_type_text` over using `browser_*` tools to type in editors
If you are typing into a monaco input and you can't use the standard methods, follow this sequence:
**Monaco editors (used throughout VS Code) DO NOT work with standard Playwright methods like `.click()` on textareas or `.fill()` / `.type()`**
**YOU MUST follow this exact sequence:**
1. **Take a page snapshot** to identify the editor structure in the accessibility tree
2. **Find the parent `code` role element** that wraps the Monaco editor
- ❌ DO NOT click on `textarea` or `textbox` elements - these are overlaid by Monaco's rendering
- ✅ DO click on the `code` role element that is the parent container
3. **Click on the `code` element** to focus the editor - this properly delegates focus to Monaco's internal text handling
4. **Verify focus** by checking that the nested textbox element has the `[active]` attribute in a new snapshot
5. **Use `page.keyboard.press()` for EACH character individually** - standard Playwright `type()` or `fill()` methods don't work with Monaco editors since they intercept keyboard events at the page level
**Example:**
```js
// ❌ WRONG - this will fail with timeout
await page.locator('textarea').click();
await page.locator('textarea').fill('text');
// ✅ CORRECT
await page.locator('[role="code"]').click();
await page.keyboard.press('t');
await page.keyboard.press('e');
await page.keyboard.press('x');
await page.keyboard.press('t');
```
**Why this is required:** Monaco editors intercept keyboard events at the page level and use a virtualized rendering system. Clicking textareas directly or using `.fill()` bypasses Monaco's event handling, causing timeouts and failures.
# Workflow Pattern
1. Gather context:
- Retrieve PR details using GitHub MCP (description, linked issues, review comments)
- Search microsoft/vscode-docs for documentation on the affected feature areas
- Examine changed files and commit messages
2. Plan which user interactions will best showcase the changes
3. Start automation and navigate to the relevant area
4. Perform the interactions
5. Document what you're demonstrating as you go
6. Ensure the recording clearly shows the before/after or new functionality
7. **ALWAYS stop the automation** by calling `vscode_automation_stop` - this is REQUIRED whether you successfully demonstrated the feature or encountered issues that prevented testing
+1 -1
View File
@@ -185,7 +185,7 @@ Always call out, separately from the pass/fail result:
## Related
- **Interactive exploration.** `test/mcp` also serves these tools over MCP (`vscode_automation_*`), which helps when you need to inspect the UI before knowing what to assert. Configure it as an MCP server with `cwd` `test/mcp` and command `npm run start-stdio`.
- **Interactive exploration.** Use the `launch` skill to drive a development window with Playwright when you need to inspect the UI before knowing what to assert.
- **Automated validation on a pull request.** `microsoft/vscode-engineering` runs the same harness in CI: labelling a pull request `~requires-ui-validation` researches the change, runs a scenario against the exact merge candidate, and posts the per-step result with captioned video. Use this skill when a scenario is not yet covered there, or to iterate locally before proposing one.
<example>
-10
View File
@@ -1,15 +1,5 @@
{
"mcpServers": {
"vscode-automation-mcp": {
"type": "stdio",
"command": "npm",
"args": [
"--prefix",
"test/mcp",
"run",
"start-stdio"
]
},
"component-explorer": {
"type": "stdio",
"command": "npm",
+1 -10
View File
@@ -1,7 +1,7 @@
# Query: // eslint-disable-next-line (local/code-no-any-casts|@typescript-eslint/no-explicit-any)
# Flags: RegExp
727 results - 269 files
724 results - 266 files
.eslint-plugin-local/code-policy-localization-key-match.ts:
123: // eslint-disable-next-line local/code-no-any-casts
@@ -1258,12 +1258,3 @@ test/automation/src/code.ts:
test/automation/src/terminal.ts:
315: // eslint-disable-next-line local/code-no-any-casts
test/mcp/src/application.ts:
250: // eslint-disable-next-line local/code-no-any-casts
test/mcp/src/playwright.ts:
17: // eslint-disable-next-line local/code-no-any-casts
test/mcp/src/automationTools/problems.ts:
76: // eslint-disable-next-line local/code-no-any-casts
-9
View File
@@ -535,15 +535,6 @@
}
}
},
{
"label": "Launch MCP Server",
"type": "shell",
"command": "cd test/mcp && npm run compile && npm run start-http",
"isBackground": true,
"problemMatcher": [
"$tsc"
]
},
{
"label": "Run Code",
"type": "shell",
-1
View File
@@ -59,7 +59,6 @@ export const dirs = [
'test/monaco',
'test/smoke',
'test/scenario',
'test/mcp',
'.vscode/extensions/vscode-selfhost-import-aid',
'.vscode/extensions/vscode-selfhost-test-provider',
'.vscode/extensions/vscode-extras',
-13
View File
@@ -2454,19 +2454,6 @@ export default defineConfig(
'*' // node modules
]
},
{
'target': 'test/mcp/**',
'restrictions': [
'test/automation',
'test/scenario',
'test/mcp/**',
'@vscode/*',
'@parcel/*',
'@playwright/*',
'@modelcontextprotocol/sdk/**/*',
'*' // node modules
]
},
{
'target': 'test/componentFixtures/playwright/**',
'restrictions': [
-17
View File
@@ -1,17 +0,0 @@
{
"servers": {
"vscode-playwright-mcp": {
"type": "stdio",
"command": "npm",
// See https://github.com/microsoft/vscode/blob/main/test/mcp/README.md for supported arguments
"args": [
"run",
"start-stdio",
"--",
"--extensionDevelopmentPath=${workspaceFolder}",
],
"cwd": "${workspaceFolder}/../vscode/test/mcp"
}
},
"inputs": []
}
-9
View File
@@ -1,9 +0,0 @@
.DS_Store
npm-debug.log
Thumbs.db
node_modules/
out/
keybindings.*.json
test_data/
src/vscode/driver.d.ts
vscode-server*/
-185
View File
@@ -1,185 +0,0 @@
# Code - OSS Development MCP Server
This directory contains a Model Context Protocol (MCP) server that provides VS Code automation capabilities for Code - OSS development and testing. The MCP server exposes Code - OSS's testing infrastructure through a standardized interface, allowing AI assistants and other tools to interact with VS Code programmatically.
## What is MCP?
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that enables AI assistants to securely connect to external data sources and tools. This MCP server specifically provides VS Code automation capabilities, making it possible for AI assistants to:
- Start and stop VS Code instances
- Interact with editors, terminals, and UI elements
- Run commands and keybindings
- Navigate the explorer, search, debug, and other viewlets
- Manage extensions, settings, and keybindings
- Work with notebooks and chat features
## Quick Start - Stdio
Firstly, make sure you install all dependencies (`npm i`) at the root of the repo.
Dependencies are installed during repository setup, not when a server starts. To reinstall this server's dependencies separately, run `npm ci --prefix test/mcp` before starting it. Do not run installation while MCP servers are using the checkout.
Then, open the Command Palette and run:
```
MCP: List Servers → vscode-automation-mcp → Start Server
```
or open [mcp.json](../../.vscode/mcp.json) and start it from there.
That's it! It should automatically compile everything needed.
## Arguments
Open the [mcp.json](../../.vscode/mcp.json) and modify the `args`:
* `["run", "start-stdio"]`: opens Electron window
* `["run", "start-stdio", "--", "--web"]`: opens a Chromium window
* `["run", "start-stdio", "--", "--web", "--headless"]`: opens a headless window
> *NOTE: `--web` requires running `npm run install-playwright` from root*
## Debugging the server
You can modify the mcp.json to debug the server:
```JSON
"vscode-automation-mcp": {
"type": "stdio",
"command": "node",
"args": ["./out/stdio.js"],
"cwd": "${workspaceFolder}/test/mcp",
"dev": {
"watch": "test/mcp/**/*.ts",
"debug": {
"type": "node"
}
}
}
```
## What the Server Provides
The MCP server exposes a comprehensive set of VS Code automation tools through the MCP protocol:
### Application Management
- Start, stop, and restart VS Code instances
- Open workspaces and folders
- Record scenario evidence with step overlays, screenshots, video, traces, and an HTML report
### Editor Tools
- Open, close, and navigate files
- Get and set editor content
- Manage selections and cursors
### Terminal Tools
- Create and manage terminal instances
- Send commands to terminals
- Read terminal output
### Debug Tools
- Start and stop debug sessions
- Manage breakpoints
- Step through code
### Search Tools
- Search for files and text
- Navigate search results
### Extension Tools
- Install and manage extensions
- View extension information
### UI Interaction
- Quick access and command palette
- Explorer and activity bar
- Source control management
- Status bar interactions
- Problems panel
- Settings and keybindings editors
- Notebook support
- Chat features
### Scenario evidence
Start evidence capture before starting VS Code so Playwright enables video recording:
1. Call `vscode_automation_evidence_start`, including any required pre-launch `userSettings`.
2. Call `vscode_automation_evidence_step` with `started` before each action.
3. Perform and validate the action with the relevant automation tools.
4. Call `vscode_automation_evidence_step` with `passed`, `failed`, or `skipped`.
5. Call `vscode_automation_evidence_finish`.
Artifacts are written to `.build/vscode-playwright-mcp/evidence/<run-id>/`.
Each evidence run uses an isolated user profile with in-memory secret storage and records every visited Playwright page as a separate video.
## Development
### Manual Setup (Advanced)
If you prefer to run the server manually:
```bash
# Navigate to the MCP directory
cd test/mcp
# Install dependencies
npm install
# Compile TypeScript
npm run compile # or watch
# Start the server
npm run start-stdio
```
### Project Structure
```
test/mcp/
├── src/
│ ├── stdio.ts # Entry point for stdio transport
│ ├── automation.ts # MCP server with automation tools
│ ├── application.ts # VS Code application lifecycle management
│ ├── options.ts # Command-line options parsing
│ ├── utils.ts # Utility functions
│ └── automationTools/ # Tool implementations organized by feature
│ ├── index.ts # Tool registration
│ ├── core.ts # Core application tools
│ ├── editor.ts # Editor tools
│ ├── terminal.ts # Terminal tools
│ ├── debug.ts # Debug tools
│ └── ... # Other feature-specific tools
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── README.md # This file
```
### Architecture
The server uses a simple architecture:
- **stdio.ts** - Entry point that creates the MCP server and connects via stdio transport
- **automation.ts** - Creates the MCP server and registers all automation tools
- **application.ts** - Manages VS Code application lifecycle (start, stop, restart)
- **automationTools/** - Modular tool implementations organized by VS Code feature area
## Troubleshooting
### Server Won't Start
- Ensure Code - OSS has been built and run at least once (via F5 or `code.sh`)
- Verify all dependencies are installed with `npm install`
### Automation Issues
- Ensure Code - OSS has been built and run at least once (via F5 or `code.sh`)
- Check the server logs for errors
- Verify the workspace path is correct
## Contributing
This MCP server is part of the Code - OSS development infrastructure. When making changes:
1. Follow the existing TypeScript and coding conventions
2. Test with multiple MCP clients if possible
3. Update this README if adding new capabilities
4. Ensure proper error handling and logging
## License
This project is licensed under the MIT License - see the top-level project's license file for details.
-1526
View File
File diff suppressed because it is too large. Load diff
-26
View File
@@ -1,26 +0,0 @@
{
"name": "code-oss-dev-mcp",
"version": "0.1.0",
"license": "MIT",
"main": "./out/main.js",
"private": true,
"scripts": {
"compile": "cd ../scenario && npm run compile && cd ../mcp && node ../../node_modules/typescript/bin/tsc6",
"watch-scenario": "cd ../scenario && npm run watch",
"watch-mcp": "node ../../node_modules/typescript/bin/tsc6 --watch --preserveWatchOutput",
"watch": "npm-run-all2 -lp watch-scenario watch-mcp",
"start-stdio": "echo 'Starting vscode-automation-mcp... For customization and troubleshooting, see ./test/mcp/README.md' && npm run -s compile && node ./out/stdio.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "1.31.0",
"minimist": "^1.2.8",
"ncp": "^2.0.0",
"node-fetch": "^2.6.7"
},
"devDependencies": {
"@types/ncp": "2.0.1",
"@types/node": "24.x",
"@types/node-fetch": "^2.5.10",
"npm-run-all2": "^8.0.4"
}
}
-7
View File
@@ -1,7 +0,0 @@
SCRIPT_DIR="$(dirname -- "$( readlink -f -- "$0"; )")"
# Go to mcp server project root
cd "$SCRIPT_DIR/.."
# Start mcp
npm run start-stdio -- --video --autostart
-60
View File
@@ -1,60 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService, EvidenceService } from '../../scenario';
import { applyAllTools } from './automationTools/index.js';
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { z } from 'zod';
import { applyEvidenceStartTool, applyEvidenceTools } from './evidenceTools.js';
export async function getServer(appService: ApplicationService): Promise<Server> {
const server = new McpServer({
name: 'VS Code Automation Server',
version: '1.0.0',
title: 'An MCP Server that can interact with a local build of VS Code. Used for verifying UI behavior.'
}, { capabilities: { logging: {} } });
const evidenceService = new EvidenceService(appService);
server.tool(
'vscode_automation_start',
'Start VS Code Build. If workspacePath is not provided, VS Code will open with the last used workspace or an empty window.',
{
recordVideo: z.boolean().optional().describe('Whether to record a video of the session'),
workspacePath: z.string().optional().describe('Optional path to a workspace or folder to open. If not provided, opens the last used workspace.')
},
async ({ recordVideo, workspacePath }) => {
const app = await appService.getOrCreateApplication({ recordVideo, workspacePath });
await app.startTracing();
return {
content: [{
type: 'text' as const,
text: app ? `VS Code started successfully${workspacePath ? ` with workspace: ${workspacePath}` : ''}` : `Failed to start VS Code`
}]
};
}
);
applyEvidenceStartTool(server, evidenceService);
applyEvidenceTools(server, evidenceService);
// Apply all VS Code automation tools using the modular structure
const registeredTools = applyAllTools(server, appService);
const app = appService.application;
if (app) {
registeredTools.forEach(t => t.enable());
} else {
registeredTools.forEach(t => t.disable());
}
appService.onApplicationChange(app => {
if (app) {
registeredTools.forEach(t => t.enable());
} else {
registeredTools.forEach(t => t.disable());
}
});
return server.server;
}
@@ -1,36 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Activity Bar Tools
*/
export function applyActivityBarTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Doesn't seem particularly useful
// server.tool(
// 'vscode_automation_activitybar_wait_for_position',
// 'Wait for the activity bar to appear at a specific position',
// {
// position: z.enum(['LEFT', 'RIGHT']).describe('Position of the activity bar (LEFT or RIGHT)')
// },
// async (args) => {
// const { position } = args;
// const activityBarPosition = position === 'LEFT' ? 0 : 1; // ActivityBarPosition.LEFT = 0, RIGHT = 1
// await app.workbench.activitybar.waitForActivityBar(activityBarPosition);
// return {
// content: [{
// type: 'text' as const,
// text: `Activity bar found at position: ${position}`
// }]
// };
// }
// );
return tools;
}
-45
View File
@@ -1,45 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Chat Tools
*/
export function applyChatTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_chat_send_message',
'Send a message to the VS Code chat panel',
{
message: z.string().describe('The message to send to the chat')
},
async (args) => {
const { message } = args;
const app = await appService.getOrCreateApplication();
try {
await app.workbench.chat.sendMessage(message);
return {
content: [{
type: 'text' as const,
text: `Sent chat message: "${message}"`
}]
};
} catch (error) {
return {
content: [{
type: 'text' as const,
text: `Failed to send chat message: ${error}`
}]
};
}
}
));
return tools;
}
-163
View File
@@ -1,163 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { ApplicationService, assertNoProfileOverrides } from '../../../scenario';
/**
* Core Application Management Tools
*/
export function applyCoreTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_restart',
'Restart VS Code with optional workspace or folder and extra command-line arguments',
{
workspaceOrFolder: z.string().optional().describe('Path to a workspace or folder to open on restart'),
extraArgs: z.array(z.string()).optional().describe('Extra CLI arguments to pass on restart')
},
async ({ workspaceOrFolder, extraArgs }) => {
assertNoProfileOverrides(extraArgs);
const app = await appService.getOrCreateApplication();
await app.restart({ workspaceOrFolder, extraArgs });
const workspaceText = workspaceOrFolder ? ` with workspace: ${workspaceOrFolder}` : '';
const argsText = extraArgs?.length ? ` (args: ${extraArgs.join(' ')})` : '';
return {
content: [{
type: 'text' as const,
text: `VS Code restarted successfully${workspaceText}${argsText}`
}]
};
}
));
tools.push(server.tool(
'vscode_automation_stop',
'Stop the VS Code application',
async () => {
const app = await appService.getOrCreateApplication();
await app.stopTracing(undefined, true);
await app.stop();
return {
content: [{
type: 'text' as const,
text: 'VS Code stopped successfully'
}]
};
}
));
// This doesn't seem particularly useful
// server.tool(
// 'vscode_automation_get_quality',
// 'Get the quality/build type of VS Code (Dev, Insiders, Stable, etc.)',
// async () => {
// const info = {
// quality: app.quality,
// remote: app.remote,
// web: app.web,
// workspacePathOrFolder: app.workspacePathOrFolder,
// extensionsPath: app.extensionsPath,
// userDataPath: app.userDataPath
// };
// return {
// content: [{
// type: 'text' as const,
// text: `VS Code Info:\n${JSON.stringify(info, null, 2)}`
// }]
// };
// }
// );
// This doesn't seem particularly useful
// server.tool(
// 'vscode_automation_wait_for_element',
// 'Wait for a UI element to appear using CSS selector - prefer using specific workbench methods when available',
// {
// selector: z.string().describe('CSS selector for the element to wait for'),
// timeout: z.number().optional().default(20).describe('Timeout in seconds (default: 20)')
// },
// async (args) => {
// const { selector, timeout = 20 } = args;
// const retryCount = Math.floor((timeout * 1000) / 100); // 100ms intervals
// const element = await app.code.waitForElement(selector, undefined, retryCount);
// return {
// content: [{
// type: 'text' as const,
// text: `Element found: ${selector} (${!!element ? 'success' : 'not found'})`
// }]
// };
// }
// );
// Defer to Playwright's tool
// server.tool(
// 'vscode_automation_click_element',
// 'Click on a UI element - prefer using specific workbench methods when available',
// {
// selector: z.string().describe('CSS selector for the element to click'),
// xOffset: z.number().optional().describe('Optional X offset from element center'),
// yOffset: z.number().optional().describe('Optional Y offset from element center')
// },
// async (args) => {
// const { selector, xOffset, yOffset } = args;
// await app.code.waitAndClick(selector, xOffset, yOffset);
// return {
// content: [{
// type: 'text' as const,
// text: `Clicked element: ${selector}${xOffset !== undefined ? ` (offset: ${xOffset}, ${yOffset})` : ''}`
// }]
// };
// }
// );
// Defer to Playwright's tool
// server.tool(
// 'vscode_automation_send_keybinding',
// 'Send a keybinding to VS Code (e.g., ctrl+shift+p, cmd+s)',
// {
// keybinding: z.string().describe('The keybinding to send (e.g., ctrl+shift+p, cmd+s, escape)'),
// waitSelector: z.string().optional().describe('Optional CSS selector to wait for after sending the keybinding')
// },
// async (args) => {
// const { keybinding, waitSelector } = args;
// await app.code.dispatchKeybinding(keybinding, async () => {
// if (waitSelector) {
// await app.code.waitForElement(waitSelector);
// }
// });
// return {
// content: [{
// type: 'text' as const,
// text: `Sent keybinding: ${keybinding}${waitSelector ? ` (waited for: ${waitSelector})` : ''}`
// }]
// };
// }
// );
// Defer to Playwright's tool
// server.tool(
// 'vscode_automation_get_text_content',
// 'Get text content from a UI element using CSS selector',
// {
// selector: z.string().describe('CSS selector for the element'),
// expectedText: z.string().optional().describe('Optional expected text to wait for')
// },
// async (args) => {
// const { selector, expectedText } = args;
// const text = await app.code.waitForTextContent(selector, expectedText);
// return {
// content: [{
// type: 'text' as const,
// text: `Text content from ${selector}: "${text}"`
// }]
// };
// }
// );
return tools;
}
-140
View File
@@ -1,140 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Debug Tools
*/
export function applyDebugTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_debug_open',
'Open the debug viewlet',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.debug.openDebugViewlet();
return {
content: [{
type: 'text' as const,
text: 'Opened debug viewlet'
}]
};
}
));
tools.push(server.tool(
'vscode_automation_debug_set_breakpoint',
'Set a breakpoint on a specific line',
{
lineNumber: z.number().describe('Line number to set breakpoint on')
},
async (args) => {
const { lineNumber } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.debug.setBreakpointOnLine(lineNumber);
return {
content: [{
type: 'text' as const,
text: `Set breakpoint on line ${lineNumber}`
}]
};
}
));
tools.push(server.tool(
'vscode_automation_debug_start',
'Start debugging',
async () => {
const app = await appService.getOrCreateApplication();
const result = await app.workbench.debug.startDebugging();
return {
content: [{
type: 'text' as const,
text: `Started debugging (result: ${result})`
}]
};
}
));
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_debug_stop',
// 'Stop debugging',
// async () => {
// await app.workbench.debug.stopDebugging();
// return {
// content: [{
// type: 'text' as const,
// text: 'Stopped debugging'
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_debug_step_over',
// 'Step over in debugger',
// async () => {
// await app.workbench.debug.stepOver();
// return {
// content: [{
// type: 'text' as const,
// text: 'Stepped over'
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_debug_step_in',
// 'Step into in debugger',
// async () => {
// await app.workbench.debug.stepIn();
// return {
// content: [{
// type: 'text' as const,
// text: 'Stepped in'
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_debug_step_out',
// 'Step out in debugger',
// async () => {
// await app.workbench.debug.stepOut();
// return {
// content: [{
// type: 'text' as const,
// text: 'Stepped out'
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_debug_continue',
// 'Continue execution in debugger',
// async () => {
// await app.workbench.debug.continue();
// return {
// content: [{
// type: 'text' as const,
// text: 'Continued execution'
// }]
// };
// }
// );
return tools;
}
-256
View File
@@ -1,256 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Editor Management Tools
*/
export function applyEditorTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_editor_open_file',
// 'Open a file in the VS Code editor through quick open',
// {
// fileName: z.string().describe('Name of the file to open (partial names work)')
// },
// async (args) => {
// const { fileName } = args;
// await app.workbench.quickaccess.openFileQuickAccessAndWait(fileName, fileName);
// return {
// content: [{
// type: 'text' as const,
// text: `Opened file: ${fileName}`
// }]
// };
// }
// );
// This one is critical as Playwright had trouble typing in monaco
tools.push(server.tool(
'vscode_automation_editor_type_text',
'Type text in the currently active editor',
{
text: z.string().describe('The text to type'),
filename: z.string().describe('Filename to target specific editor')
},
async (args) => {
const { text, filename } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.editor.waitForTypeInEditor(filename, text);
return {
content: [{
type: 'text' as const,
text: `Typed text: "${text}"`
}]
};
}
));
// Doesn't seem particularly useful
// server.tool(
// 'vscode_automation_editor_get_selection',
// 'Get the current selection in the editor',
// {
// filename: z.string().describe('Filename to target specific editor')
// },
// async (args) => {
// const { filename } = args;
// return new Promise((resolve, reject) => {
// const selectionHandler = (selection: { selectionStart: number; selectionEnd: number }) => {
// resolve({
// content: [{
// type: 'text' as const,
// text: `Selection: start=${selection.selectionStart}, end=${selection.selectionEnd}`
// }]
// });
// return true;
// };
// app.workbench.editor.waitForEditorSelection(filename, selectionHandler).catch(reject);
// });
// }
// );
// Doesn't seem particularly useful
// server.tool(
// 'vscode_automation_editor_go_to_definition',
// 'Go to definition of symbol at current cursor position',
// {
// filename: z.string().describe('File containing the symbol'),
// term: z.string().describe('The symbol/term to go to definition for'),
// line: z.number().describe('Line number where the symbol is located')
// },
// async (args) => {
// const { filename, term, line } = args;
// await app.workbench.editor.gotoDefinition(filename, term, line);
// return {
// content: [{
// type: 'text' as const,
// text: `Navigated to definition of "${term}" in ${filename} at line ${line}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_editor_peek_definition',
// 'Peek definition of symbol at current cursor position',
// {
// filename: z.string().describe('File containing the symbol'),
// term: z.string().describe('The symbol/term to peek definition for'),
// line: z.number().describe('Line number where the symbol is located')
// },
// async (args) => {
// const { filename, term, line } = args;
// await app.workbench.editor.peekDefinition(filename, term, line);
// return {
// content: [{
// type: 'text' as const,
// text: `Peeked definition of "${term}" in ${filename} at line ${line}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_editor_rename_symbol',
// 'Rename a symbol in the editor',
// {
// filename: z.string().describe('File containing the symbol'),
// line: z.number().describe('Line number where the symbol is located'),
// oldName: z.string().describe('Current name of the symbol'),
// newName: z.string().describe('New name for the symbol')
// },
// async (args) => {
// const { filename, line, oldName, newName } = args;
// await app.workbench.editor.rename(filename, line, oldName, newName);
// return {
// content: [{
// type: 'text' as const,
// text: `Renamed "${oldName}" to "${newName}" in ${filename} at line ${line}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_editor_find_references',
// 'Find all references to a symbol',
// {
// filename: z.string().describe('File containing the symbol'),
// term: z.string().describe('The symbol/term to find references for'),
// line: z.number().describe('Line number where the symbol is located')
// },
// async (args) => {
// const { filename, term, line } = args;
// await app.workbench.editor.findReferences(filename, term, line);
// return {
// content: [{
// type: 'text' as const,
// text: `Found references for "${term}" in ${filename} at line ${line}`
// }]
// };
// }
// );
// Editor File Management Tools
tools.push(server.tool(
'vscode_automation_editor_new_untitled_file',
'Create a new untitled file',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.editors.newUntitledFile();
return {
content: [{
type: 'text' as const,
text: 'Created new untitled file'
}]
};
}
));
tools.push(server.tool(
'vscode_automation_editor_save_file',
'Save the currently active file',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.editors.saveOpenedFile();
return {
content: [{
type: 'text' as const,
text: 'Saved active file'
}]
};
}
));
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_editor_select_tab',
// 'Select a specific tab by filename',
// {
// fileName: z.string().describe('Name of the file tab to select')
// },
// async (args) => {
// const { fileName } = args;
// await app.workbench.editors.selectTab(fileName);
// return {
// content: [{
// type: 'text' as const,
// text: `Selected tab: ${fileName}`
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_editor_wait_for_tab',
// 'Wait for a specific tab to appear',
// {
// fileName: z.string().describe('Name of the file tab to wait for'),
// isDirty: z.boolean().optional().describe('Whether to wait for the tab to be dirty (unsaved)')
// },
// async (args) => {
// const { fileName, isDirty = false } = args;
// await app.workbench.editors.waitForTab(fileName, isDirty);
// return {
// content: [{
// type: 'text' as const,
// text: `Tab appeared: ${fileName}${isDirty ? ' (dirty)' : ''}`
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_editor_wait_for_focus',
// 'Wait for an editor to have focus',
// {
// fileName: z.string().describe('Name of the file to wait for focus'),
// retryCount: z.number().optional().describe('Number of retries')
// },
// async (args) => {
// const { fileName, retryCount } = args;
// await app.workbench.editors.waitForEditorFocus(fileName, retryCount);
// return {
// content: [{
// type: 'text' as const,
// text: `Editor has focus: ${fileName}`
// }]
// };
// }
// );
return tools;
}
-31
View File
@@ -1,31 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Explorer and File Management Tools
*/
export function applyExplorerTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_explorer_open',
// 'Open the file explorer viewlet',
// async () => {
// await app.workbench.explorer.openExplorerView();
// return {
// content: [{
// type: 'text' as const,
// text: 'Opened file explorer'
// }]
// };
// }
// );
return tools;
}
@@ -1,94 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Extensions Tools
*/
export function applyExtensionsTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_extensions_search',
// 'Search for an extension by ID',
// {
// extensionId: z.string().describe('Extension ID to search for (e.g., "ms-python.python")')
// },
// async (args) => {
// const { extensionId } = args;
// await app.workbench.extensions.searchForExtension(extensionId);
// return {
// content: [{
// type: 'text' as const,
// text: `Searched for extension: ${extensionId}`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_extensions_open',
'Open an extension by ID',
{
extensionId: z.string().describe('Extension ID to open (e.g., "ms-python.python")')
},
async (args) => {
const { extensionId } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.extensions.openExtension(extensionId);
return {
content: [{
type: 'text' as const,
text: `Opened extension: ${extensionId}`
}]
};
}
));
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_extensions_close',
// 'Close an extension tab by title',
// {
// title: z.string().describe('Extension title to close')
// },
// async (args) => {
// const { title } = args;
// await app.workbench.extensions.closeExtension(title);
// return {
// content: [{
// type: 'text' as const,
// text: `Closed extension: ${title}`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_extensions_install',
'Install an extension by ID',
{
extensionId: z.string().describe('Extension ID to install (e.g., "ms-python.python")'),
waitUntilEnabled: z.boolean().optional().default(true).describe('Whether to wait until the extension is enabled')
},
async (args) => {
const { extensionId, waitUntilEnabled = true } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.extensions.installExtension(extensionId, waitUntilEnabled);
return {
content: [{
type: 'text' as const,
text: `Installed extension: ${extensionId}${waitUntilEnabled ? ' (waited until enabled)' : ''}`
}]
};
}
));
return tools;
}
-126
View File
@@ -1,126 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
// Import all tool modules
import { applyCoreTools } from './core.js';
import { applyEditorTools } from './editor.js';
import { applyTerminalTools } from './terminal.js';
import { applyDebugTools } from './debug.js';
import { applySearchTools } from './search.js';
import { applyExtensionsTools } from './extensions.js';
import { applyQuickAccessTools } from './quickAccess.js';
import { applyExplorerTools } from './explorer.js';
import { applyActivityBarTools } from './activityBar.js';
import { applySCMTools } from './scm.js';
import { applyStatusBarTools } from './statusbar.js';
import { applyProblemsTools } from './problems.js';
import { applySettingsTools } from './settings.js';
import { applyKeybindingsTools } from './keybindings.js';
import { applyNotebookTools } from './notebook.js';
import { applyLocalizationTools } from './localization.js';
import { applyTaskTools } from './task.js';
import { applyProfilerTools } from './profiler.js';
import { applyChatTools } from './chat.js';
import { applyWindowTools } from './windows.js';
import { ApplicationService } from '../../../scenario';
/**
* Apply all VS Code automation tools to the MCP server
* @param server - The MCP server instance
* @param appService - The application service instance
* @returns The registered tools from the server
*/
export function applyAllTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
let tools: RegisteredTool[] = [];
// Core Application Management Tools
tools = tools.concat(applyCoreTools(server, appService));
// Editor Management Tools
tools = tools.concat(applyEditorTools(server, appService));
// Terminal Management Tools
tools = tools.concat(applyTerminalTools(server, appService));
// Debug Tools
tools = tools.concat(applyDebugTools(server, appService));
// Search Tools
tools = tools.concat(applySearchTools(server, appService));
// Extensions Tools
tools = tools.concat(applyExtensionsTools(server, appService));
// Command Palette and Quick Access Tools
tools = tools.concat(applyQuickAccessTools(server, appService));
// Explorer and File Management Tools
tools = tools.concat(applyExplorerTools(server, appService));
// Activity Bar Tools
tools = tools.concat(applyActivityBarTools(server, appService));
// Source Control Management Tools
tools = tools.concat(applySCMTools(server, appService));
// Status Bar Tools
tools = tools.concat(applyStatusBarTools(server, appService));
// Problems Panel Tools
tools = tools.concat(applyProblemsTools(server, appService));
// Settings Editor Tools
tools = tools.concat(applySettingsTools(server, appService));
// Keybindings Editor Tools
tools = tools.concat(applyKeybindingsTools(server, appService));
// Notebook Tools
tools = tools.concat(applyNotebookTools(server, appService));
// Localization Tools
tools = tools.concat(applyLocalizationTools(server, appService));
// Task Tools
tools = tools.concat(applyTaskTools(server, appService));
// Profiler Tools
tools = tools.concat(applyProfilerTools(server, appService));
// Chat Tools
tools = tools.concat(applyChatTools(server, appService));
// Window Management Tools (for multi-window support)
tools = tools.concat(applyWindowTools(server, appService));
// Return all registered tools
return tools;
}
// Re-export individual tool functions for selective use
export {
applyCoreTools,
applyEditorTools,
applyTerminalTools,
applyDebugTools,
applySearchTools,
applyExtensionsTools,
applyQuickAccessTools,
applyExplorerTools,
applyActivityBarTools,
applySCMTools,
applyStatusBarTools,
applyProblemsTools,
applySettingsTools,
applyKeybindingsTools,
applyNotebookTools,
applyLocalizationTools,
applyTaskTools,
applyProfilerTools,
applyChatTools,
applyWindowTools
};
@@ -1,38 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Keybindings Editor Tools
*/
export function applyKeybindingsTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Seems too niche
// server.tool(
// 'vscode_automation_keybindings_update',
// 'Update a keybinding for a specific command',
// {
// command: z.string().describe('Command ID to update keybinding for'),
// commandName: z.string().optional().describe('Optional command display name'),
// keybinding: z.string().describe('New keybinding (e.g., "ctrl+k ctrl+c")'),
// keybindingTitle: z.string().describe('Display title for the keybinding')
// },
// async (args) => {
// const { command, commandName, keybinding, keybindingTitle } = args;
// await app.workbench.keybindingsEditor.updateKeybinding(command, commandName, keybinding, keybindingTitle);
// return {
// content: [{
// type: 'text' as const,
// text: `Updated keybinding for "${command}"${commandName ? ` (${commandName})` : ''}: ${keybinding} (${keybindingTitle})`
// }]
// };
// }
// );
return tools;
}
@@ -1,46 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Localization Tools
*/
export function applyLocalizationTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Seems too niche
// server.tool(
// 'vscode_automation_localization_get_locale_info',
// 'Get current locale information',
// async () => {
// const localeInfo = await app.workbench.localization.getLocaleInfo();
// return {
// content: [{
// type: 'text' as const,
// text: `Locale info:\n${JSON.stringify(localeInfo, null, 2)}`
// }]
// };
// }
// );
// Seems too niche
// server.tool(
// 'vscode_automation_localization_get_localized_strings',
// 'Get all localized strings',
// async () => {
// const localizedStrings = await app.workbench.localization.getLocalizedStrings();
// return {
// content: [{
// type: 'text' as const,
// text: `Localized strings:\n${JSON.stringify(localizedStrings, null, 2)}`
// }]
// };
// }
// );
return tools;
}
-248
View File
@@ -1,248 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Notebook Tools
*/
export function applyNotebookTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_notebook_open',
'Open a notebook',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.notebook.openNotebook();
return {
content: [{
type: 'text' as const,
text: 'Opened notebook'
}]
};
}
));
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_focus_next_cell',
// 'Focus the next cell in the notebook',
// async () => {
// await app.workbench.notebook.focusNextCell();
// return {
// content: [{
// type: 'text' as const,
// text: 'Focused next cell'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_focus_first_cell',
// 'Focus the first cell in the notebook',
// async () => {
// await app.workbench.notebook.focusFirstCell();
// return {
// content: [{
// type: 'text' as const,
// text: 'Focused first cell'
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_notebook_edit_cell',
'Enter edit mode for the current cell',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.notebook.editCell();
return {
content: [{
type: 'text' as const,
text: 'Entered cell edit mode'
}]
};
}
));
// Seems too niche
// server.tool(
// 'vscode_automation_notebook_stop_editing_cell',
// 'Exit edit mode for the current cell',
// async () => {
// await app.workbench.notebook.stopEditingCell();
// return {
// content: [{
// type: 'text' as const,
// text: 'Exited cell edit mode'
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_notebook_type_in_editor',
'Type text in the notebook cell editor',
{
text: z.string().describe('Text to type in the cell editor')
},
async (args) => {
const { text } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.notebook.waitForTypeInEditor(text);
return {
content: [{
type: 'text' as const,
text: `Typed in notebook cell: "${text}"`
}]
};
}
));
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_wait_for_cell_contents',
// 'Wait for specific contents in the active cell editor',
// {
// contents: z.string().describe('Expected contents in the active cell')
// },
// async (args) => {
// const { contents } = args;
// await app.workbench.notebook.waitForActiveCellEditorContents(contents);
// return {
// content: [{
// type: 'text' as const,
// text: `Active cell contains expected contents: "${contents}"`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_wait_for_markdown_contents',
// 'Wait for specific text in markdown cell output',
// {
// markdownSelector: z.string().describe('CSS selector for the markdown element'),
// text: z.string().describe('Expected text in the markdown output')
// },
// async (args) => {
// const { markdownSelector, text } = args;
// await app.workbench.notebook.waitForMarkdownContents(markdownSelector, text);
// return {
// content: [{
// type: 'text' as const,
// text: `Markdown content found: "${text}" in ${markdownSelector}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_insert_cell',
// 'Insert a new cell of specified type',
// {
// kind: z.enum(['markdown', 'code']).describe('Type of cell to insert')
// },
// async (args) => {
// const { kind } = args;
// await app.workbench.notebook.insertNotebookCell(kind);
// return {
// content: [{
// type: 'text' as const,
// text: `Inserted ${kind} cell`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_delete_active_cell',
// 'Delete the currently active cell',
// async () => {
// await app.workbench.notebook.deleteActiveCell();
// return {
// content: [{
// type: 'text' as const,
// text: 'Deleted active cell'
// }]
// };
// }
// );
// Seems too niche
// server.tool(
// 'vscode_automation_notebook_focus_in_cell_output',
// 'Focus inside cell output area',
// async () => {
// await app.workbench.notebook.focusInCellOutput();
// return {
// content: [{
// type: 'text' as const,
// text: 'Focused in cell output'
// }]
// };
// }
// );
// Seems too niche
// server.tool(
// 'vscode_automation_notebook_focus_out_cell_output',
// 'Focus outside cell output area',
// async () => {
// await app.workbench.notebook.focusOutCellOutput();
// return {
// content: [{
// type: 'text' as const,
// text: 'Focused out of cell output'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_execute_active_cell',
// 'Execute the currently active cell',
// async () => {
// await app.workbench.notebook.executeActiveCell();
// return {
// content: [{
// type: 'text' as const,
// text: 'Executed active cell'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_notebook_execute_cell_action',
// 'Execute a specific cell action using CSS selector',
// {
// selector: z.string().describe('CSS selector for the cell action to execute')
// },
// async (args) => {
// const { selector } = args;
// await app.workbench.notebook.executeCellAction(selector);
// return {
// content: [{
// type: 'text' as const,
// text: `Executed cell action: ${selector}`
// }]
// };
// }
// );
return tools;
}
-113
View File
@@ -1,113 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Problems Panel Tools
*/
export function applyProblemsTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_problems_show',
'Show the problems view',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.problems.showProblemsView();
return {
content: [{
type: 'text' as const,
text: 'Showed problems view'
}]
};
}
));
tools.push(server.tool(
'vscode_automation_problems_hide',
'Hide the problems view',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.problems.hideProblemsView();
return {
content: [{
type: 'text' as const,
text: 'Hid problems view'
}]
};
}
));
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_problems_wait_for_view',
// 'Wait for the problems view to appear',
// async () => {
// await app.workbench.problems.waitForProblemsView();
// return {
// content: [{
// type: 'text' as const,
// text: 'Problems view is now visible'
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_problems_get_selector_in_view',
'Get CSS selector for problems of a specific severity in the problems view',
{
severity: z.enum(['WARNING', 'ERROR']).describe('Problem severity (WARNING or ERROR)')
},
async (args) => {
const { severity } = args;
const severityMap: Record<string, number> = {
'WARNING': 0,
'ERROR': 1
};
// This is a static method that returns a selector, not an async operation
const app = await appService.getOrCreateApplication();
// eslint-disable-next-line local/code-no-any-casts
const selector = (app.workbench.problems.constructor as any).getSelectorInProblemsView(severityMap[severity]);
return {
content: [{
type: 'text' as const,
text: `CSS selector for ${severity} problems in view: ${selector}`
}]
};
}
));
// Seems too niche
// server.tool(
// 'vscode_automation_problems_get_selector_in_editor',
// 'Get CSS selector for problems of a specific severity in the editor',
// {
// severity: z.enum(['WARNING', 'ERROR']).describe('Problem severity (WARNING or ERROR)')
// },
// async (args) => {
// const { severity } = args;
// const severityMap: Record<string, number> = {
// 'WARNING': 0,
// 'ERROR': 1
// };
// // This is a static method that returns a selector, not an async operation
// const selector = (app.workbench.problems.constructor as any).getSelectorInEditor(severityMap[severity]);
// return {
// content: [{
// type: 'text' as const,
// text: `CSS selector for ${severity} problems in editor: ${selector}`
// }]
// };
// }
// );
return tools;
}
-38
View File
@@ -1,38 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Profiler Tools
* Note: Due to MCP limitations, these tools provide information about profiler methods
* but cannot execute them directly as they require function parameters
*/
export function applyProfilerTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Seems too niche
// server.tool(
// 'vscode_automation_profiler_info',
// 'Get information about available profiler methods',
// {},
// async () => {
// return {
// content: [{
// type: 'text' as const,
// text: `Profiler methods available:
// - checkObjectLeaks(classNames, fn): Check for object leaks during function execution
// - checkHeapLeaks(classNames, fn): Check for heap leaks during function execution
// Note: These methods require function parameters and cannot be executed directly via MCP.
// They are primarily used within VS Code's automation test framework.`
// }]
// };
// }
// );
return tools;
}
@@ -1,96 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Command Palette and Quick Access Tools
*/
export function applyQuickAccessTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_command_run',
'Run a command by name through the command palette',
{
command: z.string().describe('The command name to run'),
exactMatch: z.boolean().optional().describe('Whether to require exact label match')
},
async (args) => {
const { command, exactMatch } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.quickaccess.runCommand(command, { match: exactMatch ? 'exactLabel' : 'fuzzy', keepOpen: true });
return {
content: [{
type: 'text' as const,
text: `Executed command: "${command}"`
}]
};
}
));
tools.push(server.tool(
'vscode_automation_quick_open_file',
'Open quick file search and select a file',
{
fileName: z.string().describe('Name or pattern of file to search for'),
exactFileName: z.string().optional().describe('Exact file name to select from results')
},
async (args) => {
const { fileName, exactFileName } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.quickaccess.openFileQuickAccessAndWait(fileName, exactFileName || fileName);
return {
content: [{
type: 'text' as const,
text: `Opened file through quick open: "${fileName}"`
}]
};
}
));
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_quick_input_type',
// 'Type text into the currently open quick input',
// {
// text: z.string().describe('Text to type into quick input')
// },
// async (args) => {
// const { text } = args;
// await app.workbench.quickinput.type(text);
// return {
// content: [{
// type: 'text' as const,
// text: `Typed in quick input: "${text}"`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_quick_input_select_item',
// 'Select an item from the quick input list',
// {
// index: z.number().optional().describe('Index of item to select (0-based)'),
// keepOpen: z.boolean().optional().describe('Keep quick input open after selection')
// },
// async (args) => {
// const { index = 0, keepOpen } = args;
// await app.workbench.quickinput.selectQuickInputElement(index, keepOpen);
// return {
// content: [{
// type: 'text' as const,
// text: `Selected quick input item at index ${index}`
// }]
// };
// }
// );
return tools;
}
-143
View File
@@ -1,143 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Source Control Management Tools
*/
export function applySCMTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_open',
// 'Open the Source Control Management viewlet',
// async () => {
// await app.workbench.scm.openSCMViewlet();
// return {
// content: [{
// type: 'text' as const,
// text: 'Opened SCM viewlet'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_wait_for_change',
// 'Wait for a specific change to appear in SCM',
// {
// name: z.string().describe('Name of the file change to wait for'),
// type: z.string().optional().describe('Type of change (optional)')
// },
// async (args) => {
// const { name, type } = args;
// await app.workbench.scm.waitForChange(name, type);
// return {
// content: [{
// type: 'text' as const,
// text: `Change detected: ${name}${type ? ` (${type})` : ''}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_refresh',
// 'Refresh the SCM viewlet',
// async () => {
// await app.workbench.scm.refreshSCMViewlet();
// return {
// content: [{
// type: 'text' as const,
// text: 'Refreshed SCM viewlet'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_open_change',
// 'Open a specific change in the SCM viewlet',
// {
// name: z.string().describe('Name of the file change to open')
// },
// async (args) => {
// const { name } = args;
// await app.workbench.scm.openChange(name);
// return {
// content: [{
// type: 'text' as const,
// text: `Opened change: ${name}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_stage',
// 'Stage a specific file change',
// {
// name: z.string().describe('Name of the file to stage')
// },
// async (args) => {
// const { name } = args;
// await app.workbench.scm.stage(name);
// return {
// content: [{
// type: 'text' as const,
// text: `Staged file: ${name}`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_scm_unstage',
// 'Unstage a specific file change',
// {
// name: z.string().describe('Name of the file to unstage')
// },
// async (args) => {
// const { name } = args;
// await app.workbench.scm.unstage(name);
// return {
// content: [{
// type: 'text' as const,
// text: `Unstaged file: ${name}`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_scm_commit',
'Commit staged changes with a message',
{
message: z.string().describe('Commit message')
},
async (args) => {
const { message } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.scm.commit(message);
return {
content: [{
type: 'text' as const,
text: `Committed changes with message: "${message}"`
}]
};
}
));
return tools;
}
-101
View File
@@ -1,101 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Search Tools
*/
export function applySearchTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_search_open',
// 'Open the search viewlet',
// async () => {
// await app.workbench.search.openSearchViewlet();
// return {
// content: [{
// type: 'text' as const,
// text: 'Opened search viewlet'
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_search_for_text',
'Search for text in files',
{
searchText: z.string().describe('Text to search for')
},
async (args) => {
const { searchText } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.search.openSearchViewlet();
await app.workbench.search.searchFor(searchText);
return {
content: [{
type: 'text' as const,
text: `Searched for: "${searchText}"`
}]
};
}
));
// Seems too niche
// server.tool(
// 'vscode_automation_search_set_files_to_include',
// 'Set files to include in search',
// {
// pattern: z.string().describe('File pattern to include (e.g., "*.ts", "src/**")')
// },
// async (args) => {
// const { pattern } = args;
// await app.workbench.search.setFilesToIncludeText(pattern);
// return {
// content: [{
// type: 'text' as const,
// text: `Set files to include: "${pattern}"`
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_search_submit',
// 'Submit the current search',
// async () => {
// await app.workbench.search.submitSearch();
// return {
// content: [{
// type: 'text' as const,
// text: 'Submitted search'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_search_clear_results',
// 'Clear search results',
// async () => {
// await app.workbench.search.clearSearchResults();
// return {
// content: [{
// type: 'text' as const,
// text: 'Cleared search results'
// }]
// };
// }
// );
return tools;
}
-120
View File
@@ -1,120 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Settings Editor Tools
*/
export function applySettingsTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// I don't think we need this and the batch version
// server.tool(
// 'vscode_automation_settings_add_user_setting',
// 'Add a single user setting key-value pair',
// {
// setting: z.string().describe('Setting key (e.g., "editor.fontSize")'),
// value: z.string().describe('Setting value (as JSON string)')
// },
// async (args) => {
// const { setting, value } = args;
// await app.workbench.settingsEditor.addUserSetting(setting, value);
// return {
// content: [{
// type: 'text' as const,
// text: `Added user setting: ${setting} = ${value}`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_settings_add_user_settings',
'Add multiple user settings at once',
{
settings: z.array(z.tuple([z.string(), z.string()])).describe('Array of [key, value] setting pairs')
},
async (args) => {
const { settings } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.settingsEditor.addUserSettings(settings as [string, string][]);
return {
content: [{
type: 'text' as const,
text: `Added ${settings.length} user settings: ${settings.map(([k, v]) => `${k}=${v}`).join(', ')}`
}]
};
}
));
tools.push(server.tool(
'vscode_automation_settings_clear_user_settings',
'Clear all user settings',
async () => {
const app = await appService.getOrCreateApplication();
await app.workbench.settingsEditor.clearUserSettings();
return {
content: [{
type: 'text' as const,
text: 'Cleared all user settings'
}]
};
}
));
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_settings_open_user_file',
// 'Open the user settings JSON file',
// async () => {
// await app.workbench.settingsEditor.openUserSettingsFile();
// return {
// content: [{
// type: 'text' as const,
// text: 'Opened user settings file'
// }]
// };
// }
// );
// Playwright can probably figure this one out
// server.tool(
// 'vscode_automation_settings_open_user_ui',
// 'Open the user settings UI',
// async () => {
// await app.workbench.settingsEditor.openUserSettingsUI();
// return {
// content: [{
// type: 'text' as const,
// text: 'Opened user settings UI'
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_settings_search_ui',
'Search for settings in the settings UI',
{
query: z.string().describe('Search query for settings')
},
async (args) => {
const { query } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.settingsEditor.searchSettingsUI(query);
return {
content: [{
type: 'text' as const,
text: `Searched settings UI for: "${query}"`
}]
};
}
));
return tools;
}
-135
View File
@@ -1,135 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
/**
* Status Bar Tools
*/
export function applyStatusBarTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Seems too niche
// server.tool(
// 'vscode_automation_statusbar_wait_for_element',
// 'Wait for a specific status bar element to appear',
// {
// element: z.enum([
// 'BRANCH_STATUS',
// 'SYNC_STATUS',
// 'PROBLEMS_STATUS',
// 'SELECTION_STATUS',
// 'INDENTATION_STATUS',
// 'ENCODING_STATUS',
// 'EOL_STATUS',
// 'LANGUAGE_STATUS'
// ]).describe('Status bar element to wait for')
// },
// async (args) => {
// const { element } = args;
// // Map string to enum value
// const elementMap: Record<string, number> = {
// 'BRANCH_STATUS': 0,
// 'SYNC_STATUS': 1,
// 'PROBLEMS_STATUS': 2,
// 'SELECTION_STATUS': 3,
// 'INDENTATION_STATUS': 4,
// 'ENCODING_STATUS': 5,
// 'EOL_STATUS': 6,
// 'LANGUAGE_STATUS': 7
// };
// await app.workbench.statusbar.waitForStatusbarElement(elementMap[element]);
// return {
// content: [{
// type: 'text' as const,
// text: `Status bar element found: ${element}`
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_statusbar_click',
// 'Click on a specific status bar element',
// {
// element: z.enum([
// 'BRANCH_STATUS',
// 'SYNC_STATUS',
// 'PROBLEMS_STATUS',
// 'SELECTION_STATUS',
// 'INDENTATION_STATUS',
// 'ENCODING_STATUS',
// 'EOL_STATUS',
// 'LANGUAGE_STATUS'
// ]).describe('Status bar element to click')
// },
// async (args) => {
// const { element } = args;
// // Map string to enum value
// const elementMap: Record<string, number> = {
// 'BRANCH_STATUS': 0,
// 'SYNC_STATUS': 1,
// 'PROBLEMS_STATUS': 2,
// 'SELECTION_STATUS': 3,
// 'INDENTATION_STATUS': 4,
// 'ENCODING_STATUS': 5,
// 'EOL_STATUS': 6,
// 'LANGUAGE_STATUS': 7
// };
// await app.workbench.statusbar.clickOn(elementMap[element]);
// return {
// content: [{
// type: 'text' as const,
// text: `Clicked status bar element: ${element}`
// }]
// };
// }
// );
// Seems too niche
// server.tool(
// 'vscode_automation_statusbar_wait_for_eol',
// 'Wait for a specific End of Line (EOL) type in the status bar',
// {
// eol: z.string().describe('EOL type to wait for (e.g., "LF", "CRLF")')
// },
// async (args) => {
// const { eol } = args;
// const result = await app.workbench.statusbar.waitForEOL(eol);
// return {
// content: [{
// type: 'text' as const,
// text: `EOL status found: ${result}`
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_statusbar_wait_for_text',
// 'Wait for specific text to appear in a status bar element',
// {
// title: z.string().describe('Title/identifier of the status bar element'),
// text: z.string().describe('Text to wait for in the status bar element')
// },
// async (args) => {
// const { title, text } = args;
// await app.workbench.statusbar.waitForStatusbarText(title, text);
// return {
// content: [{
// type: 'text' as const,
// text: `Status bar text found - ${title}: "${text}"`
// }]
// };
// }
// );
return tools;
}
-83
View File
@@ -1,83 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Task Tools
*/
export function applyTaskTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
// Seems too niche
// server.tool(
// 'vscode_automation_task_assert_tasks',
// 'Assert that specific tasks exist with given properties',
// {
// filter: z.string().describe('Filter string for tasks'),
// expected: z.array(z.object({
// label: z.string().optional(),
// type: z.string().optional(),
// command: z.string().optional(),
// identifier: z.string().optional(),
// group: z.string().optional(),
// isBackground: z.boolean().optional(),
// promptOnClose: z.boolean().optional(),
// icon: z.object({
// id: z.string().optional(),
// color: z.string().optional()
// }).optional(),
// hide: z.boolean().optional()
// })).describe('Array of expected task properties'),
// type: z.enum(['run', 'configure']).describe('Type of task operation')
// },
// async (args) => {
// const { filter, expected, type } = args;
// await app.workbench.task.assertTasks(filter, expected, type);
// return {
// content: [{
// type: 'text' as const,
// text: `Asserted ${expected.length} tasks for ${type} with filter: "${filter}"`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_task_configure',
'Configure a task with specific properties',
{
properties: z.object({
label: z.string().optional(),
type: z.string().optional(),
command: z.string().optional(),
identifier: z.string().optional(),
group: z.string().optional(),
isBackground: z.boolean().optional(),
promptOnClose: z.boolean().optional(),
icon: z.object({
id: z.string().optional(),
color: z.string().optional()
}).optional(),
hide: z.boolean().optional()
}).describe('Task configuration properties')
},
async (args) => {
const { properties } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.task.configureTask(properties);
return {
content: [{
type: 'text' as const,
text: `Configured task: ${properties.label || properties.identifier || 'unnamed task'}`
}]
};
}
));
return tools;
}
-143
View File
@@ -1,143 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { ApplicationService } from '../../../scenario';
import { z } from 'zod';
/**
* Terminal Management Tools
*/
export function applyTerminalTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_terminal_create',
'Create a new terminal',
{
expectedLocation: z.enum(['editor', 'panel']).optional().describe('Expected location of terminal (editor or panel)')
},
async (args) => {
const { expectedLocation } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.terminal.createTerminal(expectedLocation);
return {
content: [{
type: 'text' as const,
text: `Created new terminal${expectedLocation ? ` in ${expectedLocation}` : ''}`
}]
};
}
));
tools.push(server.tool(
'vscode_automation_terminal_run_command',
'Run a command in the terminal',
{
command: z.string().describe('Command to run in the terminal'),
skipEnter: z.boolean().optional().describe('Skip pressing enter after typing command')
},
async (args) => {
const { command, skipEnter } = args;
const app = await appService.getOrCreateApplication();
await app.workbench.terminal.runCommandInTerminal(command, skipEnter);
return {
content: [{
type: 'text' as const,
text: `Ran command in terminal: "${command}"`
}]
};
}
));
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_terminal_wait_for_text',
// 'Wait for specific text to appear in terminal output',
// {
// acceptFunction: z.string().describe('JavaScript function body that takes buffer array and returns boolean'),
// message: z.string().optional().describe('Optional message for waiting'),
// splitIndex: z.number().optional().describe('Split terminal index (0 or 1)')
// },
// async (args) => {
// const { acceptFunction, message, splitIndex } = args;
// // Create function from string
// const acceptFn = new Function('buffer', acceptFunction) as (buffer: string[]) => boolean;
// const terminalSplitIndex = splitIndex === 0 ? 0 : splitIndex === 1 ? 1 : undefined;
// await app.workbench.terminal.waitForTerminalText(acceptFn, message, terminalSplitIndex);
// return {
// content: [{
// type: 'text' as const,
// text: `Terminal text condition met: ${message || 'custom condition'}`
// }]
// };
// }
// );
tools.push(server.tool(
'vscode_automation_terminal_get_groups',
'Get current terminal groups information',
async () => {
const app = await appService.getOrCreateApplication();
const groups = await app.workbench.terminal.getTerminalGroups();
return {
content: [{
type: 'text' as const,
text: `Terminal groups:\n${JSON.stringify(groups, null, 2)}`
}]
};
}
));
// Seems too niche and redundant with runCommand tool
// server.tool(
// 'vscode_automation_terminal_run_command_by_id',
// 'Run a terminal command by ID',
// {
// commandId: z.enum([
// 'workbench.action.terminal.split',
// 'workbench.action.terminal.killAll',
// 'workbench.action.terminal.unsplit',
// 'workbench.action.terminal.join',
// 'workbench.action.terminal.toggleTerminal',
// 'workbench.action.createTerminalEditor',
// 'workbench.action.createTerminalEditorSide',
// 'workbench.action.terminal.moveToTerminalPanel',
// 'workbench.action.terminal.moveToEditor',
// 'workbench.action.terminal.newWithProfile',
// 'workbench.action.terminal.selectDefaultShell',
// 'workbench.action.terminal.detachSession',
// 'workbench.action.terminal.new'
// ]).describe('Terminal command ID to execute'),
// expectedLocation: z.enum(['editor', 'panel']).optional().describe('Expected location after command')
// },
// async (args) => {
// const { commandId, expectedLocation } = args;
// await app.workbench.terminal.runCommand(commandId as any, expectedLocation);
// return {
// content: [{
// type: 'text' as const,
// text: `Executed terminal command: ${commandId}`
// }]
// };
// }
// );
// Playwright can probably figure this out
// server.tool(
// 'vscode_automation_terminal_split',
// 'Split the current terminal',
// async () => {
// await app.workbench.terminal.clickSplitButton();
// return {
// content: [{
// type: 'text' as const,
// text: 'Split terminal'
// }]
// };
// }
// );
return tools;
}
-442
View File
@@ -1,442 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { ApplicationService } from '../../../scenario';
/**
* Create a standardized text response for window tools
*/
function textResponse(text: string) {
return {
content: [{ type: 'text' as const, text }]
};
}
/**
* Window Management Tools for multi-window support.
* These tools provide Playwright-based window interactions through the automation driver.
*/
export function applyWindowTools(server: McpServer, appService: ApplicationService): RegisteredTool[] {
const tools: RegisteredTool[] = [];
tools.push(server.tool(
'vscode_automation_list_windows',
'List all open VS Code windows with their index and URL',
async () => {
const app = await appService.getOrCreateApplication();
const windowInfo = app.code.driver.getWindowsInfo();
return textResponse(`Open windows (${windowInfo.length}):\n${JSON.stringify(windowInfo, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_switch_window',
'Switch to a different VS Code window by index or URL pattern (e.g., "agent.html")',
{
indexOrUrl: z.union([z.number(), z.string()]).describe('Window index (0-based) or URL pattern to match (e.g., "agent.html", "workbench")')
},
async ({ indexOrUrl }) => {
const app = await appService.getOrCreateApplication();
const switched = app.code.driver.switchToWindow(indexOrUrl);
if (switched) {
return textResponse(`Switched to window (URL: ${switched.url()})`);
}
const windowInfo = app.code.driver.getWindowsInfo();
const availableWindows = windowInfo.map(w => ` ${w.index}: ${w.url}`).join('\n');
return textResponse(`Failed to switch window. Window not found for: ${indexOrUrl}\n\nAvailable windows:\n${availableWindows}`);
}
));
tools.push(server.tool(
'vscode_automation_get_current_window',
'Get information about the currently active window',
async () => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const windowInfo = driver.getWindowsInfo();
const current = windowInfo.find(w => w.isCurrent);
return textResponse(`Current window:\nIndex: ${current?.index ?? -1}\nURL: ${current?.url ?? 'Unknown'}`);
}
));
tools.push(server.tool(
'vscode_automation_window_screenshot',
'Take a screenshot of the current window (respects the window set by vscode_automation_switch_window)',
{
fullPage: z.boolean().optional().describe('When true, takes a screenshot of the full scrollable page')
},
async ({ fullPage }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const screenshotBuffer = await driver.screenshotBuffer(fullPage ?? false);
const url = driver.currentPage.url();
return {
content: [
{ type: 'text' as const, text: `Screenshot (URL: ${url})` },
{ type: 'image' as const, data: screenshotBuffer.toString('base64'), mimeType: 'image/png' }
]
};
}
));
tools.push(server.tool(
'vscode_automation_window_snapshot',
'Capture accessibility snapshot of the current window. Returns the page structure with element references that can be used for interactions.',
async () => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const snapshot = await driver.getAccessibilitySnapshot();
const url = driver.currentPage.url();
return textResponse(`Page snapshot (URL: ${url}):\n\n${snapshot}`);
}
));
tools.push(server.tool(
'vscode_automation_window_list_cdp_targets',
'List Chromium CDP targets, including out-of-process iframes',
async () => {
const app = await appService.getOrCreateApplication();
const targets = await app.code.driver.getCDPTargets();
return textResponse(`CDP targets (${targets.length}):\n${JSON.stringify(targets, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_window_cdp_target_evaluate',
'Evaluate JavaScript in a Chromium CDP target',
{
targetId: z.string().describe('CDP target ID'),
frameUrlPattern: z.string().optional().describe('Optional child frame URL substring'),
expression: z.string().describe('JavaScript expression to evaluate')
},
async ({ targetId, frameUrlPattern, expression }) => {
const app = await appService.getOrCreateApplication();
const result = frameUrlPattern
? await app.code.driver.evaluateCDPTargetFrame(targetId, frameUrlPattern, expression)
: await app.code.driver.evaluateCDPTarget(targetId, expression);
return textResponse(`Result:\n${JSON.stringify(result, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_window_click',
'Click on an element in the current window using a CSS selector',
{
selector: z.string().describe('CSS selector for the element to click'),
button: z.enum(['left', 'right', 'middle']).optional().describe('Mouse button to click'),
clickCount: z.number().optional().describe('Number of clicks (1 for single, 2 for double)')
},
async ({ selector, button, clickCount }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.clickSelector(selector, { button, clickCount });
return textResponse(`Clicked "${selector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_type',
'Type text into an element in the current window',
{
selector: z.string().describe('CSS selector for the element to type into'),
text: z.string().describe('Text to type'),
slowly: z.boolean().optional().describe('Whether to type one character at a time (useful for triggering key handlers)')
},
async ({ selector, text, slowly }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.typeText(selector, text, slowly ?? false);
return textResponse(`Typed "${text}" into "${selector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_evaluate',
'Evaluate JavaScript in the current window',
{
expression: z.string().describe('JavaScript expression to evaluate')
},
async ({ expression }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const result = await driver.evaluateExpression(expression);
return textResponse(`Result:\n${JSON.stringify(result, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_window_locator',
'Get information about elements matching a selector in the current window',
{
selector: z.string().describe('CSS selector to find elements'),
action: z.enum(['count', 'textContent', 'innerHTML', 'boundingBox', 'isVisible']).optional().describe('Action to perform on matched elements')
},
async ({ selector, action }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const result = await driver.getLocatorInfo(selector, action);
return textResponse(`Locator "${selector}":\n${JSON.stringify(result, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_window_wait_for_selector',
'Wait for an element to appear in the current window',
{
selector: z.string().describe('CSS selector to wait for'),
state: z.enum(['attached', 'detached', 'visible', 'hidden']).optional().describe('State to wait for'),
timeout: z.number().optional().describe('Timeout in milliseconds')
},
async ({ selector, state, timeout }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.waitForElement(selector, { state, timeout });
return textResponse(`Element "${selector}" is now ${state ?? 'visible'}`);
}
));
tools.push(server.tool(
'vscode_automation_window_hover',
'Hover over an element in the current window',
{
selector: z.string().describe('CSS selector for the element to hover over')
},
async ({ selector }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.hoverSelector(selector);
return textResponse(`Hovered over "${selector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_drag',
'Drag from one element to another in the current window',
{
sourceSelector: z.string().describe('CSS selector for the source element'),
targetSelector: z.string().describe('CSS selector for the target element')
},
async ({ sourceSelector, targetSelector }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.dragSelector(sourceSelector, targetSelector);
return textResponse(`Dragged from "${sourceSelector}" to "${targetSelector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_press_key',
'Press a key or key combination in the current window',
{
key: z.string().describe('Key to press (e.g., "Enter", "Tab", "Control+c", "Meta+v")')
},
async ({ key }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.pressKey(key);
return textResponse(`Pressed key "${key}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_keyboard_type',
'Type text with Playwright keyboard events in the currently focused element',
{
text: z.string().describe('Text to type'),
delay: z.number().optional().describe('Delay between key presses in milliseconds')
},
async ({ text, delay }) => {
const app = await appService.getOrCreateApplication();
await app.code.driver.currentPage.keyboard.type(text, { delay });
return textResponse(`Typed ${text.length} character(s) with keyboard events`);
}
));
tools.push(server.tool(
'vscode_automation_window_press_selector',
'Press a key or key combination on a specific element in the current window',
{
selector: z.string().describe('CSS selector for the target element'),
key: z.string().describe('Key to press')
},
async ({ selector, key }) => {
const app = await appService.getOrCreateApplication();
await app.code.driver.currentPage.locator(selector).press(key);
return textResponse(`Pressed "${key}" on "${selector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_mouse_move',
'Move mouse to a specific position in the current window',
{
x: z.number().describe('X coordinate'),
y: z.number().describe('Y coordinate')
},
async ({ x, y }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.mouseMove(x, y);
return textResponse(`Moved mouse to (${x}, ${y})`);
}
));
tools.push(server.tool(
'vscode_automation_window_mouse_click',
'Click at a specific position in the current window',
{
x: z.number().describe('X coordinate'),
y: z.number().describe('Y coordinate'),
button: z.enum(['left', 'right', 'middle']).optional().describe('Mouse button to click'),
clickCount: z.number().optional().describe('Number of clicks (1 for single, 2 for double)')
},
async ({ x, y, button, clickCount }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.mouseClick(x, y, { button, clickCount });
return textResponse(`Clicked at (${x}, ${y})`);
}
));
tools.push(server.tool(
'vscode_automation_window_mouse_drag',
'Drag from one position to another in the current window',
{
startX: z.number().describe('Starting X coordinate'),
startY: z.number().describe('Starting Y coordinate'),
endX: z.number().describe('Ending X coordinate'),
endY: z.number().describe('Ending Y coordinate')
},
async ({ startX, startY, endX, endY }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.mouseDrag(startX, startY, endX, endY);
return textResponse(`Dragged from (${startX}, ${startY}) to (${endX}, ${endY})`);
}
));
tools.push(server.tool(
'vscode_automation_window_select_option',
'Select an option in a dropdown in the current window',
{
selector: z.string().describe('CSS selector for the select element'),
value: z.union([z.string(), z.array(z.string())]).describe('Value(s) to select')
},
async ({ selector, value }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const selected = await driver.selectOption(selector, value);
return textResponse(`Selected "${selected.join(', ')}" in "${selector}"`);
}
));
tools.push(server.tool(
'vscode_automation_window_fill_form',
'Fill multiple form fields at once in the current window',
{
fields: z.array(z.object({
selector: z.string().describe('CSS selector for the form field'),
value: z.string().describe('Value to fill')
})).describe('Array of fields to fill')
},
async ({ fields }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.fillForm(fields);
return textResponse(`Filled ${fields.length} form field(s)`);
}
));
tools.push(server.tool(
'vscode_automation_window_console_messages',
'Get console messages from the current window',
async () => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const messages = await driver.getConsoleMessages();
return textResponse(`Console messages (${messages.length}):\n${JSON.stringify(messages, null, 2)}`);
}
));
tools.push(server.tool(
'vscode_automation_window_wait_for_text',
'Wait for text to appear or disappear in the current window',
{
text: z.string().optional().describe('Text to wait for to appear'),
textGone: z.string().optional().describe('Text to wait for to disappear'),
timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)')
},
async ({ text, textGone, timeout }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.waitForText({ text, textGone, timeout });
return textResponse(`Waited for ${text ? `"${text}" to appear` : ''}${textGone ? `"${textGone}" to disappear` : ''}`);
}
));
tools.push(server.tool(
'vscode_automation_window_wait_for_time',
'Wait for a specified time in the current window',
{
seconds: z.number().describe('Time to wait in seconds')
},
async ({ seconds }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
await driver.waitForTime(seconds * 1000);
return textResponse(`Waited for ${seconds} second(s)`);
}
));
tools.push(server.tool(
'vscode_automation_window_verify_element_visible',
'Verify an element is visible in the current window',
{
selector: z.string().describe('CSS selector for the element to verify')
},
async ({ selector }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const isVisible = await driver.verifyElementVisible(selector);
return textResponse(isVisible ? `✓ Element "${selector}" is visible` : `✗ Element "${selector}" is NOT visible`);
}
));
tools.push(server.tool(
'vscode_automation_window_verify_text_visible',
'Verify text is visible in the current window',
{
text: z.string().describe('Text to verify is visible')
},
async ({ text }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const isVisible = await driver.verifyTextVisible(text);
return textResponse(isVisible ? `✓ Text "${text}" is visible` : `✗ Text "${text}" is NOT visible`);
}
));
tools.push(server.tool(
'vscode_automation_window_get_input_value',
'Get the value of an input element in the current window',
{
selector: z.string().describe('CSS selector for the input element')
},
async ({ selector }) => {
const app = await appService.getOrCreateApplication();
const driver = app.code.driver;
const value = await driver.getInputValue(selector);
return textResponse(`Input "${selector}" value: "${value}"`);
}
));
return tools;
}
-85
View File
@@ -1,85 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { EvidenceService, JSONValue } from '../../scenario';
const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() => z.union([
z.string(),
z.number(),
z.boolean(),
z.null(),
z.array(jsonValueSchema),
z.record(z.string(), jsonValueSchema)
]));
function isHttpUrl(value: string): boolean {
try {
return ['http:', 'https:'].includes(new URL(value).protocol);
} catch {
return false;
}
}
export function applyEvidenceStartTool(server: McpServer, evidenceService: EvidenceService): RegisteredTool {
return server.tool(
'vscode_automation_evidence_start',
'Start VS Code with video and trace recording for a UI validation scenario',
{
scenarioId: z.string().describe('Stable scenario identifier'),
title: z.string().describe('Human-readable scenario title'),
source: z.string().url().refine(isHttpUrl, 'Source must use HTTP or HTTPS').optional().describe('Source test-plan issue URL'),
scenarioPath: z.string().optional().describe('Path to the Markdown scenario definition'),
workspacePath: z.string().optional().describe('Workspace or folder to open'),
userSettings: z.record(z.string(), jsonValueSchema).optional().describe('User settings to seed before VS Code starts'),
extraArgs: z.array(z.string()).optional().describe('Additional VS Code command-line arguments')
},
async ({ scenarioId, title, source, scenarioPath, workspacePath, userSettings, extraArgs }) => {
const runPath = await evidenceService.start(scenarioId, title, source, scenarioPath, workspacePath, userSettings, extraArgs);
return {
content: [{ type: 'text' as const, text: `Evidence capture started: ${runPath}` }]
};
}
);
}
export function applyEvidenceTools(server: McpServer, evidenceService: EvidenceService): RegisteredTool[] {
return [
server.tool(
'vscode_automation_evidence_step',
'Mark a scenario step in the video and save a screenshot of the current VS Code window',
{
id: z.string().describe('Stable step identifier from the scenario'),
title: z.string().describe('Human-readable step title'),
status: z.enum(['started', 'passed', 'failed', 'skipped']).describe('Step lifecycle status'),
details: z.string().optional().describe('Validation result or failure details')
},
async ({ id, title, status, details }) => {
const result = await evidenceService.step(id, title, status, details);
return {
content: [
{ type: 'text' as const, text: `Evidence saved: ${result.screenshotPath}` },
{ type: 'image' as const, data: result.screenshot.toString('base64'), mimeType: 'image/png' }
]
};
}
),
server.tool(
'vscode_automation_evidence_finish',
'Finish a UI validation scenario, stop VS Code, and write the evidence report',
{
outcome: z.enum(['passed', 'failed', 'aborted']).describe('Overall scenario outcome'),
notes: z.string().optional().describe('Run summary or blocking condition')
},
async ({ outcome, notes }) => {
const reportPath = await evidenceService.finish(outcome, notes);
return {
content: [{ type: 'text' as const, text: `Evidence report written: ${reportPath}` }]
};
}
)
];
}
-23
View File
@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { getServer } from './automation';
import { ApplicationService, opts } from '../../scenario';
const transport: StdioServerTransport = new StdioServerTransport();
(async () => {
const appService = new ApplicationService();
const server = await getServer(appService);
if (opts.autostart) {
await appService.getOrCreateApplication();
}
await server.connect(transport);
})().catch(err => {
transport.close();
console.error('Error occurred while connecting to server:', err);
process.exit(1);
});
-26
View File
@@ -1,26 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": false,
"removeComments": false,
"preserveConstEnums": true,
"target": "es2024",
"strict": true,
"noUnusedParameters": false,
"noUnusedLocals": true,
"rootDir": "./src",
"outDir": "out",
"sourceMap": true,
"skipLibCheck": true,
"lib": [
"esnext", // for #201187
"dom"
],
"types": [
"node"
]
},
"exclude": [
"node_modules"
]
}