Skip to main content
Glama

MCPControl

mouse.ts8.55 kB
import { execSync } from 'child_process'; import { writeFileSync, unlinkSync, readFileSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; import { MousePosition } from '../../types/common.js'; import { WindowsControlResponse } from '../../types/responses.js'; import { MouseAutomation } from '../../interfaces/automation.js'; import { MousePositionSchema, MouseButtonSchema, ScrollAmountSchema, } from '../../tools/validation.zod.js'; import { getAutoHotkeyPath } from './utils.js'; /** * AutoHotkey implementation of the MouseAutomation interface */ export class AutoHotkeyMouseAutomation implements MouseAutomation { /** * Execute an AutoHotkey script */ private executeScript(script: string): void { const scriptPath = join(tmpdir(), `mcp-ahk-${Date.now()}.ahk`); try { // Write the script to a temporary file writeFileSync(scriptPath, script, 'utf8'); // Execute the script with AutoHotkey v2 const autohotkeyPath = getAutoHotkeyPath(); execSync(`"${autohotkeyPath}" "${scriptPath}"`, { stdio: 'pipe' }); } finally { // Clean up the temporary script file try { unlinkSync(scriptPath); } catch { // Ignore cleanup errors } } } /** * Convert mouse button to AutoHotkey format */ private formatButton(button: string): string { const buttonMap: Record<string, string> = { left: 'Left', right: 'Right', middle: 'Middle', }; return buttonMap[button] || button; } moveMouse(position: MousePosition): WindowsControlResponse { try { // Validate the position MousePositionSchema.parse(position); const script = ` CoordMode("Mouse", "Screen") MouseMove(${position.x}, ${position.y}, 0) ExitApp `; this.executeScript(script); return { success: true, message: `Moved mouse to position (${position.x}, ${position.y})`, }; } catch (error) { return { success: false, message: `Failed to move mouse: ${error instanceof Error ? error.message : String(error)}`, }; } } clickMouse(button: 'left' | 'right' | 'middle' = 'left'): WindowsControlResponse { try { // Validate button MouseButtonSchema.parse(button); const formattedButton = this.formatButton(button); const script = ` Click("${formattedButton}") ExitApp `; this.executeScript(script); return { success: true, message: `Clicked ${button} mouse button`, }; } catch (error) { return { success: false, message: `Failed to click mouse: ${error instanceof Error ? error.message : String(error)}`, }; } } doubleClick(position?: MousePosition): WindowsControlResponse { try { let script: string; if (position) { MousePositionSchema.parse(position); script = ` CoordMode("Mouse", "Screen") MouseMove(${position.x}, ${position.y}, 0) Click("Left 2") ExitApp `; } else { script = ` Click("Left 2") ExitApp `; } this.executeScript(script); return { success: true, message: position ? `Double-clicked at position (${position.x}, ${position.y})` : 'Double-clicked at current position', }; } catch (error) { return { success: false, message: `Failed to double-click mouse: ${error instanceof Error ? error.message : String(error)}`, }; } } pressMouse(button: string = 'left'): WindowsControlResponse { try { // Validate button MouseButtonSchema.parse(button); const formattedButton = this.formatButton(button); const script = ` Click("${formattedButton} Down") ExitApp `; this.executeScript(script); return { success: true, message: `Pressed ${button} mouse button`, }; } catch (error) { return { success: false, message: `Failed to press mouse button: ${error instanceof Error ? error.message : String(error)}`, }; } } releaseMouse(button: string = 'left'): WindowsControlResponse { try { // Validate button MouseButtonSchema.parse(button); const formattedButton = this.formatButton(button); const script = ` Click("${formattedButton} Up") ExitApp `; this.executeScript(script); return { success: true, message: `Released ${button} mouse button`, }; } catch (error) { return { success: false, message: `Failed to release mouse button: ${error instanceof Error ? error.message : String(error)}`, }; } } scrollMouse(amount: number): WindowsControlResponse { try { // Validate amount ScrollAmountSchema.parse(amount); // Convert direction to AutoHotkey format const direction = amount > 0 ? 'up' : 'down'; const wheelDirection = amount > 0 ? 'WheelUp' : 'WheelDown'; const steps = Math.abs(amount); const script = ` Loop ${steps} { Send("{${wheelDirection}}") } ExitApp `; this.executeScript(script); return { success: true, message: `Scrolled ${direction} ${steps} times`, }; } catch (error) { return { success: false, message: `Failed to scroll mouse: ${error instanceof Error ? error.message : String(error)}`, }; } } getCursorPosition(): WindowsControlResponse { try { // Create a more complex script that writes the position to stdout const outputPath = join(tmpdir(), `mcp-ahk-output-${Date.now()}.txt`); const script = ` CoordMode("Mouse", "Screen") MouseGetPos(&x, &y) FileAppend(x . "," . y, "${outputPath}") ExitApp `; const scriptPath = join(tmpdir(), `mcp-ahk-${Date.now()}.ahk`); try { writeFileSync(scriptPath, script, 'utf8'); execSync(`AutoHotkey.exe "${scriptPath}"`, { stdio: 'pipe' }); // Read the output const output = readFileSync(outputPath, 'utf8'); const [x, y] = output.split(',').map(Number); return { success: true, message: 'Retrieved cursor position', data: { position: { x, y } }, }; } finally { // Clean up try { unlinkSync(scriptPath); unlinkSync(outputPath); } catch { // Ignore cleanup errors } } } catch (error) { return { success: false, message: `Failed to get mouse position: ${error instanceof Error ? error.message : String(error)}`, }; } } dragMouse( from: MousePosition, to: MousePosition, button: 'left' | 'right' | 'middle' = 'left', ): WindowsControlResponse { try { MousePositionSchema.parse(from); MousePositionSchema.parse(to); MouseButtonSchema.parse(button); const formattedButton = this.formatButton(button); const script = ` CoordMode("Mouse", "Screen") MouseMove(${from.x}, ${from.y}, 0) Click("${formattedButton} Down") MouseMove(${to.x}, ${to.y}, 10) Click("${formattedButton} Up") ExitApp `; this.executeScript(script); return { success: true, message: `Dragged from (${from.x}, ${from.y}) to (${to.x}, ${to.y})`, }; } catch (error) { return { success: false, message: `Failed to drag mouse: ${error instanceof Error ? error.message : String(error)}`, }; } } clickAt( x: number, y: number, button: 'left' | 'right' | 'middle' = 'left', ): WindowsControlResponse { try { MouseButtonSchema.parse(button); const position = { x, y }; MousePositionSchema.parse(position); const formattedButton = this.formatButton(button); const script = ` CoordMode("Mouse", "Screen") Click(${x}, ${y}, "${formattedButton}") ExitApp `; this.executeScript(script); return { success: true, message: `Clicked ${button} at (${x}, ${y})`, }; } catch (error) { return { success: false, message: `Failed to click at position: ${error instanceof Error ? error.message : String(error)}`, }; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/claude-did-this/MCPControl'

If you have feedback or need assistance with the MCP directory API, please join our Discord server