AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Script Execution

skill-happy-technologies-llc-happy-platform-skills-script-execution · by Happy-Technologies-LLC

Safe background script execution patterns including automated execution via sys_trigger, fix script generation, error handling, and logging best practices

No reviews yet
0 installs
19 views
0.0% view→install

Install

$ agentstack add skill-happy-technologies-llc-happy-platform-skills-script-execution

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-happy-technologies-llc-happy-platform-skills-script-execution)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Script Execution? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Script Execution

Overview

This skill covers safe and effective execution of background scripts in ServiceNow:

  • Automated script execution via sys_trigger (the breakthrough method)
  • Safe script patterns and templates
  • Fix script generation for manual execution
  • Error handling and logging best practices
  • Debugging and troubleshooting techniques

When to use: When MCP tools don't support the required operation, or when complex data manipulation is needed.

Who should use this: Administrators and developers who need to execute server-side JavaScript in ServiceNow.

Prerequisites

  • Roles: admin (background script execution requires full admin)
  • Access: systrigger, syslog, sysscript_fix tables
  • Knowledge: GlideRecord API, ServiceNow server-side JavaScript
  • Caution: Background scripts run with elevated privileges - test carefully

Execution Methods

Method Comparison

| Method | Automation | Speed | Use Case | |--------|------------|-------|----------| | sys_trigger | Full | ~1-2 sec | Preferred for all automated tasks | | UI endpoint | Full | ~1-2 sec | Fallback if trigger fails | | Fix Script | Manual | Variable | Audit trail, scheduled execution | | Scripts - Background | Manual | Immediate | One-time interactive testing |

Procedure

Phase 1: Automated Execution (Recommended)

Step 1.1: Basic Script Execution

The sys_trigger method creates a scheduled job that executes immediately and self-deletes.

Using MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    gs.info('Hello from automated script execution!');

    // Your logic here
    var count = 0;
    var gr = new GlideRecord('incident');
    gr.addQuery('active', true);
    gr.addQuery('priority', 1);
    gr.setLimit(10);
    gr.query();

    while (gr.next()) {
      count++;
      gs.info('Found P1 incident: ' + gr.number);
    }

    gs.info('Total P1 incidents found: ' + count);
  description: Count active P1 incidents
  execution_method: trigger

How It Works:

  1. Creates a scheduled job in sys_trigger table
  2. Sets next_action to run immediately
  3. Trigger executes the script
  4. Script logs output to system logs
  5. Trigger auto-deletes after execution
Step 1.2: Verify Execution

Check System Logs:

Tool: SN-Query-Table
Parameters:
  table_name: syslog
  query: message=*automated script*^sys_created_on>javascript:gs.minutesAgo(5)
  fields: message,level,sys_created_on,source
  limit: 20

Log Levels: | Level | Method | Purpose | |-------|--------|---------| | 0 | gs.info() | Informational messages | | 1 | gs.warn() | Warning messages | | 2 | gs.error() | Error messages | | 3 | gs.debug() | Debug messages (if enabled) |

Phase 2: Safe Script Patterns

Step 2.1: Query Pattern (Read-Only)

Safe pattern for reading data:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Safe read-only pattern
    var results = [];

    var gr = new GlideRecord('incident');
    gr.addQuery('active', true);
    gr.addQuery('priority', 1);
    gr.setLimit(100);  // Always set a limit
    gr.query();

    while (gr.next()) {
      results.push({
        number: gr.number.toString(),
        short_description: gr.short_description.toString(),
        assigned_to: gr.assigned_to.getDisplayValue(),
        created: gr.sys_created_on.toString()
      });
    }

    gs.info('Query Results: ' + JSON.stringify(results, null, 2));
  description: Query active P1 incidents (read-only)
Step 2.2: Update Pattern (With Safety Checks)

Pattern for updating records with safety measures:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Safe update pattern with validation
    var TABLE = 'incident';
    var QUERY = 'active=true^priority=1^assigned_toISEMPTY';
    var MAX_UPDATES = 10;  // Limit updates per execution

    // DRY RUN flag - set to false to actually update
    var DRY_RUN = true;

    var updated = 0;
    var skipped = 0;

    var gr = new GlideRecord(TABLE);
    gr.addEncodedQuery(QUERY);
    gr.setLimit(MAX_UPDATES);
    gr.query();

    gs.info('Found ' + gr.getRowCount() + ' records to process');

    while (gr.next()) {
      // Validate before update
      if (!gr.canWrite()) {
        gs.warn('Cannot write to: ' + gr.number);
        skipped++;
        continue;
      }

      if (DRY_RUN) {
        gs.info('[DRY RUN] Would update: ' + gr.number);
      } else {
        gr.work_notes = 'Automated: Escalating unassigned P1';
        gr.assignment_group = 'Critical Incidents Team';
        gr.update();
        gs.info('Updated: ' + gr.number);
        updated++;
      }
    }

    gs.info('Summary: Updated=' + updated + ', Skipped=' + skipped + ', DryRun=' + DRY_RUN);
  description: Escalate unassigned P1 incidents (with dry run)
Step 2.3: Insert Pattern (With Duplicate Check)

Pattern for creating records safely:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Safe insert pattern with duplicate check
    var TABLE = 'sys_properties';
    var PROPERTY_NAME = 'custom.automation.enabled';
    var PROPERTY_VALUE = 'true';

    // Check for existing
    var existing = new GlideRecord(TABLE);
    existing.addQuery('name', PROPERTY_NAME);
    existing.query();

    if (existing.next()) {
      gs.warn('Property already exists: ' + PROPERTY_NAME + ' = ' + existing.value);

      // Update if different
      if (existing.value != PROPERTY_VALUE) {
        existing.value = PROPERTY_VALUE;
        existing.update();
        gs.info('Updated property value to: ' + PROPERTY_VALUE);
      }
    } else {
      // Create new
      var gr = new GlideRecord(TABLE);
      gr.initialize();
      gr.name = PROPERTY_NAME;
      gr.value = PROPERTY_VALUE;
      gr.description = 'Created by automated script';
      var sysId = gr.insert();
      gs.info('Created property: ' + PROPERTY_NAME + ' (sys_id: ' + sysId + ')');
    }
  description: Create or update system property
Step 2.4: Delete Pattern (With Confirmation)

Pattern for deleting records with extreme caution:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // DANGEROUS: Delete pattern - use with extreme caution
    var TABLE = 'sys_user_preference';
    var QUERY = 'user.active=false^sys_created_on= 0; i--) {
        var change = changes[i];
        var rollback = new GlideRecord(change.table);
        rollback.get(change.sys_id);
        rollback[change.field] = change.old;
        rollback.update();
        gs.info('Rolled back: ' + change.table + '.' + change.sys_id);
      }

      gs.info('Rollback complete');
    }
  description: Multi-step update with rollback capability

Phase 5: Logging Best Practices

Step 5.1: Structured Logging

Use consistent log formatting:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Structured logging pattern
    var Logger = {
      scriptName: 'DataMigration',

      info: function(message, data) {
        var logMessage = '[' + this.scriptName + '] [INFO] ' + message;
        if (data) logMessage += ' | Data: ' + JSON.stringify(data);
        gs.info(logMessage);
      },

      warn: function(message, data) {
        var logMessage = '[' + this.scriptName + '] [WARN] ' + message;
        if (data) logMessage += ' | Data: ' + JSON.stringify(data);
        gs.warn(logMessage);
      },

      error: function(message, error) {
        var logMessage = '[' + this.scriptName + '] [ERROR] ' + message;
        if (error) logMessage += ' | Error: ' + error.message;
        gs.error(logMessage);
      },

      metric: function(name, value) {
        gs.info('[' + this.scriptName + '] [METRIC] ' + name + '=' + value);
      }
    };

    // Usage
    Logger.info('Starting migration', { source: 'incident', target: 'x_custom_incident' });
    Logger.metric('records_processed', 150);
    Logger.warn('Skipped record due to missing field', { number: 'INC0012345' });
    Logger.error('Failed to process record', new Error('Invalid reference'));
  description: Demonstrate structured logging pattern
Step 5.2: Query Script Logs

Find Script Execution Logs:

Tool: SN-Query-Table
Parameters:
  table_name: syslog
  query: source=Script execution^sys_created_on>javascript:gs.hoursAgo(1)
  fields: message,level,sys_created_on
  limit: 100

Filter by Script Name:

Tool: SN-Query-Table
Parameters:
  table_name: syslog
  query: messageLIKE[DataMigration]^sys_created_on>javascript:gs.hoursAgo(1)
  fields: message,level,sys_created_on
  limit: 100

Phase 6: Advanced Patterns

Step 6.1: Batch Processing with Progress

For long-running operations:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Batch processing with progress tracking
    var BATCH_SIZE = 100;
    var MAX_BATCHES = 10;
    var TABLE = 'incident';
    var QUERY = 'active=true';

    var totalProcessed = 0;
    var totalRecords = 0;
    var batchNum = 0;

    // Get total count
    var countGR = new GlideAggregate(TABLE);
    countGR.addEncodedQuery(QUERY);
    countGR.addAggregate('COUNT');
    countGR.query();
    if (countGR.next()) {
      totalRecords = parseInt(countGR.getAggregate('COUNT'));
    }
    gs.info('Total records to process: ' + totalRecords);

    // Process in batches
    while (batchNum ', tracker);
    }
    gr.orderBy('sys_created_on');
    gr.setLimit(RECORDS_PER_EXECUTION);
    gr.query();

    var lastProcessed = '';
    var count = 0;

    while (gr.next()) {
      // Process record
      count++;
      lastProcessed = gr.sys_created_on.toString();
    }

    if (count > 0) {
      // Update tracker
      gs.setProperty('x_custom.migration.last_processed', lastProcessed);
      gs.info('Processed ' + count + ' records. Last: ' + lastProcessed);

      // Schedule next execution
      if (count == RECORDS_PER_EXECUTION) {
        var trigger = new GlideRecord('sys_trigger');
        trigger.initialize();
        trigger.name = 'Continue Migration ' + new GlideDateTime().getDisplayValue();
        trigger.next_action = new GlideDateTime();
        trigger.next_action.addSeconds(DELAY_SECONDS);
        trigger.script = 'gs.include("MigrationScript");';  // Call script include
        trigger.trigger_type = 0;  // Run once
        trigger.insert();
        gs.info('Scheduled next execution in ' + DELAY_SECONDS + ' seconds');
      } else {
        gs.info('Migration complete!');
        gs.setProperty('x_custom.migration.last_processed', '');  // Reset
      }
    } else {
      gs.info('No more records to process');
    }
  description: Self-scheduling migration script

Script Templates

Template 1: Data Audit

// Data Audit Template
var TABLE = 'incident';
var AUDIT_FIELDS = ['state', 'priority', 'assigned_to'];

var audit = {};
AUDIT_FIELDS.forEach(function(field) {
  audit[field] = {};
});

var gr = new GlideRecord(TABLE);
gr.addQuery('active', true);
gr.query();

while (gr.next()) {
  AUDIT_FIELDS.forEach(function(field) {
    var value = gr.getDisplayValue(field) || '(empty)';
    audit[field][value] = (audit[field][value] || 0) + 1;
  });
}

gs.info('Audit Results:\n' + JSON.stringify(audit, null, 2));

Template 2: Reference Data Validation

// Reference Validation Template
var TABLE = 'incident';
var REF_FIELD = 'assigned_to';
var REF_TABLE = 'sys_user';

var invalid = [];
var gr = new GlideRecord(TABLE);
gr.addQuery('active', true);
gr.addQuery(REF_FIELD + '.active', false);  // Reference to inactive record
gr.query();

while (gr.next()) {
  invalid.push({
    number: gr.number.toString(),
    invalid_ref: gr.getDisplayValue(REF_FIELD)
  });
}

gs.info('Found ' + invalid.length + ' records with invalid references');
gs.info(JSON.stringify(invalid, null, 2));

Template 3: Bulk Field Update

// Bulk Field Update Template
var TABLE = 'incident';
var QUERY = 'active=true^category=inquiry';
var UPDATES = {
  subcategory: 'general',
  contact_type: 'email'
};
var DRY_RUN = true;

var gr = new GlideRecord(TABLE);
gr.addEncodedQuery(QUERY);
gr.query();

var updated = 0;
while (gr.next()) {
  if (DRY_RUN) {
    gs.info('[DRY RUN] Would update ' + gr.number);
  } else {
    for (var field in UPDATES) {
      gr[field] = UPDATES[field];
    }
    gr.update();
    updated++;
  }
}

gs.info((DRY_RUN ? '[DRY RUN] ' : '') + 'Updated ' + updated + ' records');

Tool Usage Summary

| Operation | MCP Tool | Purpose | |-----------|----------|---------| | Execute | SN-Execute-Background-Script | Run server-side JavaScript | | Fix Script | SN-Create-Fix-Script | Create auditable script | | Query Logs | SN-Query-Table | Check execution results | | Create Record | SN-Create-Record | Manual fix script creation |

Best Practices

  • Always Use Dry Run First: Test with DRY_RUN = true before real execution
  • Set Limits: Always use setLimit() to prevent runaway scripts
  • Log Extensively: Use structured logging for debugging
  • Handle Errors: Wrap all scripts in try-catch
  • Document Scripts: Include author, date, ticket reference in comments
  • Avoid Hardcoding: Use variables for sys_ids and queries
  • Test in Sub-Production: Never run untested scripts in production
  • Use Transactions: Consider rollback patterns for multi-step operations

Troubleshooting

Script Doesn't Execute

Symptom: No output in system logs Causes:

  • sys_trigger permissions
  • Script syntax error before any logging

Solution:

Tool: SN-Query-Table
Parameters:
  table_name: sys_trigger
  query: nameLIKEMCP^sys_created_on>javascript:gs.minutesAgo(5)
  fields: name,state,next_action,script

Script Times Out

Symptom: Partial execution, timeout error Causes:

  • Too many records processed
  • Inefficient queries

Solution:

  • Add setLimit()
  • Use batch processing pattern
  • Optimize queries with proper indexes

No Logs Appearing

Symptom: Script runs but no gs.info() output visible Causes:

  • Log level filtering
  • Looking in wrong time range

Solution:

Tool: SN-Query-Table
Parameters:
  table_name: syslog
  query: sys_created_on>javascript:gs.minutesAgo(10)
  fields: message,level,source,sys_created_on
  limit: 50

Related Skills

  • admin/batch-operations - Bulk record operations
  • admin/update-set-management - Track script changes
  • admin/deployment-workflow - Deploy scripts between instances

References

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.