AI Agent Support
Use AI coding assistants like Cursor, GitHub Copilot, and Windsurf with sn-scriptsync for automated ServiceNow development.
Last updated: January 24, 2026
Last updated: January 24, 2026
Version 4.0 of sn-scriptsync brings native AI coding assistant support. Changes made by AI tools are automatically detected and synced to your ServiceNow instance.
sn-scriptsync works with any tool that modifies files on disk:
syncDelay setting controls sync frequency (default: 30 seconds, set to 0 to disable)Manual saves (Ctrl+S / Cmd+S) always sync immediately, bypassing the queue.
A tree view panel shows files waiting to sync:
When you start sn-scriptsync, an agentinstructions.md file is written to your workspace root. It contains guidelines for AI agents to work effectively with sn-scriptsync — file-structure patterns, Agent API documentation, and ServiceNow best practices.
Recommended: import or reference it instead of renaming. Keeping agentinstructions.md as the single source of truth lets the extension keep it up to date automatically when you upgrade. Point your tool's own rules file at it:
.cursorrules (or a file under .cursor/rules/) add: Follow the rules in @agentinstructions.mdCLAUDE.md add: @agentinstructions.md.github/copilot-instructions.md (Copilot has no import; see auto-refresh below).windsurfrules (no import; see auto-refresh below)agentinstructions.md and the on-demand skills under agentrules/skills/ are regenerated by the extension on every start and re-synced whenever their version is newer than the copy in your workspace. To pull the latest, update the sn-scriptsync extension and reload VS Code (or run ServiceNow ScriptSync: Enable). Renamed or removed skills are cleaned up automatically, and any file you authored yourself is never touched.
If you maintain your own CLAUDE.md / AGENTS.md / .cursorrules and don't want the managed reference block added to them, set sn-scriptsync.agentInstructions.autoUpdate to false. The extension then stops injecting and refreshing that block inside your files — but still keeps agentinstructions.md and agentrules/skills/ current, so you can reference them on demand yourself (for example @agentinstructions.md, or point your agent at a specific agentrules/skills/<name>/SKILL.md).
AI agents can create new ServiceNow artifacts by creating files in the correct folder structure:
{instance}/{scope}/{table}/{name}.{field}.{extension}
For example, to create a new Script Include:
myinstance/global/sys_script_include/MyNewUtils.script.js
The extension will automatically detect the new file, create the record in ServiceNow, and update _map.json with the new sys_id.
For advanced integrations, sn-scriptsync exposes a programmatic Agent API so AI tools can drive ServiceNow directly. There are two transports for the same set of commands:
When the extension starts it launches a local HTTP server bound to 127.0.0.1 on a random free port, secured with a per-session token. Send commands with a single POST /api call, passing the token in the X-Agent-Token header:
curl -s -X POST http://127.0.0.1:<port>/api \
-H "X-Agent-Token: <token>" \
-H "Content-Type: application/json" \
-d '{ "id": "chk_1", "command": "check_connection", "instance": "dev12345" }'
The extension publishes the live endpoint to .vscode/sn-agent-port.json in your workspace:
{
"port": 53123,
"token": "4f9a…hex…",
"pid": 68861,
"apiVersion": 2,
"startedAt": 1730000000000
}
Treat this file as a hint and validate it before trusting it — it can be stale after a crash, a machine move, or cloud-sync (iCloud/OneDrive/git). Do this every session and never cache the port/token:
port, token, and pid from .vscode/sn-agent-port.json.GET http://127.0.0.1:<port>/api/health (no auth required).health.pid matches the file's pid, and health.apiVersion is one you support.health.commands[] rather than hard-coding them.curl -s http://127.0.0.1:<port>/api/health
# → { "status": "success", "apiVersion": 2, "commands": [ … ], "pid": 68861 }
Every authenticated request is a POST /api with a JSON body containing a command, an optional id (auto-generated if omitted), an optional instance, and command-specific params:
{ "id": "upd_1", "command": "update_record", "instance": "dev12345",
"params": { "table": "sys_script_include", "sys_id": "…", "field": "script", "content": "gs.info('hi');" } }
Successful responses echo the id/command and carry a result:
{ "id": "upd_1", "command": "update_record", "status": "success",
"result": { "success": true, "table": "sys_script_include", "sys_id": "…", "field": "script" },
"timestamp": 1730000001235 }
Errors return status: "error" with a structured code:
{ "status": "error", "code": "E_BROWSER_DISCONNECTED", "error": "No browser helper tab is connected." }
| Code | HTTP | Meaning |
|---|---|---|
E_INVALID_PARAMS / E_INVALID_REQUEST | 400 | Malformed request or missing params |
E_UNAUTHORIZED | 401 | Missing/invalid X-Agent-Token |
E_UNKNOWN_COMMAND | 404 | No such command |
E_INSTANCE_REQUIRED / E_INSTANCE_NOT_FOUND | 422 | Instance not resolvable |
E_DISABLED | 423 | Feature disabled via settings |
E_SERVER_NOT_RUNNING / E_BROWSER_DISCONNECTED | 503 | Can't reach ServiceNow |
E_TIMEOUT | 504 | Round-trip exceeded the deadline |
E_ACL / E_TOKEN_EXPIRED | 502 | ServiceNow rejected the request |
E_INTERNAL | 500 | Unexpected error |
The same commands work over both transports. The authoritative list for your installed version is always health.commands[].
Connection & sync
| Command | Description |
|---|---|
check_connection | Verify the server is running and a browser tab is connected |
get_capabilities | Preflight what the helper tab can do right now — license tier, whether the browser debugger (CDP) is usable (cdp.available), and which write/create/delete permission gates are enabled |
get_instance_info | Resolved instance name and connection flags |
get_sync_status | Inspect the pending-file sync queue |
sync_now | Flush all pending files to ServiceNow immediately |
get_last_error | Read the last remote sync error |
clear_last_error | Clear the recorded error |
Records
| Command | Description |
|---|---|
create_artifact | Create a record from a fields payload (executes immediately) |
update_record | Update a single field on an existing record |
update_record_batch | Update multiple fields on one record in a single round-trip |
get_table_metadata | Fetch column metadata for a table (mandatory fields, types, references) |
check_name_exists_remote | Ask ServiceNow whether a record name already exists |
Query
| Command | Description |
|---|---|
query_records | Run an encoded query against any table |
get_parent_options | Fetch reference options (e.g. parent services for a REST endpoint) |
code_search | Run the SN Utils GraphQL field-index code search across script tables and return structured matches (Pro feature — needs an active SN Utils Pro/Trial/Enterprise license in the connected browser) |
Local files
| Command | Description |
|---|---|
list_tables | List table folders under the instance |
list_artifacts | List artifact files in a table folder |
check_name_exists | Look up a name in local _map.json files |
get_file_structure | Return the file-naming convention and code fields per table |
validate_path | Validate a proposed file path against the convention |
Browser
| Command | Description |
|---|---|
open_in_browser | Open a record/widget form in the connected browser |
activate_tab | Find a tab by URL pattern and activate (or open) it |
refresh_preview | Refresh widget/portal preview tabs |
take_screenshot | Capture a ServiceNow URL/tab to screenshots/ |
upload_attachment | Attach a file (disk path or base64) to a record |
run_slash_command | Execute an SN Utils slash command (e.g. /tn, /bg) |
switch_context | Switch update set, application scope, or domain |
Browser debugger (CDP) — needs the Debug edition build + a Pro subscription, off by default
These commands drive the connected ServiceNow tab through the Chrome DevTools Protocol (chrome.debugger) for things the normal content-script bridge can't do — network bodies, console errors, beyond-viewport screenshots, and captured dialog text. They are an escalation: reach for the g_form / REST commands first.
| Command | Description |
|---|---|
start_network_capture | Start recording network traffic (method/URL/headers and, by default, response bodies). Supports urlFilter, includeBodies, includeTypes, maxEntries, maxBodyBytes |
stop_network_capture | Stop the capture and return the recorded requests; detaches the debugger unless another capture/handler is still active |
start_console_capture | Start capturing console.* output, log entries, and uncaught exceptions |
stop_console_capture | Stop the capture and return the collected entries (kind: console / log / exception) |
capture_full_page | Full-page (entire scrollable page) or single-element (selector) screenshot saved under screenshots/. Needs no per-tab grant, unlike take_screenshot |
set_dialog_handler | Auto-answer and record native confirm() / alert() / prompt() / beforeunload dialogs (autoAccept, promptText). Persists across navigations |
clear_dialog_handler | Remove the dialog handler and return the dialogs intercepted while it was active |
debugger_detach | Force-detach the debugger and drop the banner — a safety net if a session was left open |
A typical "why did that request fail?" loop:
start_network_capture (urlFilter: "/api/now") start_console_capture → run_ui_action / click_element / navigate (reproduce the action) stop_network_capture (inspect status + response body) stop_console_capture (inspect client-side errors)
All browser-debugger commands share these error codes: E_PRO_REQUIRED, E_DISABLED, E_CDP_UNAVAILABLE, E_DEBUGGER_BUSY, E_NO_TAB, E_BROWSER_DISCONNECTED, E_TIMEOUT (plus E_NO_ELEMENT for capture_full_page with a selector).
The original transport is an event-driven folder queue — no HTTP, just JSON files:
{instance}/agent/requests/req_<id>.json{instance}/agent/responses/res_<id>.json// {instance}/agent/requests/req_chk_1.json
{ "id": "chk_1", "command": "check_connection" }
This transport stays available as a fallback (controlled by the sn-scriptsync.agentApi.fileFallback setting, enabled by default) so existing automations keep working. New integrations should prefer the HTTP API.
If you built automations against the file queue, here's how to move to the HTTP API:
.vscode/sn-agent-port.json and health-check it, instead of writing files into agent/requests/.POST the same JSON payload ({ "id": ..., "command": ... }) to http://127.0.0.1:<port>/api with an X-Agent-Token: <token> header, and read the response from the HTTP body instead of polling agent/responses/.sn-scriptsync.agentApi.fileFallback once everything is on HTTP.The complete, always-current command reference also ships inside the extension as agentinstructions.md (the same file your AI agent reads). The sn-scriptsync GitHub repository holds the source and issue tracker.
Comprehensive security measures protect your workspace and ServiceNow instance:
127.0.0.1 (never exposed on the network) on a random free portPOST /api request must carry the X-Agent-Token: <token> header from .vscode/sn-agent-port.json; the port and token are regenerated each sessionagent/ folders and .vscode/sn-agent-port.json are excluded from ServiceNow sync and git tracking