diff --git a/README.md b/README.md index e9fd7ae..52fd698 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ To launch MCPControl locally with SSE transport: First, start the MCPControl server on your VM or local machine: -```bash +```powershell mcp-control --sse ``` @@ -238,30 +238,30 @@ By using this software, you acknowledge and accept that: MCPControl supports multiple automation providers for different use cases: -- **keysender** (default) - Native Windows automation with high reliability +- **autohotkey** (default) - AutoHotkey v2 scripting for advanced automation needs +- **keysender** - Native Windows automation with high reliability (optional) - **powershell** - Windows PowerShell-based automation for simpler operations -- **autohotkey** - AutoHotkey v2 scripting for advanced automation needs ### Provider Configuration You can configure the automation provider using environment variables: -```bash +```powershell # Use a specific provider for all operations -export AUTOMATION_PROVIDER=autohotkey +$env:AUTOMATION_PROVIDER = "autohotkey" # Configure AutoHotkey executable path (if not in PATH) -export AUTOHOTKEY_PATH="C:\Program Files\AutoHotkey\v2\AutoHotkey.exe" +$env:AUTOHOTKEY_PATH = "C:\Program Files\AutoHotkey\v2\AutoHotkey.exe" ``` Or use modular configuration for specific operations: -```bash +```powershell # Mix and match providers for different operations -export AUTOMATION_KEYBOARD_PROVIDER=autohotkey -export AUTOMATION_MOUSE_PROVIDER=keysender -export AUTOMATION_SCREEN_PROVIDER=keysender -export AUTOMATION_CLIPBOARD_PROVIDER=powershell +$env:AUTOMATION_KEYBOARD_PROVIDER = "autohotkey" +$env:AUTOMATION_MOUSE_PROVIDER = "autohotkey" +$env:AUTOMATION_SCREEN_PROVIDER = "autohotkey" +$env:AUTOMATION_CLIPBOARD_PROVIDER = "powershell" ``` See provider-specific documentation: @@ -275,14 +275,14 @@ If you're interested in contributing or building from source, please see [CONTRI To build this project for development, you'll need: -1. Windows operating system (required for the keysender dependency) +1. Windows operating system 2. Node.js 18 or later (install using the official Windows installer which includes build tools) 3. npm package manager -4. Native build tools: +4. Native build tools (optional, only needed if using the keysender provider): - node-gyp: `npm install -g node-gyp` - cmake-js: `npm install -g cmake-js` -The keysender dependency relies on Windows-specific native modules that require these build tools. +The keysender provider is optional and relies on Windows-specific native modules that require these build tools. If you don't need keysender, you can skip installing the build tools. ## 📋 Project Structure diff --git a/package.json b/package.json index 1a009d2..50c2171 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.11.2", "clipboardy": "^4.0.0", - "keysender": "^2.3.0", "sharp": "^0.34.1", "ulid": "^3.0.0", "uuid": "^11.1.0", "zod": "^3.24.4" }, + "optionalDependencies": { + "keysender": "^2.3.0" + }, "devDependencies": { "@eslint/js": "^9.25.0", "@types/express": "^5.0.1", diff --git a/src/config.ts b/src/config.ts index edaf473..e140de2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,7 +4,8 @@ export interface AutomationConfig { /** * Legacy: The provider to use for all automation - * Currently supported: 'keysender' + * Currently supported: 'keysender', 'autohotkey' + * Default: 'autohotkey' */ provider?: string; @@ -43,6 +44,6 @@ export function loadConfig(): AutomationConfig { // Fall back to legacy configuration return { - provider: process.env.AUTOMATION_PROVIDER || 'keysender', + provider: process.env.AUTOMATION_PROVIDER || 'autohotkey', }; } diff --git a/src/index.ts b/src/index.ts index 74cf1a2..13e00f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,7 +49,7 @@ class MCPControlServer { } // Validate that the provider is supported - const supportedProviders = ['keysender']; // add others as they become available + const supportedProviders = ['keysender', 'autohotkey']; // add others as they become available if (!supportedProviders.includes(config.provider.toLowerCase())) { throw new Error( `Unsupported provider: ${config.provider}. Supported providers: ${supportedProviders.join(', ')}`, diff --git a/src/providers/factory.test.ts b/src/providers/factory.test.ts index 2b07c4d..ef1885e 100644 --- a/src/providers/factory.test.ts +++ b/src/providers/factory.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { createAutomationProvider } from './factory.js'; import { KeysenderProvider } from './keysender/index.js'; +import { AutoHotkeyProvider } from './autohotkey/index.js'; // Mock the providers vi.mock('./keysender/index.js', () => { @@ -14,10 +15,21 @@ vi.mock('./keysender/index.js', () => { }; }); +vi.mock('./autohotkey/index.js', () => { + return { + AutoHotkeyProvider: vi.fn().mockImplementation(() => ({ + keyboard: {}, + mouse: {}, + screen: {}, + clipboard: {}, + })), + }; +}); + describe('createAutomationProvider', () => { - it('should create KeysenderProvider by default', () => { + it('should create AutoHotkeyProvider by default', () => { const provider = createAutomationProvider(); - expect(KeysenderProvider).toHaveBeenCalled(); + expect(AutoHotkeyProvider).toHaveBeenCalled(); expect(provider).toBeDefined(); }); @@ -27,12 +39,24 @@ describe('createAutomationProvider', () => { expect(provider).toBeDefined(); }); + it('should create AutoHotkeyProvider when explicitly specified', () => { + const provider = createAutomationProvider({ provider: 'autohotkey' }); + expect(AutoHotkeyProvider).toHaveBeenCalled(); + expect(provider).toBeDefined(); + }); + it('should be case insensitive for KeysenderProvider', () => { const provider = createAutomationProvider({ provider: 'KeYsEnDeR' }); expect(KeysenderProvider).toHaveBeenCalled(); expect(provider).toBeDefined(); }); + it('should be case insensitive for AutoHotkeyProvider', () => { + const provider = createAutomationProvider({ provider: 'AuToHoTkEy' }); + expect(AutoHotkeyProvider).toHaveBeenCalled(); + expect(provider).toBeDefined(); + }); + it('should throw error for unknown provider type', () => { expect(() => createAutomationProvider({ provider: 'unknown' })).toThrow( 'Unknown provider type: unknown', diff --git a/src/providers/factory.ts b/src/providers/factory.ts index 6ff7976..bf3e2fa 100644 --- a/src/providers/factory.ts +++ b/src/providers/factory.ts @@ -72,7 +72,7 @@ export function createAutomationProvider(config?: AutomationConfig): AutomationP if (!config || !config.providers) { // Legacy behavior: use monolithic provider - const type = config?.provider || 'keysender'; + const type = config?.provider || 'autohotkey'; const providerType = type.toLowerCase(); // Return cached instance if available @@ -106,19 +106,19 @@ export function createAutomationProvider(config?: AutomationConfig): AutomationP // Get individual components from the registry const keyboardProvider = config.providers.keyboard ? registry.getKeyboard(config.providers.keyboard) - : new KeysenderProvider().keyboard; + : new AutoHotkeyProvider().keyboard; const mouseProvider = config.providers.mouse ? registry.getMouse(config.providers.mouse) - : new KeysenderProvider().mouse; + : new AutoHotkeyProvider().mouse; const screenProvider = config.providers.screen ? registry.getScreen(config.providers.screen) - : new KeysenderProvider().screen; + : new AutoHotkeyProvider().screen; const clipboardProvider = config.providers.clipboard ? registry.getClipboard(config.providers.clipboard) - : new KeysenderProvider().clipboard; + : new AutoHotkeyProvider().clipboard; if (!keyboardProvider || !mouseProvider || !screenProvider || !clipboardProvider) { throw new Error('Failed to resolve all provider components');