Skip to main content
Glama
content.ts4.12 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { evaluateSchema, getContentSchema, querySelectorSchema } from '../schemas.js'; import { getPageForOperation } from '../tabs.js'; import { handleResult, ok, err, evaluationError, selectorNotFound, normalizeError, } from '../errors.js'; /** * Register content extraction tools */ export function registerContentTools(server: McpServer): void { // Evaluate JavaScript server.tool( 'evaluate', 'Execute JavaScript code in the browser context and return the result', evaluateSchema.shape, async ({ script, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; try { // Use Function constructor to evaluate the script const result = await page.evaluate((code) => { // eslint-disable-next-line no-new-func const fn = new Function(code); return fn(); }, script); return handleResult(ok({ result })); } catch (error) { if (error instanceof Error) { return handleResult(err(evaluationError(error.message))); } return handleResult(err(normalizeError(error))); } } ); // Get page content server.tool( 'get_content', 'Get the HTML or text content of the page or a specific element', getContentSchema.shape, async ({ selector, type, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; const contentType = type ?? 'text'; try { if (selector) { // Get content of specific element const element = await page.$(selector); if (!element) { return handleResult(err(selectorNotFound(selector))); } const content = await element.evaluate((el, t) => { return t === 'html' ? el.innerHTML : el.textContent ?? ''; }, contentType); return handleResult(ok({ content, selector })); } else { // Get full page content let content: string; if (contentType === 'html') { content = await page.content(); } else { content = await page.evaluate(() => document.body.innerText); } return handleResult(ok({ content })); } } catch (error) { return handleResult(err(normalizeError(error))); } } ); // Query selector server.tool( 'query_selector', 'Get information about an element matching a CSS selector', querySelectorSchema.shape, async ({ selector, tabId }) => { const pageResult = await getPageForOperation(tabId); if (!pageResult.success) { return handleResult(pageResult); } const page = pageResult.data; try { const element = await page.$(selector); if (!element) { return handleResult(ok({ exists: false, selector, })); } const info = await element.evaluate((el) => { const rect = el.getBoundingClientRect(); const attributes: Record<string, string> = {}; for (let i = 0; i < el.attributes.length; i++) { const attr = el.attributes[i]; if (attr) { attributes[attr.name] = attr.value; } } return { tagName: el.tagName.toLowerCase(), textContent: el.textContent?.slice(0, 1000) ?? '', attributes, boundingBox: { x: rect.x, y: rect.y, width: rect.width, height: rect.height, }, }; }); return handleResult(ok({ exists: true, selector, ...info, })); } 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