Skip to main content
Glama

Cursor MCP Server

by johnneerdael
cursor-tool.js6.27 kB
import robot from 'robotjs'; import { spawn } from 'child_process'; import path from 'path'; import { windowManager } from 'node-window-manager'; import { getPaletteCommand, CommandSchema } from '../commands/cursor-commands.js'; const DEFAULT_CURSOR_PATH = path.join(process.env.LOCALAPPDATA || '', 'Programs', 'cursor', 'Cursor.exe'); async function findCursorWindow() { const windows = windowManager.getWindows(); return windows.find(w => w.getTitle().includes('Cursor')); } async function ensureCursorFocus(cursorWindow) { // Get currently active window const windows = windowManager.getWindows(); const activeWindow = windows.find(w => w.getTitle().includes('Cursor')); // Check if Cursor is already focused if (activeWindow) { return true; } // If not focused, try to restore focus console.log('Restoring Cursor window focus...'); cursorWindow.bringToTop(); cursorWindow.show(); cursorWindow.restore(); // Wait for focus change await new Promise(resolve => setTimeout(resolve, 500)); // Verify focus was restored const newWindows = windowManager.getWindows(); const newActiveWindow = newWindows.find(w => w.getTitle().includes('Cursor')); return !!newActiveWindow; } async function typeText(text, cursorWindow) { for (const char of text) { // Ensure focus before each keystroke if (!await ensureCursorFocus(cursorWindow)) { console.log('Failed to maintain Cursor window focus'); throw new Error('Lost window focus'); } // Handle uppercase letters if (char >= 'A' && char <= 'Z') { robot.keyToggle('shift', 'down'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyTap(char.toLowerCase()); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('shift', 'up'); await new Promise(resolve => setTimeout(resolve, 50)); } // Handle special characters else if (char === ':') { robot.keyToggle('shift', 'down'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyTap(';'); // On most keyboards, shift+; gives : await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('shift', 'up'); await new Promise(resolve => setTimeout(resolve, 50)); } // Handle spaces else if (char === ' ') { robot.keyTap('space'); await new Promise(resolve => setTimeout(resolve, 50)); } // Handle normal lowercase letters else { robot.keyTap(char); await new Promise(resolve => setTimeout(resolve, 50)); } } } async function executeCursorCommand(commandName) { // Validate command const parseResult = CommandSchema.safeParse({ command: commandName }); if (!parseResult.success) { throw new Error(`Invalid command: ${commandName}. Available commands: ${Object.keys(CommandSchema.shape.command.enum).join(', ')}`); } // Get the palette command const paletteCommand = getPaletteCommand(commandName); if (!paletteCommand) { throw new Error(`No palette command found for: ${commandName}`); } try { console.log('Launching Cursor...'); const cursorProcess = spawn(DEFAULT_CURSOR_PATH, [], { detached: false, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: false }); // Wait for Cursor to start console.log('Waiting for Cursor to initialize...'); await new Promise(resolve => setTimeout(resolve, 3000)); // Find the Cursor window const cursorWindow = await findCursorWindow(); if (!cursorWindow) { throw new Error('Could not find Cursor window'); } // Initial focus console.log('Focusing Cursor window...'); if (!await ensureCursorFocus(cursorWindow)) { throw new Error('Failed to focus Cursor window'); } console.log('Opening command palette...'); // Press Ctrl+Shift+P using robotjs robot.keyToggle('control', 'down'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('shift', 'down'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('p', 'down'); await new Promise(resolve => setTimeout(resolve, 50)); // Release in reverse order with delays robot.keyToggle('p', 'up'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('shift', 'up'); await new Promise(resolve => setTimeout(resolve, 50)); robot.keyToggle('control', 'up'); await new Promise(resolve => setTimeout(resolve, 50)); // Wait for command palette to appear await new Promise(resolve => setTimeout(resolve, 500)); // Type the command with focus checks console.log('Executing command:', paletteCommand); await typeText(paletteCommand, cursorWindow); // Wait briefly after typing await new Promise(resolve => setTimeout(resolve, 500)); // Ensure focus before Enter if (!await ensureCursorFocus(cursorWindow)) { throw new Error('Lost window focus before pressing Enter'); } // Press Enter console.log('Confirming command...'); robot.keyTap('enter'); // Keep process alive briefly to observe result await new Promise(resolve => setTimeout(resolve, 2000)); // Cleanup console.log('Done!'); cursorProcess.kill(); } catch (error) { console.error('Command failed:', error); process.exit(1); } } // Get command from command line const commandName = process.argv[2]; if (!commandName) { console.log('Available commands:'); Object.entries(CommandSchema.shape.command.enum).forEach(([cmd]) => { console.log(`- ${cmd}`); }); process.exit(0); } // Execute the command console.log(`Executing Cursor command: ${commandName}`); executeCursorCommand(commandName).catch(error => { console.error('Unhandled error:', error); process.exit(1); });

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/johnneerdael/multiplatform-cursor-mcp'

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