Skip to main content
Glama
omgwtfwow

MCP Server for Crawl4AI

by omgwtfwow

execute_js

Execute JavaScript on web pages to extract data, trigger dynamic content, and check page state, returning values directly from scripts with return statements.

Instructions

[STATELESS] Execute JavaScript and get return values + page content. Creates new browser each time. Use for: extracting data, triggering dynamic content, checking page state. Scripts with "return" statements return actual values (strings, numbers, objects, arrays). Note: null returns as {"success": true}. Returns values but page state is lost. For persistent JS execution, use crawl with session_id.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesThe URL to load
scriptsYesJavaScript to execute. Use "return" to get values back! Each string runs separately. Returns appear in results array. Examples: "return document.title", "return document.querySelectorAll('a').length", "return {url: location.href, links: [...document.links].map(a => a.href)}". Use proper JS syntax: real quotes, no HTML entities.

Implementation Reference

  • Core handler implementation for the 'execute_js' tool. Validates input, executes JS via Crawl4AI service, processes results from multiple scripts, formats output including return values and post-execution page markdown into MCP content blocks.
    async executeJS(options: JSExecuteEndpointOptions) {
      try {
        // Check if scripts is provided
        if (!options.scripts || options.scripts === null) {
          throw new Error(
            'scripts is required. Please provide JavaScript code to execute. Use "return" statements to get values back.',
          );
        }
    
        const result: JSExecuteEndpointResponse = await this.service.executeJS(options);
    
        // Extract JavaScript execution results
        const jsResults = result.js_execution_result?.results || [];
        // Ensure scripts is always an array for mapping
        const scripts = Array.isArray(options.scripts) ? options.scripts : [options.scripts];
    
        // Format results for display
        let formattedResults = '';
        if (jsResults.length > 0) {
          formattedResults = jsResults
            .map((res: unknown, idx: number) => {
              const script = scripts[idx] || 'Script ' + (idx + 1);
              // Handle the actual return value or success/error status
              let resultStr = '';
              if (res && typeof res === 'object' && 'success' in res) {
                // This is a status object (e.g., from null return or execution without return)
                const statusObj = res as { success: unknown; error?: unknown };
                resultStr = statusObj.success
                  ? 'Executed successfully (no return value)'
                  : `Error: ${statusObj.error || 'Unknown error'}`;
              } else {
                // This is an actual return value
                resultStr = JSON.stringify(res, null, 2);
              }
              return `Script: ${script}\nReturned: ${resultStr}`;
            })
            .join('\n\n');
        } else {
          formattedResults = 'No results returned';
        }
    
        // Handle markdown content - can be string or object
        let markdownContent = '';
        if (result.markdown) {
          if (typeof result.markdown === 'string') {
            markdownContent = result.markdown;
          } else if (typeof result.markdown === 'object' && result.markdown.raw_markdown) {
            // Use raw_markdown from the object structure
            markdownContent = result.markdown.raw_markdown;
          }
        }
    
        return {
          content: [
            {
              type: 'text',
              text: `JavaScript executed on: ${options.url}\n\nResults:\n${formattedResults}${markdownContent ? `\n\nPage Content After Execution:\n${markdownContent}` : ''}`,
            },
          ],
        };
      } catch (error) {
        throw this.formatError(error, 'execute JavaScript');
      }
    }
  • src/server.ts:842-845 (registration)
    Registration of the 'execute_js' tool in the MCP server request handler switch statement. Validates arguments using ExecuteJsSchema and delegates to utilityHandlers.executeJS.
    case 'execute_js':
      return await this.validateAndExecute('execute_js', args, ExecuteJsSchema, async (validatedArgs) =>
        this.utilityHandlers.executeJS(validatedArgs),
      );
  • Zod schema for validating 'execute_js' tool inputs: requires URL and scripts (string or array of JS code). Uses JsCodeSchema for JS validation.
    export const ExecuteJsSchema = createStatelessSchema(
      z.object({
        url: z.string().url(),
        scripts: JsCodeSchema,
      }),
      'execute_js',
    );
  • src/server.ts:191-210 (registration)
    Tool specification for 'execute_js' in the MCP listTools response, including name, detailed description, and inputSchema definition.
      name: 'execute_js',
      description:
        '[STATELESS] Execute JavaScript and get return values + page content. Creates new browser each time. Use for: extracting data, triggering dynamic content, checking page state. Scripts with "return" statements return actual values (strings, numbers, objects, arrays). Note: null returns as {"success": true}. Returns values but page state is lost. For persistent JS execution, use crawl with session_id.',
      inputSchema: {
        type: 'object',
        properties: {
          url: {
            type: 'string',
            description: 'The URL to load',
          },
          scripts: {
            type: ['string', 'array'],
            items: { type: 'string' },
            description:
              'JavaScript to execute. Use "return" to get values back! Each string runs separately. Returns appear in results array. Examples: "return document.title", "return document.querySelectorAll(\'a\').length", "return {url: location.href, links: [...document.links].map(a => a.href)}". Use proper JS syntax: real quotes, no HTML entities.',
          },
        },
        required: ['url', 'scripts'],
      },
    },
  • Supporting schema for JavaScript code validation used in ExecuteJsSchema. Validates single string or array, rejects invalid JS with HTML entities or malformed syntax.
    export const JsCodeSchema = z
      .union([
        z.string().refine(validateJavaScriptCode, {
          message:
            'Invalid JavaScript: Contains HTML entities ("), literal \\n outside strings, or HTML tags. Use proper JS syntax with real quotes and newlines.',
        }),
        z.array(
          z.string().refine(validateJavaScriptCode, {
            message:
              'Invalid JavaScript: Contains HTML entities ("), literal \\n outside strings, or HTML tags. Use proper JS syntax with real quotes and newlines.',
          }),
        ),
      ])
      .describe('JavaScript code as string or array of strings');

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/omgwtfwow/mcp-crawl4ai-ts'

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