Skip to main content
Glama

firefox-devtools-mcp

formatter.ts3.94 kB
/** * Snapshot text formatter * Formats snapshot tree as LLM-friendly text */ import type { SnapshotNode } from './types.js'; /** * Max attribute value length */ const MAX_ATTR_LENGTH = 50; /** * Formatting options */ export interface FormatOptions { includeAttributes?: boolean; includeText?: boolean; maxDepth?: number; } /** * Format snapshot tree as human-readable text */ export function formatSnapshotTree( node: SnapshotNode, depth = 0, options: FormatOptions = {} ): string { const { includeAttributes = true, includeText = true, maxDepth } = options; // Check max depth if (maxDepth !== undefined && depth >= maxDepth) { return ''; } const indent = ' '.repeat(depth); const attrs: string[] = []; // UID (always first) attrs.push(`uid=${node.uid}`); // Role or tag const role = node.role || node.tag; attrs.push(role); // Name (in quotes) - only if exists if (node.name) { attrs.push(`"${truncate(node.name, MAX_ATTR_LENGTH)}"`); } // Tag (for debugging) if (node.role && node.role !== node.tag) { attrs.push(`tag=${node.tag}`); } // Value if (node.value) { attrs.push(`value="${truncate(node.value, MAX_ATTR_LENGTH)}"`); } // Href if (node.href) { attrs.push(`href="${truncate(node.href, MAX_ATTR_LENGTH)}"`); } // Src if (node.src) { attrs.push(`src="${truncate(node.src, MAX_ATTR_LENGTH)}"`); } // Text (controlled by includeText option) if (includeText && node.text) { attrs.push(`text="${truncate(node.text, MAX_ATTR_LENGTH)}"`); } // ARIA attributes (controlled by includeAttributes option) if (includeAttributes && node.aria) { // Boolean states if (node.aria.disabled) { attrs.push('disabled'); } if (node.aria.hidden) { attrs.push('hidden'); } if (node.aria.selected) { attrs.push('selected'); } if (node.aria.expanded !== undefined) { attrs.push(node.aria.expanded ? 'expanded' : 'collapsed'); } // Mixed states if (node.aria.checked !== undefined) { if (node.aria.checked === 'mixed') { attrs.push('checked="mixed"'); } else { attrs.push(node.aria.checked ? 'checked' : 'unchecked'); } } if (node.aria.pressed !== undefined) { if (node.aria.pressed === 'mixed') { attrs.push('pressed="mixed"'); } else { attrs.push(node.aria.pressed ? 'pressed' : 'unpressed'); } } // String properties if (node.aria.autocomplete) { attrs.push(`autocomplete="${node.aria.autocomplete}"`); } if (node.aria.haspopup) { attrs.push(`haspopup="${node.aria.haspopup}"`); } if (node.aria.invalid) { attrs.push(`invalid="${node.aria.invalid}"`); } if (node.aria.level) { attrs.push(`level=${node.aria.level}`); } } // Computed properties (controlled by includeAttributes option) if (includeAttributes && node.computed) { if (node.computed.focusable) { attrs.push('focusable'); } if (node.computed.interactive) { attrs.push('interactive'); } if (!node.computed.visible) { attrs.push('invisible'); } if (!node.computed.accessible) { attrs.push('inaccessible'); } } // Iframe marker if (node.isIframe) { attrs.push('[iframe'); if (node.frameSrc) { attrs.push(`src="${truncate(node.frameSrc, MAX_ATTR_LENGTH)}"`); } if (node.crossOrigin) { attrs.push('cross-origin'); } attrs.push(']'); } let result = indent + attrs.join(' ') + '\n'; // Format children (pass options recursively) for (const child of node.children) { result += formatSnapshotTree(child, depth + 1, options); } return result; } /** * Truncate string to max length */ function truncate(str: string, maxLen: number): string { if (str.length <= maxLen) { return str; } return str.substring(0, maxLen - 3) + '...'; }

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