# VSCode Automation MCP Server

[![npm version](https://badge.fury.io/js/vscode-automation-mcp.svg)](https://www.npmjs.com/package/vscode-automation-mcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A **Model Context Protocol (MCP)** server that enables AI agents to **automate and control VSCode**: give your AI assistant the ability to interact with the VSCode UI, execute commands, inspect the DOM, read editor content, and perform complex automation workflows.

Built on top of [vscode-extension-tester](https://github.com/redhat-developer/vscode-extension-tester), this mcp server is ideal for:
- **AI-assisted VSCode extension development** - Let AI agents test and interact with your extensions
- **Automated UI testing** - Build automation scripts driven by AI
- **Development workflow automation** - Automate repetitive VSCode tasks
- **Extension debugging** - Inspect UI state, DOM structure, and diagnostics

## 🚀 Features

- **DOM Inspection** - Get the full DOM structure, UI regions, and accessibility tree for AI understanding
- **Execute VSCode Commands** - Run any command from the Command Palette programmatically
- **UI Automation** - Click elements, type text, navigate the UI
- **Keyboard Input** - Press key combinations, shortcuts, and special keys
- **File Operations** - Open files at specific lines and columns
- **Screenshots** - Capture the VSCode window for debugging and verification
- **Element Inspection** - Get detailed information about UI elements
- **Element Queries** - Find multiple elements matching selectors with attributes
- **JavaScript Execution** - Run arbitrary JS in the VSCode window (like DevTools console)
- **Diagnostics** - Access errors and warnings from the Problems panel
- **Editor Content** - Read and verify editor content
- **Webview Support** - Interact with extension webviews
- **Wait & Synchronization** - Wait for elements, text, or idle state
- **Notifications** - Get, dismiss, and handle notifications
- **Dialogs** - Handle InputBox, QuickPick, and confirmation dialogs
- **IntelliSense** - Trigger completions, get items, select suggestions
- **Code Navigation** - Go to definition, hover tooltips, signature help
- **Context Menus** - Open and interact with context menus
- **Output Channels** - Read from VSCode output channels and extension logs
- **Console Logs** - Capture and filter browser console logs
- **Performance Metrics** - Memory usage, DOM stats, timing info

## 📋 Requirements

- **Node.js** >= 18.0.0
- **Internet connection** (for first run to download VSCode and ChromeDriver)

> **Note**: The server automatically downloads a standalone VSCode instance and matching ChromeDriver on first run. It does NOT use your installed VSCode - this ensures consistent behavior and prevents conflicts with your main VSCode instance.

## 📦 Installation

### Quick Start (Recommended)

No installation required! Just add to your MCP client configuration:

```json
{
  "mcpServers": {
    "vscode-automation": {
      "command": "npx",
      "args": ["-y", "vscode-automation-mcp@latest"]
    }
  }
}
```

### Global Installation

```bash
npm install -g vscode-automation-mcp@latest
```

Then run directly:
```bash
vscode-automation-mcp
```

### From Source

```bash
git clone https://github.com/sukarth/vscode-automation-mcp.git
cd vscode-automation-mcp
npm install
npm run build
npm start
```

## ⚙️ Configuration

### Claude Desktop

Add to your Claude Desktop configuration file:

**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`  
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "vscode-automation": {
      "command": "npx",
      "args": ["-y", "vscode-automation-mcp@latest"]
    }
  }
}
```

Or if installed globally:

```json
{
  "mcpServers": {
    "vscode-automation": {
      "command": "vscode-automation-mcp"
    }
  }
}
```

### VSCode (GitHub Copilot / Copilot Chat)

Add to your VSCode `settings.json` or `.vscode/mcp.json`:

```json
{
  "mcp": {
    "servers": {
      "vscode-automation": {
        "command": "npx",
        "args": ["-y", "vscode-automation-mcp@latest"]
      }
    }
  }
}
```

### Cursor

Add to Cursor's MCP configuration:

```json
{
  "mcpServers": {
    "vscode-automation": {
      "command": "npx",
      "args": ["-y", "vscode-automation-mcp@latest"]
    }
  }
}
```

### Environment Variables

You can customize the server behavior using environment variables:

| Variable | Description | Default |
|----------|-------------|---------|
| `VSCODE_AUTOMATION_VERSION` | VSCode version to download (e.g., `1.95.0`, `1.85.0`, or `latest`) | `latest` |
| `VSCODE_AUTOMATION_STORAGE_PATH` | Directory to store VSCode and ChromeDriver | System temp dir |
| `VSCODE_AUTOMATION_OFFLINE` | Set to `true` to use cached binaries only | `false` |
| `VSCODE_AUTOMATION_LOG_LEVEL` | Log level: `trace`, `debug`, `info`, `warn`, `error` | `info` |

Example with environment variables:

```json
{
  "mcpServers": {
    "vscode-automation": {
      "command": "npx",
      "args": ["-y", "vscode-automation-mcp@latest"],
      "env": {
        "VSCODE_AUTOMATION_VERSION": "1.95.0",
        "VSCODE_AUTOMATION_STORAGE_PATH": "/path/to/storage"
      }
    }
  }
}
```

> **ChromeDriver Compatibility**: The server automatically downloads the correct ChromeDriver version that matches the VSCode/Electron version. You don't need to manage ChromeDriver manually.

## 🛠️ Available Tools

### Command Tools

| Tool | Description |
|------|-------------|
| `vscode_execute_command` | Execute any VSCode command with optional arguments |
| `vscode_list_commands` | List available VSCode commands with optional filter |

### UI Action Tools

| Tool | Description |
|------|-------------|
| `vscode_click_element` | Click a UI element by CSS/XPath/accessibility selector |
| `vscode_type_text` | Type text into an input field or focused element |
| `vscode_open_file` | Open a file in the editor at a specific line/column |

### Inspection Tools

| Tool | Description |
|------|-------------|
| `vscode_take_screenshot` | Capture a screenshot of the VSCode window |
| `vscode_get_element` | Get properties and text of a UI element |
| `vscode_open_webview` | Open an extension's webview panel |
| `vscode_get_diagnostics` | Get all diagnostic messages from Problems panel |

### DOM Inspection Tools

| Tool | Description |
|------|-------------|
| `vscode_get_dom` | Get the full DOM structure of VSCode in tree, HTML, or JSON format |
| `vscode_get_ui_structure` | Get structure of specific UI regions (sidebar, editor, panel, etc.) |
| `vscode_query_elements` | Find all elements matching a CSS selector with attributes |
| `vscode_get_accessibility_tree` | Get the semantic accessibility tree with ARIA roles and labels |

### DOM Navigation Tools

| Tool | Description |
|------|-------------|
| `vscode_get_element_children` | Get direct children or descendants of an element for incremental exploration |
| `vscode_get_element_parents` | Get the parent chain (ancestors) of an element |
| `vscode_get_element_siblings` | Get sibling elements at the same level |
| `vscode_find_interactive_elements` | Find all buttons, inputs, links, tabs within a container |
| `vscode_search_dom` | Search DOM by text, ID, class, aria-label, title, or role |
| `vscode_dump_dom_to_file` | Dump full DOM to a file for incremental exploration by AI |

### JavaScript Execution & DOM Query Tools

| Tool | Description |
|------|-------------|
| `vscode_execute_script` | Execute arbitrary JavaScript in the VSCode window (like DevTools console) |
| `vscode_query_selector` | Direct document.querySelector() / querySelectorAll() wrapper |
| `vscode_get_element_by_id` | Get element by ID (document.getElementById wrapper) |
| `vscode_get_elements_by_class` | Get elements by class name (document.getElementsByClassName wrapper) |
| `vscode_get_elements_by_tag` | Get elements by tag name (document.getElementsByTagName wrapper) |

### Testing Tools

| Tool | Description |
|------|-------------|
| `vscode_get_editor_content` | Get the current editor's text content |
| `vscode_verify_element` | Verify UI element presence and state |
| `vscode_assert_text` | Assert text content in editor or elements |
| `vscode_check_file_open` | Check if a specific file is open |

### Keyboard & Input Tools

| Tool | Description |
|------|-------------|
| `vscode_press_keys` | Press keyboard keys or key combinations (Ctrl+S, Ctrl+Shift+P, Enter, Escape, etc.) |
| `vscode_focus_element` | Focus a UI element by CSS selector |
| `vscode_scroll` | Scroll an element in a direction or to top/bottom |
| `vscode_drag_drop` | Drag and drop between elements |
| `vscode_hover` | Hover over an element for a duration |

### Wait & Sync Tools

| Tool | Description |
|------|-------------|
| `vscode_wait_for_element` | Wait for an element to appear or disappear |
| `vscode_wait_for_text` | Wait for text to appear or disappear in an element |
| `vscode_wait_for_idle` | Wait for VSCode to become idle (no pending operations) |

### Notification & Dialog Tools

| Tool | Description |
|------|-------------|
| `vscode_get_notifications` | Get all visible notifications with type, message, and actions |
| `vscode_dismiss_notification` | Dismiss a notification by index or message text |
| `vscode_handle_dialog` | Handle modal dialogs (InputBox, QuickPick, confirmations) |
| `vscode_get_quick_pick_items` | Get items from the QuickPick/Command Palette |
| `vscode_select_quick_pick_item` | Select a QuickPick item by text or index |

### Extension Testing Tools

| Tool | Description |
|------|-------------|
| `vscode_trigger_hover` | Trigger hover on an element and get tooltip content |
| `vscode_open_context_menu` | Open context menu (right-click) on an element |
| `vscode_get_menu_items` | Get items from visible menus |
| `vscode_click_menu_item` | Click a menu item by text |
| `vscode_get_tooltip` | Get currently visible tooltip content |
| `vscode_trigger_completion` | Trigger IntelliSense/code completion (Ctrl+Space) |
| `vscode_get_completion_items` | Get IntelliSense completion items |
| `vscode_select_completion_item` | Select and accept a completion item |
| `vscode_get_problems_panel` | Get problems from Problems panel with severity and location |
| `vscode_go_to_definition` | Trigger Go to Definition (F12) |
| `vscode_trigger_signature_help` | Trigger signature help (Ctrl+Shift+Space) |

### Debug & Performance Tools

| Tool | Description |
|------|-------------|
| `vscode_get_console_logs` | Get captured console logs (log, info, warn, error, debug) |
| `vscode_clear_console` | Clear captured console logs |
| `vscode_get_output_channels` | List available output channels |
| `vscode_get_output_channel_content` | Read content from an output channel |
| `vscode_get_performance_metrics` | Get performance metrics (memory, timing, DOM stats) |
| `vscode_get_extension_logs` | Get extension-related logs, optionally filtered by extension ID |
| `vscode_get_devtools_info` | Get DevTools-style window/document/navigator info |

### Utility Tools

| Tool | Description |
|------|-------------|
| `vscode_initialize` | Pre-initialize the VSCode driver |
| `vscode_get_status` | Get the current driver status |

## 📖 Usage Examples

### Execute a Command

```typescript
// Save the current file
await vscode_execute_command({ commandId: "workbench.action.files.save" });

// Format the document
await vscode_execute_command({ commandId: "editor.action.formatDocument" });

// Open settings
await vscode_execute_command({ commandId: "workbench.action.openSettings" });
```

### Click UI Elements

```typescript
// Click by CSS selector
await vscode_click_element({ 
  selector: ".explorer-viewlet", 
  selectorType: "css" 
});

// Click by accessibility label
await vscode_click_element({ 
  selector: "Explorer", 
  selectorType: "accessibility" 
});

// Double-click a file
await vscode_click_element({ 
  selector: ".file-item", 
  selectorType: "css",
  doubleClick: true 
});

// Right-click for context menu
await vscode_click_element({ 
  selector: ".file-item", 
  selectorType: "css",
  rightClick: true 
});
```

### Type Text

```typescript
// Type into the focused element
await vscode_type_text({ text: "Hello, World!" });

// Type into a specific input and press Enter
await vscode_type_text({ 
  text: "search term",
  selector: "input.search-input",
  pressEnter: true 
});

// Clear and type with a delay (for autocomplete)
await vscode_type_text({ 
  text: "import React from 'react'",
  clear: true,
  delay: 50 
});
```

### Open Files

```typescript
// Open a file
await vscode_open_file({ filePath: "/path/to/file.ts" });

// Open a file at a specific line
await vscode_open_file({ 
  filePath: "src/index.ts", 
  line: 42 
});

// Open a file at a specific line and column
await vscode_open_file({ 
  filePath: "src/utils.ts", 
  line: 10, 
  column: 5 
});
```

### Take Screenshots

```typescript
// Take a screenshot with default name
await vscode_take_screenshot({});

// Take a screenshot with custom filename
await vscode_take_screenshot({ 
  filename: "test-result-screenshot" 
});
```

### Get Diagnostics

```typescript
// Get all diagnostics
await vscode_get_diagnostics({});

// Get only errors
await vscode_get_diagnostics({ severity: "error" });

// Get warnings
await vscode_get_diagnostics({ severity: "warning" });
```

### Inspect the DOM (for AI Agents)

```typescript
// Get the full page DOM as a readable tree
await vscode_get_dom({});

// Get DOM of a specific element as JSON
await vscode_get_dom({ 
  selector: ".sidebar",
  format: "json",
  depth: 5
});

// Get raw HTML of an element
await vscode_get_dom({ 
  selector: ".editor-container",
  format: "html"
});

// Get the sidebar structure
await vscode_get_ui_structure({ region: "sidebar" });

// Get the editor area structure with more depth
await vscode_get_ui_structure({ 
  region: "editor", 
  depth: 8 
});

// Find all buttons on the page
await vscode_query_elements({ selector: "button" });

// Find elements with a specific role
await vscode_query_elements({ 
  selector: "[role='tab']", 
  limit: 10 
});

// Get the accessibility tree for semantic understanding
await vscode_get_accessibility_tree({});

// Get accessibility tree of a specific region
await vscode_get_accessibility_tree({ 
  selector: ".sidebar",
  depth: 6
});
```

### Incremental DOM Exploration (for Large DOMs)

```typescript
// Step 1: Get top-level children of body
await vscode_get_element_children({ selector: "body" });

// Step 2: Drill into a specific child
await vscode_get_element_children({ 
  selector: ".workbench-container",
  depth: 2
});

// Get parent chain to understand context
await vscode_get_element_parents({ 
  selector: ".my-button",
  levels: 5
});

// Get siblings (useful for tabs, list items)
await vscode_get_element_siblings({ 
  selector: ".tab:nth-child(2)",
  direction: "all"
});

// Find all interactive elements in a region
await vscode_find_interactive_elements({ 
  selector: ".sidebar",
  types: ["button", "input", "link"]
});

// Search for elements by text/label
await vscode_search_dom({ 
  query: "Explorer",
  searchIn: ["text", "aria-label", "title"]
});

// For very large DOMs: dump to file and read incrementally
await vscode_dump_dom_to_file({ 
  filePath: "/tmp/vscode-dom.json",
  format: "json",
  depth: 15
});
// Then use file reading tools to explore sections
```

### Execute JavaScript (DevTools Console Style)

```typescript
// Get the document title
await vscode_execute_script({ 
  script: "return document.title" 
});

// Get Monaco editor models (VSCode internals)
await vscode_execute_script({ 
  script: "return window.monaco?.editor?.getModels()?.map(m => m.uri.toString())" 
});

// Click an element programmatically
await vscode_execute_script({ 
  script: "document.querySelector('.my-button')?.click(); return 'clicked'",
  returnType: "string"
});

// Run complex DOM manipulation
await vscode_execute_script({ 
  script: `
    const editors = document.querySelectorAll('.editor-instance');
    return Array.from(editors).map(e => ({
      id: e.id,
      visible: e.offsetWidth > 0
    }));
  `
});

// Access VSCode's internal APIs (if available)
await vscode_execute_script({ 
  script: "return typeof acquireVsCodeApi !== 'undefined'"
});
```

### Simple DOM Queries (querySelector style)

```typescript
// Find a single element
await vscode_query_selector({ selector: ".my-button" });

// Find all matching elements
await vscode_query_selector({ selector: "button", all: true });

// Extract specific properties
await vscode_query_selector({ 
  selector: "input",
  all: true,
  properties: ["value", "placeholder", "type", "name"]
});

// Get element by ID
await vscode_get_element_by_id({ id: "workbench.parts.editor" });

// Get all elements with a class
await vscode_get_elements_by_class({ 
  className: "editor-container",
  limit: 10
});

// Get all buttons
await vscode_get_elements_by_tag({ 
  tagName: "button",
  properties: ["textContent", "aria-label", "disabled"]
});

// Get all inputs with their values
await vscode_get_elements_by_tag({ 
  tagName: "input",
  properties: ["type", "value", "placeholder", "name"]
});
```

### Keyboard & Input

```typescript
// Press keyboard shortcuts
await vscode_press_keys({ keys: "ctrl+s" }); // Save file
await vscode_press_keys({ keys: "ctrl+shift+p" }); // Command Palette
await vscode_press_keys({ keys: "ctrl+/" }); // Toggle comment

// Press keys multiple times
await vscode_press_keys({ keys: "ArrowDown", count: 5 });

// Focus on a specific element first
await vscode_press_keys({ 
  keys: "Enter",
  selector: ".my-button"
});

// Scroll within an element
await vscode_scroll({ direction: "down", amount: 500 });
await vscode_scroll({ selector: ".sidebar", direction: "bottom" });

// Drag and drop
await vscode_drag_drop({
  sourceSelector: ".file-item:nth-child(2)",
  targetSelector: ".folder-item"
});

// Hover over element
await vscode_hover({ 
  selector: ".status-item", 
  duration: 1000 
});
```

### Waiting & Synchronization

```typescript
// Wait for an element to appear
await vscode_wait_for_element({ 
  selector: ".notification",
  timeout: 5000
});

// Wait for element to disappear
await vscode_wait_for_element({ 
  selector: ".loading-spinner",
  present: false,
  timeout: 10000
});

// Wait for specific text
await vscode_wait_for_text({ 
  text: "Build succeeded",
  selector: ".terminal",
  timeout: 30000
});

// Wait for VSCode to be idle
await vscode_wait_for_idle({ timeout: 5000 });
```

### Notifications & Dialogs

```typescript
// Get all notifications
await vscode_get_notifications({});

// Dismiss a notification by message
await vscode_dismiss_notification({ 
  messageContains: "Extension installed" 
});

// Handle input dialog
await vscode_handle_dialog({ 
  text: "my-new-file.ts",
  action: "accept"
});

// Get QuickPick items (Command Palette)
await vscode_press_keys({ keys: "ctrl+shift+p" });
await vscode_get_quick_pick_items({});

// Select a QuickPick item
await vscode_select_quick_pick_item({ 
  text: "Format Document" 
});
```

### IntelliSense & Code Navigation

```typescript
// Trigger completion
await vscode_trigger_completion({});

// Get completion items
await vscode_get_completion_items({ limit: 20 });

// Select a completion item
await vscode_select_completion_item({ 
  itemLabel: "console.log" 
});

// Go to definition
await vscode_go_to_definition({});

// Trigger signature help
await vscode_trigger_signature_help({});

// Trigger hover and get tooltip
await vscode_trigger_hover({ 
  selector: ".view-line span" 
});

// Open context menu
await vscode_open_context_menu({ 
  selector: ".file-item" 
});

// Get and click menu items
await vscode_get_menu_items({});
await vscode_click_menu_item({ itemText: "Rename" });
```

### Debug & Performance

```typescript
// Get console logs
await vscode_get_console_logs({ level: "error" });

// Get all recent logs
await vscode_get_console_logs({ limit: 50 });

// Clear console logs
await vscode_clear_console({});

// Get output channels
await vscode_get_output_channels({});

// Read from an output channel
await vscode_get_output_channel_content({ 
  channelName: "Extension Host" 
});

// Get extension logs
await vscode_get_extension_logs({ 
  extensionId: "my.extension" 
});

// Get performance metrics
await vscode_get_performance_metrics({});

// Get DevTools info
await vscode_get_devtools_info({});
```

### Verify UI State

```typescript
// Check if an element exists and is visible
await vscode_verify_element({ 
  selector: ".explorer-viewlet",
  selectorType: "css",
  shouldBeVisible: true 
});

// Check if element contains specific text
await vscode_verify_element({ 
  selector: ".statusbar",
  selectorType: "css",
  containsText: "TypeScript" 
});
```

### Get Editor Content

```typescript
// Get the content of the active editor
const result = await vscode_get_editor_content({});
// Returns: { content: "...", editor: { fileName: "...", lineCount: ... } }
```

## 🔧 Common Command IDs

Here are some frequently used VSCode command IDs:

### File Operations
- `workbench.action.files.save` - Save file
- `workbench.action.files.saveAll` - Save all files
- `workbench.action.files.newUntitledFile` - New file
- `workbench.action.closeActiveEditor` - Close editor

### Navigation
- `workbench.action.quickOpen` - Quick Open (Ctrl+P)
- `workbench.action.gotoLine` - Go to Line
- `editor.action.revealDefinition` - Go to Definition

### View
- `workbench.action.toggleSidebarVisibility` - Toggle Sidebar
- `workbench.action.togglePanel` - Toggle Panel
- `workbench.action.terminal.toggleTerminal` - Toggle Terminal

### Editor
- `editor.action.formatDocument` - Format Document
- `editor.action.commentLine` - Toggle Line Comment

### Git
- `git.commit` - Git Commit
- `git.push` - Git Push
- `git.pull` - Git Pull

## 🧪 Testing the MCP Server

### Manual Testing

1. **Build the project:**
   ```bash
   npm run build
   npm run validate
   ```

2. **Test with MCP Inspector (recommended):**
   ```bash
   npx @modelcontextprotocol/inspector node dist/index.js
   ```
   This opens a web UI where you can call each tool interactively.

3. **Test with Claude Desktop:**
   - Add the server to your Claude Desktop config (see Configuration section)
   - Restart Claude Desktop
   - Ask Claude to use the vscode_automation tools

4. **Test basic functionality:**
   ```bash
   # Start the server manually to check for startup errors
   node dist/index.js
   ```
   The server communicates via stdio, so you won't see output unless there are errors.

### What Gets Tested

- `vscode_get_status` - Always works, returns server state
- `vscode_initialize` - Launches VSCode with WebDriver (requires display)
- All other tools - Require `vscode_initialize` to be called first

### Notes on Testing

- The server uses vscode-extension-tester which launches a real VSCode instance
- First run may take time to download ChromeDriver
- VSCode must be installed on the system
- Requires a display (or virtual display on Linux)

## 🐛 Troubleshooting

### VSCode Not Found

Make sure VSCode is installed and accessible from the command line:

```bash
code --version
```

### ChromeDriver Issues

The server uses ChromeDriver to automate VSCode. If you encounter issues:

1. Make sure Chrome/Chromium is installed
2. The correct ChromeDriver version will be downloaded automatically
3. Check that no other automation tools are conflicting

### Timeout Errors

If operations are timing out:

1. Increase the timeout in the tool options
2. Make sure VSCode has finished loading
3. Call `vscode_initialize` before other operations

### Element Not Found

If selectors aren't finding elements:

1. Use `vscode_take_screenshot` to see the current state
2. Try different selector types (css, xpath, accessibility, text)
3. Check if the element is in a webview (requires different handling)

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

- [Model Context Protocol](https://modelcontextprotocol.io/) - The protocol that makes this possible
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - The SDK used to build this server
- [vscode-extension-tester](https://github.com/redhat-developer/vscode-extension-tester) - The automation library powering this server
- [Anthropic](https://anthropic.com/) - For creating Claude and the MCP specification

## 💖 Support

If this project helps or saves you time, consider supporting my work, as it keeps projects like this free, open source, and maintained:

[![GitHub Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-%E2%9D%A4-EA4AAA?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/Sukarth)
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/sukarth)
[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-☕-FFDD00?logoColor=black)](https://buymeacoffee.com/sukarth)

Can't donate? Starring the repo ⭐, reporting bugs, and sharing the project help just as much!

---

Built with ❤️ by [Sukarth](https://github.com/sukarth)
