Skip to main content
Glama
snapshot.ts4.82 kB
/** * Snapshot tools for DOM structure capture with UID mapping */ import { successResponse, errorResponse, TOKEN_LIMITS } from '../utils/response-helpers.js'; import type { McpToolResponse } from '../types/common.js'; const DEFAULT_SNAPSHOT_LINES = 100; // Tool definitions export const takeSnapshotTool = { name: 'take_snapshot', description: 'Capture DOM snapshot with stable UIDs. Retake after navigation.', inputSchema: { type: 'object', properties: { maxLines: { type: 'number', description: 'Max lines (default: 100)', }, includeAttributes: { type: 'boolean', description: 'Include ARIA attributes (default: false)', }, includeText: { type: 'boolean', description: 'Include text (default: true)', }, maxDepth: { type: 'number', description: 'Max tree depth', }, }, }, }; export const resolveUidToSelectorTool = { name: 'resolve_uid_to_selector', description: 'Resolve UID to CSS selector. Fails if stale.', inputSchema: { type: 'object', properties: { uid: { type: 'string', description: 'UID from snapshot', }, }, required: ['uid'], }, }; export const clearSnapshotTool = { name: 'clear_snapshot', description: 'Clear snapshot cache. Usually not needed.', inputSchema: { type: 'object', properties: {}, }, }; // Handlers export async function handleTakeSnapshot(args: unknown): Promise<McpToolResponse> { try { const { maxLines: requestedMaxLines = DEFAULT_SNAPSHOT_LINES, includeAttributes = false, includeText = true, maxDepth, } = (args as { maxLines?: number; includeAttributes?: boolean; includeText?: boolean; maxDepth?: number; }) || {}; // Apply hard cap on maxLines to prevent token overflow const maxLines = Math.min(Math.max(1, requestedMaxLines), TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP); const wasCapped = requestedMaxLines > TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP; const { getFirefox } = await import('../index.js'); const firefox = await getFirefox(); const snapshot = await firefox.takeSnapshot(); // Import formatter to apply custom options const { formatSnapshotTree } = await import('../firefox/snapshot/formatter.js'); const options: any = { includeAttributes, includeText, }; if (maxDepth !== undefined) { options.maxDepth = maxDepth; } const formattedText = formatSnapshotTree(snapshot.json.root, 0, options); // Get snapshot text (truncated if needed) const lines = formattedText.split('\n'); const truncated = lines.length > maxLines; const displayLines = truncated ? lines.slice(0, maxLines) : lines; // Build compact output let output = `📸 Snapshot (id=${snapshot.json.snapshotId})`; if (wasCapped) { output += ` [maxLines capped: ${TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP}]`; } if (snapshot.json.truncated) { output += ' [DOM truncated]'; } output += '\n\n'; // Add snapshot tree output += displayLines.join('\n'); if (truncated) { output += `\n\n[+${lines.length - maxLines} lines, use maxLines to see more]`; } return successResponse(output); } catch (error) { return errorResponse( new Error( `Failed to take snapshot: ${(error as Error).message}\n\n` + 'The page may not be fully loaded or accessible.' ) ); } } export async function handleResolveUidToSelector(args: unknown): Promise<McpToolResponse> { try { const { uid } = args as { uid: string }; if (!uid || typeof uid !== 'string') { throw new Error('uid parameter is required and must be a string'); } const { getFirefox } = await import('../index.js'); const firefox = await getFirefox(); try { const selector = firefox.resolveUidToSelector(uid); return successResponse(`${uid} → ${selector}`); } catch (error) { const errorMsg = (error as Error).message; // Concise error for stale UIDs if ( errorMsg.includes('stale') || errorMsg.includes('Snapshot') || errorMsg.includes('UID') || errorMsg.includes('not found') ) { throw new Error(`UID "${uid}" stale/invalid. Call take_snapshot first.`); } throw error; } } catch (error) { return errorResponse(error as Error); } } export async function handleClearSnapshot(_args: unknown): Promise<McpToolResponse> { try { const { getFirefox } = await import('../index.js'); const firefox = await getFirefox(); firefox.clearSnapshot(); return successResponse('🧹 Snapshot cleared'); } catch (error) { return errorResponse(error as Error); } }

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/freema/firefox-devtools-mcp'

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