key_sequence
Simulate HID keycodes with configurable delays on iOS simulators to test or automate interactions using precise key sequence inputs.
Instructions
Press key sequence using HID keycodes on iOS simulator with configurable delay
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| delay | No | ||
| keyCodes | Yes | ||
| simulatorUuid | Yes |
Implementation Reference
- Core handler logic: parses params, builds AXe 'key-sequence' command args, executes via executeAxeCommand, handles errors and returns responses.export async function key_sequenceLogic( params: KeySequenceParams, executor: CommandExecutor, axeHelpers: AxeHelpers = { getAxePath, getBundledAxeEnvironment, createAxeNotAvailableResponse, }, ): Promise<ToolResponse> { const toolName = 'key_sequence'; const { simulatorId, keyCodes, delay } = params; const commandArgs = ['key-sequence', '--keycodes', keyCodes.join(',')]; if (delay !== undefined) { commandArgs.push('--delay', String(delay)); } log( 'info', `${LOG_PREFIX}/${toolName}: Starting key sequence [${keyCodes.join(',')}] on ${simulatorId}`, ); try { await executeAxeCommand(commandArgs, simulatorId, 'key-sequence', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); return createTextResponse(`Key sequence [${keyCodes.join(',')}] executed successfully.`); } catch (error) { log('error', `${LOG_PREFIX}/${toolName}: Failed - ${error}`); if (error instanceof DependencyError) { return axeHelpers.createAxeNotAvailableResponse(); } else if (error instanceof AxeError) { return createErrorResponse( `Failed to execute key sequence: ${error.message}`, error.axeOutput, ); } else if (error instanceof SystemError) { return createErrorResponse( `System error executing axe: ${error.message}`, error.originalError?.stack, ); } return createErrorResponse( `An unexpected error occurred: ${error instanceof Error ? error.message : String(error)}`, ); } }
- Zod schema defining input parameters: simulatorId (UUID), keyCodes (array of 0-255 ints, min 1), optional delay (non-neg number).const keySequenceSchema = z.object({ simulatorId: z.string().uuid('Invalid Simulator UUID format'), keyCodes: z.array(z.number().int().min(0).max(255)).min(1, 'At least one key code required'), delay: z.number().min(0, 'Delay must be non-negative').optional(), });
- src/mcp/tools/ui-testing/key_sequence.ts:92-107 (registration)Tool registration as default export: name 'key_sequence', description, public schema (without simulatorId), and session-aware handler wrapping the logic.export default { name: 'key_sequence', description: 'Press key sequence using HID keycodes on iOS simulator with configurable delay', schema: publicSchemaObject.shape, // MCP SDK compatibility handler: createSessionAwareTool<KeySequenceParams>({ internalSchema: keySequenceSchema as unknown as z.ZodType<KeySequenceParams>, logicFunction: (params: KeySequenceParams, executor: CommandExecutor) => key_sequenceLogic(params, executor, { getAxePath, getBundledAxeEnvironment, createAxeNotAvailableResponse, }), getExecutor: getDefaultCommandExecutor, requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], }), };
- TypeScript type inferred from the Zod schema for type safety.type KeySequenceParams = z.infer<typeof keySequenceSchema>;
- Internal helper to execute AXe commands: resolves binary, adds UDID, runs with executor, throws typed errors.async function executeAxeCommand( commandArgs: string[], simulatorId: string, commandName: string, executor: CommandExecutor = getDefaultCommandExecutor(), axeHelpers: AxeHelpers = { getAxePath, getBundledAxeEnvironment, createAxeNotAvailableResponse }, ): Promise<void> { // Get the appropriate axe binary path const axeBinary = axeHelpers.getAxePath(); if (!axeBinary) { throw new DependencyError('AXe binary not found'); } // Add --udid parameter to all commands const fullArgs = [...commandArgs, '--udid', simulatorId]; // Construct the full command array with the axe binary as the first element const fullCommand = [axeBinary, ...fullArgs]; try { // Determine environment variables for bundled AXe const axeEnv = axeBinary !== 'axe' ? axeHelpers.getBundledAxeEnvironment() : undefined; const result = await executor(fullCommand, `${LOG_PREFIX}: ${commandName}`, false, axeEnv); if (!result.success) { throw new AxeError( `axe command '${commandName}' failed.`, commandName, result.error ?? result.output, simulatorId, ); } // Check for stderr output in successful commands if (result.error) { log( 'warn', `${LOG_PREFIX}: Command '${commandName}' produced stderr output but exited successfully. Output: ${result.error}`, ); } // Function now returns void - the calling code creates its own response } catch (error) { if (error instanceof Error) { if (error instanceof AxeError) { throw error; } // Otherwise wrap it in a SystemError throw new SystemError(`Failed to execute axe command: ${error.message}`, error); } // For any other type of error throw new SystemError(`Failed to execute axe command: ${String(error)}`); } }