Skip to main content
Glama
interaction.ts5.73 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { clickSchema, fillSchema, selectSchema, hoverSchema, focusSchema } from '../schemas.js'; import { getPageForOperation } from '../tabs.js'; import { getDefaultTimeout } from '../browser.js'; import { handleResult, ok, err, selectorNotFound, normalizeError, } from '../errors.js'; import type { MouseButton } from '../types.js'; /** * Register interaction tools */ export function registerInteractionTools(server: McpServer): void { // Click element server.tool( 'click', 'Click an element on the page', clickSchema.shape, async ({ selector, button, clickCount, delay, timeout, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const timeoutMs = timeout ?? getDefaultTimeout(); try { // Wait for element to be visible const element = await page.waitForSelector(selector, { timeout: timeoutMs, visible: true, }); if (!element) { return handleResult(err(selectorNotFound(selector))); } await element.click({ button: (button ?? 'left') as MouseButton, clickCount: clickCount ?? 1, delay: delay, }); return handleResult(ok({ clicked: true, selector })); } catch (error) { if (error instanceof Error && error.message.includes('waiting for selector')) { return handleResult(err(selectorNotFound(selector))); } return handleResult(err(normalizeError(error))); } } ); // Fill input server.tool( 'fill', 'Fill a text input or textarea with a value', fillSchema.shape, async ({ selector, value, clearFirst, timeout, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const timeoutMs = timeout ?? getDefaultTimeout(); try { const element = await page.waitForSelector(selector, { timeout: timeoutMs, }); if (!element) { return handleResult(err(selectorNotFound(selector))); } if (clearFirst ?? true) { // Triple-click to select all, then delete await element.click({ clickCount: 3 }); await page.keyboard.press('Backspace'); } await element.type(value); return handleResult(ok({ filled: true, selector, value })); } catch (error) { if (error instanceof Error && error.message.includes('waiting for selector')) { return handleResult(err(selectorNotFound(selector))); } return handleResult(err(normalizeError(error))); } } ); // Select dropdown option server.tool( 'select', 'Select option(s) from a dropdown/select element', selectSchema.shape, async ({ selector, values, timeout, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const timeoutMs = timeout ?? getDefaultTimeout(); try { await page.waitForSelector(selector, { timeout: timeoutMs }); const selected = await page.select(selector, ...values); return handleResult(ok({ selected, selector })); } catch (error) { if (error instanceof Error && error.message.includes('waiting for selector')) { return handleResult(err(selectorNotFound(selector))); } return handleResult(err(normalizeError(error))); } } ); // Hover over element server.tool( 'hover', 'Hover over an element on the page', hoverSchema.shape, async ({ selector, timeout, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const timeoutMs = timeout ?? getDefaultTimeout(); try { const element = await page.waitForSelector(selector, { timeout: timeoutMs, }); if (!element) { return handleResult(err(selectorNotFound(selector))); } await element.hover(); return handleResult(ok({ hovered: true, selector })); } catch (error) { if (error instanceof Error && error.message.includes('waiting for selector')) { return handleResult(err(selectorNotFound(selector))); } return handleResult(err(normalizeError(error))); } } ); // Focus element server.tool( 'focus', 'Focus an element on the page', focusSchema.shape, async ({ selector, timeout, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const timeoutMs = timeout ?? getDefaultTimeout(); try { const element = await page.waitForSelector(selector, { timeout: timeoutMs, }); if (!element) { return handleResult(err(selectorNotFound(selector))); } await element.focus(); return handleResult(ok({ focused: true, selector })); } catch (error) { if (error instanceof Error && error.message.includes('waiting for selector')) { return handleResult(err(selectorNotFound(selector))); } return handleResult(err(normalizeError(error))); } } ); }

Implementation Reference

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/andytango/puppeteer-mcp'

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