Skip to main content
Glama

browser-evaluate

Execute JavaScript code directly in the browser to retrieve data, manipulate DOM elements, or run functions, returning results for automation and testing.

Instructions

Evaluates JavaScript code directly in the browser context and returns the result. Supports expressions, function strings, and complex code with automatic execution handling. Can target the entire page or work with element handles for precise DOM manipulation.

Examples:

  • Simple expression: "document.title"

  • Arrow function: "() => document.querySelectorAll('a').length"

  • Regular function: "function() { return document.body.children.length; }"

  • Function with arguments: "(tag) => document.getElementsByTagName(tag).length"

  • Complex code: "() => { const divs = document.querySelectorAll('div'); return { count: divs.length, hasClass: divs[0]?.className }; }"

  • Async function: "async () => { const res = await fetch('/api'); return res.json(); }"

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
functionYesJavaScript code to execute (expression or function string). Examples: - Expression: "document.title" - Arrow function: "() => document.querySelectorAll('a').length" - Regular function: "function() { return document.body.children.length; }" - With arguments: "(tag) => document.getElementsByTagName(tag).length" - Async function: "async () => { const res = await fetch('/api'); return res.json(); }"
argsNoOptional arguments to pass to the JavaScript function. Can be primitives, objects, arrays, or element references. Example: { url: "https://example.com", count: 5 }
elementNoCSS selector to target specific element for evaluation
contextIdNoBrowser ID to execute on (uses most recent browser if not provided)
timeoutNoExecution timeout in milliseconds (default: 30000)
returnTypeNoExpected return type for better serialization (default: auto)

Implementation Reference

  • Registration of the 'browser-evaluate' tool using server.tool(), including description, input schema, and handler reference.
    server.tool( 'browser-evaluate', `Evaluates JavaScript code directly in the browser context and returns the result. Supports expressions, function strings, and complex code with automatic execution handling. Can target the entire page or work with element handles for precise DOM manipulation. Examples: - Simple expression: "document.title" - Arrow function: "() => document.querySelectorAll('a').length" - Regular function: "function() { return document.body.children.length; }" - Function with arguments: "(tag) => document.getElementsByTagName(tag).length" - Complex code: "() => { const divs = document.querySelectorAll('div'); return { count: divs.length, hasClass: divs[0]?.className }; }" - Async function: "async () => { const res = await fetch('/api'); return res.json(); }"`, { function: z.string().describe(`JavaScript code to execute (expression or function string). Examples: - Expression: "document.title" - Arrow function: "() => document.querySelectorAll('a').length" - Regular function: "function() { return document.body.children.length; }" - With arguments: "(tag) => document.getElementsByTagName(tag).length" - Async function: "async () => { const res = await fetch('/api'); return res.json(); }"`), args: z.any().optional().describe(`Optional arguments to pass to the JavaScript function. Can be primitives, objects, arrays, or element references. Example: { url: "https://example.com", count: 5 }`), element: z.string().optional().describe('CSS selector to target specific element for evaluation'), contextId: z.string().optional().describe('Browser ID to execute on (uses most recent browser if not provided)'), timeout: z.number().optional().describe('Execution timeout in milliseconds (default: 30000)'), returnType: z.enum(['auto', 'json', 'string', 'number', 'boolean']).optional() .describe('Expected return type for better serialization (default: auto)'), }, async ({ function: jsFunction, args, element, contextId, timeout = 30000, returnType = 'auto' }) => { try { // 1. Browser status validation const browserStatus = getContextForOperation(contextId); if (!browserStatus.isStarted) { return browserStatus.error; } // 2. JavaScript execution relies on browser context isolation for security // 3. Get current checkpoint ID const checkpointId = await getCurrentCheckpointId(browserStatus.page); // 4. Execute JavaScript with optional element targeting let result: any; const startTime = Date.now(); if (element) { // Element-targeted evaluation try { await browserStatus.page.waitForSelector(element, { timeout: 5000 }); const elementHandle = await browserStatus.page.$(element); if (!elementHandle) { return { content: [ { type: 'text', text: `Element with selector "${element}" not found` } ], isError: true }; } // Execute JavaScript on element - Playwright handles both expressions and functions result = args !== undefined ? await Promise.race([ elementHandle.evaluate(jsFunction, args), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]) : await Promise.race([ elementHandle.evaluate(jsFunction), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); } catch (elementError) { const errorMessage = elementError instanceof Error ? elementError.message : String(elementError); Logger.error(`Element evaluation failed: ${errorMessage}`); return { content: [ { type: 'text', text: `Failed to evaluate on element "${element}": ${errorMessage}` } ], isError: true }; } } else { // Page-level evaluation try { // Debug: Check page state before evaluation const currentUrl = browserStatus.page.url(); const readyState = await browserStatus.page.evaluate(() => document.readyState); Logger.info(`Evaluating on page: ${currentUrl}, readyState: ${readyState}`); // Execute JavaScript with fallback for different input types Logger.info(`Executing JavaScript: ${jsFunction.substring(0, 100)}...`); try { // First try: direct evaluation (works for expressions and some functions) result = await Promise.race([ browserStatus.page.evaluate(jsFunction, args), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); // Check if result is undefined or a function (meaning it wasn't executed) Logger.info(`First try result: ${typeof result}, value: ${result}`); if (result === undefined || typeof result === 'function') { throw new Error('Function not executed, trying alternative method'); } } catch (directError) { // Second try: wrap function string with eval wrapper Logger.info('Direct evaluation failed, trying eval wrapper'); const wrapper = ({ fn, arg }: { fn: string, arg: any }) => { const func = eval(`(${fn})`); return func(arg); }; result = await Promise.race([ browserStatus.page.evaluate(wrapper, { fn: jsFunction, arg: args }), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); } Logger.info(`Raw result type: ${typeof result}, value: ${JSON.stringify(result)?.substring(0, 200) || 'undefined'}`); } catch (evalError) { Logger.error(`Page evaluation error: ${evalError}`); throw evalError; } } const executionTime = Date.now() - startTime; // 5. Serialize result based on returnType const serializedResult = serializeResult(result, returnType); // 6. Build response const resultMessage = { result: serializedResult, executionTime, checkpointId, element: element || null, returnType, functionCode: jsFunction && jsFunction.length > 200 ? jsFunction.substring(0, 200) + '...' : jsFunction }; return { content: [ { type: 'text', text: JSON.stringify(resultMessage, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); Logger.error(`Failed to evaluate JavaScript: ${errorMessage}`); return { content: [ { type: 'text', text: `Failed to evaluate JavaScript: ${errorMessage}` } ], isError: true }; } } );
  • Main execution handler for browser-evaluate tool. Validates browser context, executes JavaScript via Playwright's evaluate methods (with element targeting support and fallback eval wrapper), serializes results, and returns structured response with execution metadata.
    async ({ function: jsFunction, args, element, contextId, timeout = 30000, returnType = 'auto' }) => { try { // 1. Browser status validation const browserStatus = getContextForOperation(contextId); if (!browserStatus.isStarted) { return browserStatus.error; } // 2. JavaScript execution relies on browser context isolation for security // 3. Get current checkpoint ID const checkpointId = await getCurrentCheckpointId(browserStatus.page); // 4. Execute JavaScript with optional element targeting let result: any; const startTime = Date.now(); if (element) { // Element-targeted evaluation try { await browserStatus.page.waitForSelector(element, { timeout: 5000 }); const elementHandle = await browserStatus.page.$(element); if (!elementHandle) { return { content: [ { type: 'text', text: `Element with selector "${element}" not found` } ], isError: true }; } // Execute JavaScript on element - Playwright handles both expressions and functions result = args !== undefined ? await Promise.race([ elementHandle.evaluate(jsFunction, args), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]) : await Promise.race([ elementHandle.evaluate(jsFunction), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); } catch (elementError) { const errorMessage = elementError instanceof Error ? elementError.message : String(elementError); Logger.error(`Element evaluation failed: ${errorMessage}`); return { content: [ { type: 'text', text: `Failed to evaluate on element "${element}": ${errorMessage}` } ], isError: true }; } } else { // Page-level evaluation try { // Debug: Check page state before evaluation const currentUrl = browserStatus.page.url(); const readyState = await browserStatus.page.evaluate(() => document.readyState); Logger.info(`Evaluating on page: ${currentUrl}, readyState: ${readyState}`); // Execute JavaScript with fallback for different input types Logger.info(`Executing JavaScript: ${jsFunction.substring(0, 100)}...`); try { // First try: direct evaluation (works for expressions and some functions) result = await Promise.race([ browserStatus.page.evaluate(jsFunction, args), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); // Check if result is undefined or a function (meaning it wasn't executed) Logger.info(`First try result: ${typeof result}, value: ${result}`); if (result === undefined || typeof result === 'function') { throw new Error('Function not executed, trying alternative method'); } } catch (directError) { // Second try: wrap function string with eval wrapper Logger.info('Direct evaluation failed, trying eval wrapper'); const wrapper = ({ fn, arg }: { fn: string, arg: any }) => { const func = eval(`(${fn})`); return func(arg); }; result = await Promise.race([ browserStatus.page.evaluate(wrapper, { fn: jsFunction, arg: args }), new Promise((_, reject) => setTimeout(() => reject(new Error('Execution timeout')), timeout)) ]); } Logger.info(`Raw result type: ${typeof result}, value: ${JSON.stringify(result)?.substring(0, 200) || 'undefined'}`); } catch (evalError) { Logger.error(`Page evaluation error: ${evalError}`); throw evalError; } } const executionTime = Date.now() - startTime; // 5. Serialize result based on returnType const serializedResult = serializeResult(result, returnType); // 6. Build response const resultMessage = { result: serializedResult, executionTime, checkpointId, element: element || null, returnType, functionCode: jsFunction && jsFunction.length > 200 ? jsFunction.substring(0, 200) + '...' : jsFunction }; return { content: [ { type: 'text', text: JSON.stringify(resultMessage, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); Logger.error(`Failed to evaluate JavaScript: ${errorMessage}`); return { content: [ { type: 'text', text: `Failed to evaluate JavaScript: ${errorMessage}` } ], isError: true }; } }
  • Zod input schema defining parameters for browser-evaluate: JavaScript function/code to execute, optional args, element selector, contextId, timeout, and returnType.
    function: z.string().describe(`JavaScript code to execute (expression or function string). Examples: - Expression: "document.title" - Arrow function: "() => document.querySelectorAll('a').length" - Regular function: "function() { return document.body.children.length; }" - With arguments: "(tag) => document.getElementsByTagName(tag).length" - Async function: "async () => { const res = await fetch('/api'); return res.json(); }"`), args: z.any().optional().describe(`Optional arguments to pass to the JavaScript function. Can be primitives, objects, arrays, or element references. Example: { url: "https://example.com", count: 5 }`), element: z.string().optional().describe('CSS selector to target specific element for evaluation'), contextId: z.string().optional().describe('Browser ID to execute on (uses most recent browser if not provided)'), timeout: z.number().optional().describe('Execution timeout in milliseconds (default: 30000)'), returnType: z.enum(['auto', 'json', 'string', 'number', 'boolean']).optional() .describe('Expected return type for better serialization (default: auto)'), },
  • Helper function serializeResult used by the handler to serialize evaluation results based on specified returnType (auto, json, string, number, boolean).
    const serializeResult = (result: any, returnType: string): any => { try { switch (returnType) { case 'string': return String(result); case 'number': return Number(result); case 'boolean': return Boolean(result); case 'json': return JSON.stringify(result, null, 2); case 'auto': default: // Auto-detect and return serializable result return JSON.parse(JSON.stringify(result)); } } catch (error) { Logger.warn('Result serialization failed, returning string representation'); return String(result); } };
  • Helper getContextForOperation retrieves and validates the browser page context (by ID or most recent), returning status with page or error.
    const getContextForOperation = (contextId?: string): BrowserStatus => { let contextInstance; if (contextId) { contextInstance = contextManager.getContext(contextId); if (!contextInstance) { return { isStarted: false, error: { content: [ { type: 'text', text: `Browser '${contextId}' not found. Use 'list-browsers' to see available browsers or 'start-browser' to create one.` } ], isError: true } }; } } else { contextInstance = contextManager.getMostRecentContext(); if (!contextInstance) { return { isStarted: false, error: { content: [ { type: 'text', text: 'No active browsers found. Use \'start-browser\' to create a browser first.' } ], isError: true } }; } } // Note: contextInstance.page is now always defined (never null) return { isStarted: true, page: contextInstance.page }; };

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/ESnark/blowback'

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