Skip to main content
Glama

drag

Automate drag-and-drop actions in Firefox using Playwright by specifying coordinates, offsets, and duration. Ideal for browser testing and debugging multi-tab workflows.

Instructions

Perform drag operation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
durationNo
fromCoordinatesNo
offsetXNo
offsetYNo
selectorNo
stepsNo
tabIdNo
toCoordinatesNo

Implementation Reference

  • The handler function for the 'drag' tool. It supports dragging from an element selector or coordinates to a target selector, coordinates, or offset. Uses Playwright's mouse API for precise control, with optional smooth animation over duration with steps.
    async drag(args) {
      this.ensureBrowserRunning();
      const { 
        selector, 
        fromCoordinates, 
        toSelector, 
        toCoordinates, 
        offsetX, 
        offsetY, 
        duration = 0, 
        steps = 1, 
        tabId 
      } = args;
      const page = this.getPage(tabId);
      
      // Validate inputs
      if (!selector && !fromCoordinates) {
        throw new Error('Either selector or fromCoordinates must be provided');
      }
      
      if (!toSelector && !toCoordinates && offsetX === undefined && offsetY === undefined) {
        throw new Error('Either toSelector, toCoordinates, or offset values must be provided');
      }
      
      // Get starting position
      let startX, startY;
      if (selector) {
        const element = await page.$(selector);
        if (!element) {
          throw new Error(`Element not found: ${selector}`);
        }
        const box = await element.boundingBox();
        if (!box) {
          throw new Error(`Cannot get bounding box for element: ${selector}`);
        }
        startX = box.x + box.width / 2;
        startY = box.y + box.height / 2;
      } else {
        startX = fromCoordinates.x;
        startY = fromCoordinates.y;
      }
      
      // Get ending position
      let endX, endY;
      if (toSelector) {
        const element = await page.$(toSelector);
        if (!element) {
          throw new Error(`Target element not found: ${toSelector}`);
        }
        const box = await element.boundingBox();
        if (!box) {
          throw new Error(`Cannot get bounding box for target element: ${toSelector}`);
        }
        endX = box.x + box.width / 2;
        endY = box.y + box.height / 2;
      } else if (toCoordinates) {
        endX = toCoordinates.x;
        endY = toCoordinates.y;
      } else {
        // Use offset from start position
        endX = startX + (offsetX || 0);
        endY = startY + (offsetY || 0);
      }
      
      // Perform the drag
      await page.mouse.move(startX, startY);
      await page.mouse.down();
      
      if (duration > 0 && steps > 1) {
        // Smooth drag with intermediate steps
        const stepDelay = duration / steps;
        for (let i = 1; i <= steps; i++) {
          const progress = i / steps;
          const currentX = startX + (endX - startX) * progress;
          const currentY = startY + (endY - startY) * progress;
          await page.mouse.move(currentX, currentY);
          if (i < steps) {
            await new Promise(resolve => setTimeout(resolve, stepDelay));
          }
        }
      } else {
        // Direct drag
        await page.mouse.move(endX, endY);
      }
      
      await page.mouse.up();
      
      return {
        content: [{
          type: 'text',
          text: `Dragged from (${Math.round(startX)}, ${Math.round(startY)}) to (${Math.round(endX)}, ${Math.round(endY)}) in tab '${tabId || this.activeTabId}'${duration > 0 ? ` over ${duration}ms` : ''}`
        }]
      };
    }
  • Input schema for the 'drag' tool defining parameters like selector, from/to coordinates, offsets, duration, steps, and tabId.
    inputSchema: {
      type: 'object',
      properties: {
        selector: { type: 'string' },
        fromCoordinates: {
          type: 'object',
          properties: { x: { type: 'number' }, y: { type: 'number' } }
        },
        toCoordinates: {
          type: 'object',
          properties: { x: { type: 'number' }, y: { type: 'number' } }
        },
        offsetX: { type: 'number' },
        offsetY: { type: 'number' },
        duration: { type: 'number', default: 0 },
        steps: { type: 'number', default: 1 },
        tabId: { type: 'string' }
      }
    }
  • Registration of the 'drag' tool in the tools list returned by the ListToolsRequest handler.
    {
      name: 'drag',
      description: 'Perform drag operation',
      inputSchema: {
        type: 'object',
        properties: {
          selector: { type: 'string' },
          fromCoordinates: {
            type: 'object',
            properties: { x: { type: 'number' }, y: { type: 'number' } }
          },
          toCoordinates: {
            type: 'object',
            properties: { x: { type: 'number' }, y: { type: 'number' } }
          },
          offsetX: { type: 'number' },
          offsetY: { type: 'number' },
          duration: { type: 'number', default: 0 },
          steps: { type: 'number', default: 1 },
          tabId: { type: 'string' }
        }
      }
    },
  • Dispatch registration in the CallToolRequest switch statement that routes calls to the drag handler.
    case 'drag':
      return await this.drag(args);

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/JediLuke/firefox-mcp-server'

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