Skip to main content
Glama

touch

Simulate precise touch events at specific coordinates in Xcode simulators. Use 'describe_ui' to identify exact positions, ensuring accurate interactions for testing.

Instructions

Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
delayNo
downNo
simulatorUuidYes
upNo
xYes
yYes

Implementation Reference

  • The main handler function `touchLogic` that parses parameters, constructs the axe 'touch' command, executes it via `executeAxeCommand`, and handles responses and errors.
    export async function touchLogic( params: TouchParams, executor: CommandExecutor, axeHelpers?: AxeHelpers, ): Promise<ToolResponse> { const toolName = 'touch'; // Params are already validated by createTypedTool - use directly const { simulatorId, x, y, down, up, delay } = params; // Validate that at least one of down or up is specified if (!down && !up) { return createErrorResponse('At least one of "down" or "up" must be true'); } const commandArgs = ['touch', '-x', String(x), '-y', String(y)]; if (down) { commandArgs.push('--down'); } if (up) { commandArgs.push('--up'); } if (delay !== undefined) { commandArgs.push('--delay', String(delay)); } const actionText = down && up ? 'touch down+up' : down ? 'touch down' : 'touch up'; log( 'info', `${LOG_PREFIX}/${toolName}: Starting ${actionText} at (${x}, ${y}) on ${simulatorId}`, ); try { await executeAxeCommand(commandArgs, simulatorId, 'touch', executor, axeHelpers); log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`); const warning = getCoordinateWarning(simulatorId); const message = `Touch event (${actionText}) at (${x}, ${y}) executed successfully.`; if (warning) { return createTextResponse(`${message}\n\n${warning}`); } return createTextResponse(message); } catch (error) { log( 'error', `${LOG_PREFIX}/${toolName}: Failed - ${error instanceof Error ? error.message : String(error)}`, ); if (error instanceof DependencyError) { return createAxeNotAvailableResponse(); } else if (error instanceof AxeError) { return createErrorResponse( `Failed to execute touch event: ${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 the input parameters for the touch tool, including simulatorId, coordinates, down/up flags, and optional delay.
    const touchSchema = z.object({ simulatorId: z.string().uuid('Invalid Simulator UUID format'), x: z.number().int('X coordinate must be an integer'), y: z.number().int('Y coordinate must be an integer'), down: z.boolean().optional(), up: z.boolean().optional(), delay: z.number().min(0, 'Delay must be non-negative').optional(), });
  • Default export that registers the 'touch' tool with MCP, providing name, description, schema, and a session-aware handler wrapping `touchLogic`.
    export default { name: 'touch', description: "Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots).", schema: publicSchemaObject.shape, // MCP SDK compatibility handler: createSessionAwareTool<TouchParams>({ internalSchema: touchSchema as unknown as z.ZodType<TouchParams>, logicFunction: (params: TouchParams, executor: CommandExecutor) => touchLogic(params, executor), getExecutor: getDefaultCommandExecutor, requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }], }), };
  • Helper for tracking describe_ui sessions and providing coordinate warnings in touch responses.
    interface DescribeUISession { timestamp: number; simulatorId: string; } const describeUITimestamps = new Map<string, DescribeUISession>(); const DESCRIBE_UI_WARNING_TIMEOUT = 60000; // 60 seconds function getCoordinateWarning(simulatorId: string): string | null { const session = describeUITimestamps.get(simulatorId); if (!session) { return 'Warning: describe_ui has not been called yet. Consider using describe_ui for precise coordinates instead of guessing from screenshots.'; } const timeSinceDescribe = Date.now() - session.timestamp; if (timeSinceDescribe > DESCRIBE_UI_WARNING_TIMEOUT) { const secondsAgo = Math.round(timeSinceDescribe / 1000); return `Warning: describe_ui was last called ${secondsAgo} seconds ago. Consider refreshing UI coordinates with describe_ui instead of using potentially stale coordinates.`; } return null; }
  • Inlined helper function to execute AXe commands, used by touchLogic to run the 'touch' axe subcommand.
    async function executeAxeCommand( commandArgs: string[], simulatorId: string, commandName: string, executor: CommandExecutor = getDefaultCommandExecutor(), axeHelpers?: AxeHelpers, ): Promise<void> { // Use injected helpers or default to imported functions const helpers = axeHelpers ?? { getAxePath, getBundledAxeEnvironment }; // Get the appropriate axe binary path const axeBinary = helpers.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' ? helpers.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)}`); } }

Other Tools

Related Tools

  • @cameroncooke/XcodeBuildMCP
  • @cameroncooke/XcodeBuildMCP
  • @atom2ueki/mcp-server-ios-simulator
  • @joshuayoes/ios-simulator-mcp
  • @joshuayoes/ios-simulator-mcp
  • @vs4vijay/espresso-mcp

Latest Blog Posts

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/cameroncooke/XcodeBuildMCP'

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