Skip to main content
Glama

tap

Tap at specified coordinates on Xcode simulators using exact UI element positions obtained via describe_ui. Configure optional timing delays for precise interactions.

Instructions

Tap at specific coordinates. Use describe_ui to get precise element coordinates (don't guess from screenshots). Supports optional timing delays.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
postDelayNo
preDelayNo
simulatorUuidYes
xYes
yYes

Implementation Reference

  • Core handler function that constructs the axe 'tap' command with coordinates and optional delays, executes it on the simulator, handles responses, warnings about UI coordinates, and all error cases.
    export async function tapLogic(
      params: TapParams,
      executor: CommandExecutor,
      axeHelpers: AxeHelpers = {
        getAxePath,
        getBundledAxeEnvironment,
        createAxeNotAvailableResponse,
      },
    ): Promise<ToolResponse> {
      const toolName = 'tap';
      const { simulatorId, x, y, preDelay, postDelay } = params;
      const commandArgs = ['tap', '-x', String(x), '-y', String(y)];
      if (preDelay !== undefined) {
        commandArgs.push('--pre-delay', String(preDelay));
      }
      if (postDelay !== undefined) {
        commandArgs.push('--post-delay', String(postDelay));
      }
    
      log('info', `${LOG_PREFIX}/${toolName}: Starting for (${x}, ${y}) on ${simulatorId}`);
    
      try {
        await executeAxeCommand(commandArgs, simulatorId, 'tap', executor, axeHelpers);
        log('info', `${LOG_PREFIX}/${toolName}: Success for ${simulatorId}`);
    
        const warning = getCoordinateWarning(simulatorId);
        const message = `Tap at (${x}, ${y}) simulated successfully.`;
    
        if (warning) {
          return createTextResponse(`${message}\n\n${warning}`);
        }
    
        return createTextResponse(message);
      } 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 simulate tap at (${x}, ${y}): ${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 input schema validating simulatorId (UUID), integer coordinates x/y, and optional non-negative pre/post delays.
    const tapSchema = 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'),
      preDelay: z.number().min(0, 'Pre-delay must be non-negative').optional(),
      postDelay: z.number().min(0, 'Post-delay must be non-negative').optional(),
    });
  • Default export defining the tool object with name 'tap', description, public schema (without simulatorId), and session-aware handler wrapper. This object is loaded by the plugin registry and registered dynamically.
    export default {
      name: 'tap',
      description:
        "Tap at specific coordinates. Use describe_ui to get precise element coordinates (don't guess from screenshots). Supports optional timing delays.",
      schema: publicSchemaObject.shape, // MCP SDK compatibility
      handler: createSessionAwareTool<TapParams>({
        internalSchema: tapSchema as unknown as z.ZodType<TapParams>,
        logicFunction: (params: TapParams, executor: CommandExecutor) =>
          tapLogic(params, executor, {
            getAxePath,
            getBundledAxeEnvironment,
            createAxeNotAvailableResponse,
          }),
        getExecutor: getDefaultCommandExecutor,
        requirements: [{ allOf: ['simulatorId'], message: 'simulatorId is required' }],
      }),
    };
  • Internal helper that executes the axe command binary with udid, handles bundled axe env, checks success, logs warnings, and 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)}`);
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'Supports optional timing delays' which adds useful context about pre/post delay parameters. However, it doesn't describe what happens after the tap (e.g., UI response, error conditions, or whether this is a destructive action), leaving gaps in behavioral understanding.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly concise with three clear sentences that each serve a distinct purpose: stating the action, providing usage guidance, and mentioning optional features. No wasted words, and the most critical information (what the tool does) comes first.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter tool with no annotations and no output schema, the description provides adequate basic information but has significant gaps. It explains the core action and how to get coordinates, but doesn't cover parameter details fully, behavioral outcomes, or error conditions. Given the complexity, it's minimally complete but could be more comprehensive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage for all 5 parameters, the description adds some value by mentioning 'coordinates' (mapping to x/y) and 'timing delays' (mapping to preDelay/postDelay). However, it doesn't explain the simulatorUuid parameter at all, leaving a critical parameter undocumented. The description partially compensates but doesn't fully address the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Tap at specific coordinates') and resource (coordinates), making the purpose immediately understandable. It doesn't explicitly differentiate from sibling tools like 'touch' or 'button', but the coordinate-based approach is specific enough for basic understanding.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear guidance on how to obtain coordinates ('Use describe_ui to get precise element coordinates') and warns against alternatives ('don't guess from screenshots'). However, it doesn't explicitly state when to use this tool versus similar sibling tools like 'touch' or 'button', which would be helpful for complete differentiation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

  • @getsentry/XcodeBuildMCP
  • @getsentry/XcodeBuildMCP
  • @joshuayoes/ios-simulator-mcp
  • @vs4vijay/espresso-mcp
  • @joshuayoes/ios-simulator-mcp
  • @getsentry/XcodeBuildMCP

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

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