Skip to main content
Glama
by macacoai
getext.js10.6 kB
/** * Copyright (c) Microsoft Corpo handle: async (tab, params, response) => { const { ref, element, attribute } = params; try { const locator = await tab.refLocator(params); let result: string; if (attribute) { result = await locator.getAttribute(attribute) || ''; response.addCode(`const attributeValue = await page.${await generateLocator(locator)}.getAttribute('${attribute}');`); } else { result = await locator.textContent() || ''; response.addCode(`const textContent = await page.${await generateLocator(locator)}.textContent();`); } response.addResult(`${attribute ? `Attribute "${attribute}"` : 'Text content'}: ${result}`); } catch (error) { response.addError(`Failed to get ${attribute ? `attribute "${attribute}"` : 'text content'} from element: ${error}`); } },ed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { z } from 'zod'; import { defineTabTool } from './tool.js'; import { elementSchema } from './snapshot.js'; import { generateLocator } from './utils.js'; // Schema for comprehensive element information const getElementSchema = elementSchema.extend({ includeAttributes: z.array(z.string()).optional().describe('Optional array of specific attributes to include (e.g., ["id", "class", "href"])'), includeMetadata: z.boolean().optional().default(true).describe('Whether to include element metadata (tag, type, state, etc.)'), }); const getElement = defineTabTool({ capability: 'core', schema: { name: 'browser_get_element', title: 'Get comprehensive element information', description: 'Get complete information from an element including text content, input value, attributes, and metadata', inputSchema: getElementSchema, type: 'readOnly', }, handle: async (tab, params, response) => { const { ref, element, includeAttributes, includeMetadata } = params; try { const locator = await tab.refLocator(params); const elementInfo = {}; // Get basic element information const tagName = await locator.evaluate(el => el.tagName.toLowerCase()); const elementType = await locator.evaluate(el => el.type || null); // Get text content const textContent = await locator.textContent() || ''; if (textContent.trim()) { elementInfo.textContent = textContent.trim(); } // Get input value if it's an input element const isInputElement = ['input', 'textarea', 'select'].includes(tagName); if (isInputElement) { try { const inputValue = await locator.inputValue(); if (inputValue !== null && inputValue !== undefined) { elementInfo.inputValue = inputValue; } } catch (e) { // Some elements might not support inputValue, that's ok } } // Get common attributes const commonAttributes = ['id', 'class', 'name', 'href', 'src', 'alt', 'title', 'placeholder', 'value']; const attributes = {}; for (const attr of commonAttributes) { const value = await locator.getAttribute(attr); if (value !== null) { attributes[attr] = value; } } // Get additional requested attributes if (includeAttributes && includeAttributes.length > 0) { for (const attr of includeAttributes) { if (!attributes[attr]) { const value = await locator.getAttribute(attr); if (value !== null) { attributes[attr] = value; } } } } if (Object.keys(attributes).length > 0) { elementInfo.attributes = attributes; } // Get metadata if requested if (includeMetadata) { const metadata = { tagName: tagName, }; if (elementType) { metadata.type = elementType; } // Get element state const isEnabled = await locator.isEnabled(); const isVisible = await locator.isVisible(); const isEditable = await locator.isEditable(); metadata.state = { enabled: isEnabled, visible: isVisible, editable: isEditable, }; // Get accessibility information try { const ariaLabel = await locator.getAttribute('aria-label'); const ariaRole = await locator.getAttribute('role'); if (ariaLabel || ariaRole) { metadata.accessibility = {}; if (ariaLabel) metadata.accessibility.label = ariaLabel; if (ariaRole) metadata.accessibility.role = ariaRole; } } catch (e) { // Accessibility info is optional } elementInfo.metadata = metadata; } // Generate appropriate code based on what was retrieved const codeLines = []; codeLines.push(`const locator = page.${await generateLocator(locator)};`); if (elementInfo.textContent) { codeLines.push(`const textContent = await locator.textContent();`); } if (elementInfo.inputValue !== undefined) { codeLines.push(`const inputValue = await locator.inputValue();`); } if (elementInfo.attributes) { const attrNames = Object.keys(elementInfo.attributes); if (attrNames.length === 1) { codeLines.push(`const ${attrNames[0]}Attr = await locator.getAttribute('${attrNames[0]}');`); } else if (attrNames.length > 1) { codeLines.push(`// Get multiple attributes`); attrNames.forEach(attr => { codeLines.push(`const ${attr}Attr = await locator.getAttribute('${attr}');`); }); } } if (elementInfo.metadata) { codeLines.push(`const tagName = await locator.evaluate(el => el.tagName.toLowerCase());`); codeLines.push(`const isEnabled = await locator.isEnabled();`); codeLines.push(`const isVisible = await locator.isVisible();`); } response.addCode(codeLines.join('\n')); // Format the result const resultLines = [`Element Information:`]; if (elementInfo.textContent) { resultLines.push(`- Text Content: "${elementInfo.textContent}"`); } if (elementInfo.inputValue !== undefined) { resultLines.push(`- Input Value: "${elementInfo.inputValue}"`); } if (elementInfo.attributes && Object.keys(elementInfo.attributes).length > 0) { resultLines.push(`- Attributes:`); Object.entries(elementInfo.attributes).forEach(([key, value]) => { resultLines.push(` - ${key}: "${value}"`); }); } if (elementInfo.metadata) { const { metadata } = elementInfo; resultLines.push(`- Metadata:`); resultLines.push(` - Tag: ${metadata.tagName}`); if (metadata.type) { resultLines.push(` - Type: ${metadata.type}`); } resultLines.push(` - State: enabled=${metadata.state.enabled}, visible=${metadata.state.visible}, editable=${metadata.state.editable}`); if (metadata.accessibility) { resultLines.push(` - Accessibility:`); if (metadata.accessibility.label) { resultLines.push(` - Label: "${metadata.accessibility.label}"`); } if (metadata.accessibility.role) { resultLines.push(` - Role: "${metadata.accessibility.role}"`); } } } response.addResult(resultLines.join('\n')); } catch (error) { response.addError(`Failed to get element information: ${error}`); } }, }); const getPageInfo = defineTabTool({ capability: 'core', schema: { name: 'browser_get_page_info', title: 'Get page information', description: 'Get basic information about the current page (title, URL, etc.)', inputSchema: z.object({}), type: 'readOnly', }, handle: async (tab, params, response) => { try { const title = await tab.page.title(); const url = tab.page.url(); const viewport = tab.page.viewportSize(); response.addCode(`const title = await page.title(); const url = page.url(); const viewport = page.viewportSize();`); response.addResult(`Page Information: - Title: ${title} - URL: ${url} - Viewport: ${viewport ? `${viewport.width}x${viewport.height}` : 'Not set'}`); } catch (error) { response.addError(`Failed to get page information: ${error}`); } }, }); export default [ getElement, getPageInfo ];

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/macacoai/mcp-playwright'

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