Skip to main content
Glama
input.ts4.85 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { KeyInput } from 'puppeteer'; import { keyboardSchema, mouseSchema, scrollSchema } from '../schemas.js'; import { getPageForOperation } from '../tabs.js'; import { handleResult, ok, err, selectorNotFound, normalizeError, } from '../errors.js'; import type { KeyModifier, MouseButton, MouseAction, ScrollDirection } from '../types.js'; /** * Register input tools */ export function registerInputTools(server: McpServer): void { // Keyboard input server.tool( 'keyboard', 'Press a key or key combination', keyboardSchema.shape, async ({ key, modifiers, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const mods = (modifiers ?? []) as KeyModifier[]; try { // Press modifier keys for (const mod of mods) { await page.keyboard.down(mod); } // Press the main key await page.keyboard.press(key as KeyInput); // Release modifier keys for (const mod of mods.reverse()) { await page.keyboard.up(mod); } return handleResult(ok({ pressed: true, key, modifiers: mods, })); } catch (error) { return handleResult(err(normalizeError(error))); } } ); // Mouse input server.tool( 'mouse', 'Perform mouse actions at specific coordinates', mouseSchema.shape, async ({ x, y, button, action, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const mouseButton = (button ?? 'left') as MouseButton; const mouseAction = (action ?? 'click') as MouseAction; try { switch (mouseAction) { case 'move': await page.mouse.move(x, y); break; case 'click': await page.mouse.click(x, y, { button: mouseButton }); break; case 'down': await page.mouse.move(x, y); await page.mouse.down({ button: mouseButton }); break; case 'up': await page.mouse.move(x, y); await page.mouse.up({ button: mouseButton }); break; } return handleResult(ok({ action: mouseAction, x, y, button: mouseButton, })); } catch (error) { return handleResult(err(normalizeError(error))); } } ); // Scroll server.tool( 'scroll', 'Scroll the page or a specific element', scrollSchema.shape, async ({ direction, amount, selector, smooth, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const scrollDirection = (direction ?? 'down') as ScrollDirection; const scrollAmount = amount ?? 100; const useSmooth = smooth ?? true; try { // Calculate scroll deltas let deltaX = 0; let deltaY = 0; switch (scrollDirection) { case 'up': deltaY = -scrollAmount; break; case 'down': deltaY = scrollAmount; break; case 'left': deltaX = -scrollAmount; break; case 'right': deltaX = scrollAmount; break; } if (selector) { // Scroll specific element const element = await page.$(selector); if (!element) { return handleResult(err(selectorNotFound(selector))); } await element.evaluate( (el, dx, dy, smoothScroll) => { el.scrollBy({ left: dx, top: dy, behavior: smoothScroll ? 'smooth' : 'auto', }); }, deltaX, deltaY, useSmooth ); } else { // Scroll page await page.evaluate( (dx, dy, smoothScroll) => { window.scrollBy({ left: dx, top: dy, behavior: smoothScroll ? 'smooth' : 'auto', }); }, deltaX, deltaY, useSmooth ); } return handleResult(ok({ scrolled: true, direction: scrollDirection, amount: scrollAmount, selector, })); } catch (error) { 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