Skip to main content
Glama

execute_background_script

Execute server-side JavaScript in ServiceNow using Background Scripts to run custom code, automate processes, and interact with instance data within specified application scopes.

Instructions

Execute server-side JavaScript in ServiceNow using Background Scripts. ⚠️ SANDBOX ONLY - executes arbitrary code. 🛡️ Auto-truncates large outputs. 📁 Use {{file:path}} for large scripts.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scriptYesThe server-side JavaScript code to execute (e.g., gs.print("Hello");). Maximum 50,000 characters. Supports {{file:...}} placeholders to load content from local files.
scopeYesThe application scope (e.g., "global" or specific app scope). Required.
timeout_msNoOptional timeout in milliseconds (default: 60000, range: 1000-300000)
include_htmlNoInclude HTML output in response (default: true). Set to false for text-only mode to reduce response size.
response_modeNoResponse verbosity: full (all data), minimal (essential only), compact (summarized). Default: full

Implementation Reference

  • MCP CallToolRequest handler specifically for 'execute_background_script': initializes client check, extracts and validates parameters (script, scope, etc.), resolves file placeholders, executes via client.executeScript, applies overflow prevention, formats JSON response, handles all errors.
    if (request.params.name === 'execute_background_script') { try { // Check if client was initialized successfully if (!client) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: { code: 'INITIALIZATION_ERROR', message: 'ServiceNow client failed to initialize', details: initError instanceof Error ? initError.message : 'Unknown error', }, } as ServiceNowErrorResponse), }, ], isError: true, }; } // Extract parameters from tool arguments const args = request.params.arguments as Record<string, unknown> | undefined; const script = args?.script as string | undefined; const scope = args?.scope as string | undefined; const timeoutMs = args?.timeout_ms as number | undefined; const includeHtml = args?.include_html as boolean | undefined; const responseMode = args?.response_mode as string | undefined; // Validate required parameters if (!script) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: { code: ERROR_CODES.MISSING_PARAMETER, message: 'Required parameter "script" is missing', details: 'Please provide the JavaScript code to execute', }, } as ServiceNowErrorResponse), }, ], isError: true, }; } if (!scope) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: { code: ERROR_CODES.MISSING_PARAMETER, message: 'Required parameter "scope" is missing', details: 'Please provide the application scope (e.g., "global")', }, } as ServiceNowErrorResponse), }, ], isError: true, }; } // Resolve file placeholders in the request let resolvedScript = script; let resolvedScope = scope; try { const scriptResolution = resolveFilePlaceholders(script); resolvedScript = scriptResolution.data; if (scope) { const scopeResolution = resolveFilePlaceholders(scope); resolvedScope = scopeResolution.data; } } catch (error) { if (error instanceof FilePlaceholderError) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: { code: 'FILE_PLACEHOLDER_ERROR', message: 'Failed to resolve file placeholder', details: `${error.placeholder}: ${error.message}`, }, } as ServiceNowErrorResponse), }, ], isError: true, }; } // Re-throw unknown errors throw error; } // Execute the script const result = await client.executeScript({ script: resolvedScript, scope: resolvedScope, timeoutMs, include_html: includeHtml, response_mode: responseMode as any, }); // Apply global context overflow prevention const { response: protectedResult, monitoring } = globalContextOverflowPrevention.monitorResponse(result, 'execute_background_script', responseMode); return { content: [ { type: 'text', text: JSON.stringify(protectedResult, null, 2), }, ], }; } catch (error) { const errorResponse: ServiceNowErrorResponse = { success: false, error: { code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred', }, }; if (error instanceof ServiceNowScriptError) { errorResponse.error.code = error.code; errorResponse.error.message = error.message; errorResponse.error.details = `HTTP Status: ${error.statusCode || 'N/A'}`; } else if (error instanceof Error) { errorResponse.error.message = error.message; errorResponse.error.details = 'Check ServiceNow instance connectivity and credentials'; } return { content: [ { type: 'text', text: JSON.stringify(errorResponse), }, ], isError: true, }; } }
  • Tool schema definition in ListToolsRequest handler: specifies name, detailed description, and inputSchema with properties, types, descriptions, and required fields for the execute_background_script tool.
    name: 'execute_background_script', description: 'Execute server-side JavaScript in ServiceNow using Background Scripts. ⚠️ SANDBOX ONLY - executes arbitrary code. 🛡️ Auto-truncates large outputs. 📁 Use {{file:path}} for large scripts.', inputSchema: { type: 'object', properties: { script: { type: 'string', description: 'The server-side JavaScript code to execute (e.g., gs.print("Hello");). Maximum 50,000 characters. Supports {{file:...}} placeholders to load content from local files.', }, scope: { type: 'string', description: 'The application scope (e.g., "global" or specific app scope). Required.', }, timeout_ms: { type: 'number', description: 'Optional timeout in milliseconds (default: 60000, range: 1000-300000)', }, include_html: { type: 'boolean', description: 'Include HTML output in response (default: true). Set to false for text-only mode to reduce response size.', }, response_mode: { type: 'string', enum: ['full', 'minimal', 'compact'], description: 'Response verbosity: full (all data), minimal (essential only), compact (summarized). Default: full', }, }, required: ['script', 'scope'], }, },
  • src/index.ts:583-790 (registration)
    Registration of all MCP tools via setRequestHandler(ListToolsRequestSchema), listing 'execute_background_script' with its schema and description.
    server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'execute_background_script', description: 'Execute server-side JavaScript in ServiceNow using Background Scripts. ⚠️ SANDBOX ONLY - executes arbitrary code. 🛡️ Auto-truncates large outputs. 📁 Use {{file:path}} for large scripts.', inputSchema: { type: 'object', properties: { script: { type: 'string', description: 'The server-side JavaScript code to execute (e.g., gs.print("Hello");). Maximum 50,000 characters. Supports {{file:...}} placeholders to load content from local files.', }, scope: { type: 'string', description: 'The application scope (e.g., "global" or specific app scope). Required.', }, timeout_ms: { type: 'number', description: 'Optional timeout in milliseconds (default: 60000, range: 1000-300000)', }, include_html: { type: 'boolean', description: 'Include HTML output in response (default: true). Set to false for text-only mode to reduce response size.', }, response_mode: { type: 'string', enum: ['full', 'minimal', 'compact'], description: 'Response verbosity: full (all data), minimal (essential only), compact (summarized). Default: full', }, }, required: ['script', 'scope'], }, }, { name: 'execute_table_operation', description: 'CRUD operations on ServiceNow tables via Table API. Supports GET/POST/PUT/PATCH/DELETE with query syntax and batch operations. ⚠️ SANDBOX ONLY - reads/modifies data. 🛡️ Auto-limits large results. Use pagination for big datasets. 📁 Use {{file:path}} for large data.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], description: 'The operation to perform on the table. Required.', }, table: { type: 'string', description: 'The ServiceNow table name (e.g., "incident", "sys_user"). Required.', }, sys_id: { type: 'string', description: 'System ID for single record operations (GET, PUT, PATCH, DELETE).', }, sys_ids: { type: 'array', items: { type: 'string' }, description: 'Array of system IDs for batch operations.', }, query: { type: 'string', description: 'ServiceNow encoded query string (e.g., "active=true^priority=1").', }, fields: { type: 'string', description: 'Comma-separated list of fields to return.', }, limit: { type: 'number', description: 'Maximum number of records to return (default: 1000).', }, offset: { type: 'number', description: 'Number of records to skip for pagination.', }, display_value: { type: 'string', enum: ['true', 'false', 'all'], description: 'Return display values for reference fields.', }, exclude_reference_link: { type: 'boolean', description: 'Exclude reference link fields from response.', }, data: { type: 'object', description: 'Record data for POST/PUT/PATCH operations. Can be single object or array for batch operations. Supports {{file:...}} placeholders to load content from local files.', }, batch: { type: 'boolean', description: 'Enable batch mode for multiple record operations.', }, validate_fields: { type: 'boolean', description: 'Enable field validation warnings to catch typos and invalid field names. Default: true (validation enabled by default).', }, context_overflow_prevention: { type: 'boolean', description: 'Enable context overflow prevention to limit large result sets. Default: true. Set to false to disable automatic truncation (use with caution).', }, strict_fields: { type: 'boolean', description: 'Strict field filtering - only return requested fields and strip large fields (script, html, css) unless explicitly requested. Default: false.', }, response_mode: { type: 'string', enum: ['full', 'minimal', 'compact'], description: 'Response verbosity: full (all data), minimal (essential only), compact (summarized). Default: full', }, }, required: ['operation', 'table'], }, }, { name: 'execute_updateset_operation', description: 'Manage ServiceNow update sets with lifecycle operations, XML reassignment, and working set tracking. ⚠️ SANDBOX ONLY - modifies update sets. 🛡️ Auto-limits large results. Use pagination for big datasets. 📁 Use {{file:path}} for large data.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['create', 'set_working', 'show_working', 'clear_working', 'insert', 'update', 'rehome', 'contents', 'recent', 'list', 'info', 'complete', 'reopen', 'delete', 'diff_default'], description: 'The update set operation to perform. Required.', }, name: { type: 'string', description: 'Update set name (required for create operation).', }, description: { type: 'string', description: 'Update set description (optional for create operation).', }, scope: { type: 'string', description: 'Update set scope (optional, defaults to configured scope).', }, set_as_working: { type: 'boolean', description: 'Set the created update set as working set (for create operation).', }, update_set_sys_id: { type: 'string', description: 'Update set sys_id for operations that require it.', }, table: { type: 'string', description: 'Table name for insert/update operations.', }, sys_id: { type: 'string', description: 'Record sys_id for update operations.', }, data: { type: 'object', description: 'Record data for insert/update operations. Can be single object or array for batch operations. Supports {{file:...}} placeholders to load content from local files.', }, batch: { type: 'boolean', description: 'Enable batch mode for multiple record operations.', }, xml_sys_ids: { type: 'array', items: { type: 'string' }, description: 'Array of XML sys_ids for rehome operations.', }, query: { type: 'string', description: 'ServiceNow encoded query string for rehome operations.', }, force: { type: 'boolean', description: 'Force reassignment even if XML is not in Default update set.', }, limit: { type: 'number', description: 'Maximum number of records to return for list/recent operations.', }, offset: { type: 'number', description: 'Number of records to skip for pagination.', }, filters: { type: 'object', description: 'Filters for list operations (scope, state, created_by, sys_created_on).', }, response_mode: { type: 'string', enum: ['full', 'minimal', 'compact'], description: 'Response verbosity: full (all data), minimal (essential only), compact (summarized). Default: full', }, quiet: { type: 'boolean', description: 'Compact acknowledgment for update operations to avoid RESPONSE_TOO_LARGE errors. Default: false.', }, }, required: ['operation'], }, }, ], }; });
  • Core helper method ServiceNowBackgroundScriptClient.executeScript(): performs full flow of authenticating UI session, fetching/parsing background script form, posting script execution with retry on session timeout, parsing HTML/text output, returning structured ScriptExecutionResult.
    async executeScript(request: ScriptExecutionRequest): Promise<ScriptExecutionResult> { const startTime = Date.now(); try { // Validate input this.validateScriptRequest(request); // Step 1: Establish UI session via form login await this.establishUISession(); // Step 2: GET the background script page and parse form data const pageResponse = await this.getBackgroundScriptPage(); const { actionUrl, formData } = this.parseFormData(pageResponse.html, request.script, request.scope); // Step 3: POST the script execution request with retry logic let executionResponse = await this.postScriptExecution({ actionUrl, formData, timeoutMs: request.timeoutMs || this.clientConfig.timeoutMs, }); // Log telemetry (expert specification) const cookieNames = this.getCookieHeader(actionUrl).split(';').map(c => c.split('=')[0]).filter(c => c).join(','); // Check for session timeout and retry if needed let retry = false; if (this.hasSessionTimeout(executionResponse.body)) { retry = true; // Get fresh page and parse form data const freshPageResponse = await this.getBackgroundScriptPage(); const { actionUrl: freshActionUrl, formData: freshFormData } = this.parseFormData(freshPageResponse.html, request.script, request.scope); // Retry POST with fresh form data executionResponse = await this.postScriptExecution({ actionUrl: freshActionUrl, formData: freshFormData, timeoutMs: request.timeoutMs || this.clientConfig.timeoutMs, }); } // Step 3: Parse the response const parsedOutput = parseScriptOutput(executionResponse.body); const executionTime = Date.now() - startTime; // Log pre blocks telemetry const preBlocks = (executionResponse.body.match(/<pre[^>]*>/gi) || []).length; // Handle response_mode shortcuts and include_html parameter for text-only mode const shouldIncludeHtml = request.include_html !== false && request.response_mode !== 'minimal'; if (!shouldIncludeHtml) { return { success: true, output: { text: parsedOutput.text, }, metadata: { executionTime, htmlSize: 0, timestamp: new Date().toISOString(), scope: request.scope, }, }; } return { success: true, output: { html: parsedOutput.html, text: parsedOutput.text, }, metadata: { executionTime, htmlSize: parsedOutput.html.length, timestamp: new Date().toISOString(), scope: request.scope, }, }; } catch (error) { if (error instanceof ServiceNowScriptError) { throw error; } throw new ServiceNowScriptError( ERROR_CODES.SCRIPT_EXECUTION_ERROR, undefined, `Script execution failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } }

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/ClearSkye/SkyeNet-MCP-ACE'

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