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 };
    };
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions 'automatic execution handling' and 'returns the result' which covers basic behavior, but doesn't address important aspects like error handling, security implications, performance characteristics, or what happens when JavaScript execution fails. The examples help but don't constitute comprehensive behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear purpose statement followed by supporting details and comprehensive examples. While the examples section is lengthy, each example serves a distinct purpose demonstrating different use cases. The description is front-loaded with the core functionality, making it efficient for understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex JavaScript evaluation tool with 6 parameters and no output schema, the description provides good examples but lacks information about return value formats, error conditions, and browser context requirements. The absence of annotations means the description should cover more behavioral aspects, particularly for a tool that executes arbitrary JavaScript in a browser environment.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the schema already documents all 6 parameters thoroughly. The description doesn't add meaningful parameter semantics beyond what's in the schema - it mentions targeting options but doesn't explain parameter interactions or provide guidance beyond the schema's examples. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Evaluates JavaScript code directly in the browser context and returns the result.' It specifies the verb ('evaluates'), resource ('JavaScript code'), and context ('browser context'), distinguishing it from sibling tools like capture-screenshot or get-console-logs that perform different browser operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage through examples showing different JavaScript evaluation scenarios, but doesn't explicitly state when to use this tool versus alternatives like execute-browser-commands or get-element-properties. It mentions targeting 'the entire page or work with element handles' which provides some context, but lacks explicit guidance on tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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