Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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:
Expand All @@ -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

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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',
};
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(', ')}`,
Expand Down
28 changes: 26 additions & 2 deletions src/providers/factory.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -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();
});

Expand All @@ -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',
Expand Down
10 changes: 5 additions & 5 deletions src/providers/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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');
Expand Down
Loading