ServiceNow MCP Server
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Avanade ServiceNow MCP Server
Version 2.0 · Multi-instance Model Context Protocol server for ServiceNow
Connect Claude, GitHub Copilot or any MCP client to one or many ServiceNow instances: 45 tools, generic CRUD over any table, and automated background-script execution.
An Avanade internal engineering asset · in development since October 2026
📖 Read the complete guide in the wiki →
Install · configure · all 45 tools · deployment · operations · governance
Contents
Documentation — complete guide: the wiki
Related MCP server: servicenow-platform-mcp
What this is
A Node.js MCP server that exposes ServiceNow to AI assistants. It is metadata-driven: generic CRUD tools work against any ServiceNow table by name, with a curated catalogue of 162 tables (ITSM, CMDB, Service Catalog, platform development, workflows, Flow Designer, integration) used for schema hints and discovery.
At a glance
MCP tools | 45 — frozen client contract, see Frozen Contract |
MCP resources | 2 read-only |
Tables | Generic CRUD on any table; 162 in the curated catalogue |
Instances | Unlimited, configured in one file, switched per session |
Transports | HTTP + SSE ( |
Documentation | The wiki — complete guide, 19 pages |
Server identity |
|
Node.js | 18+ (Docker image builds on Node 22) |
What's in v2.0
Multi-instance routing with per-session instance switching
45 tools: generic CRUD, ITSM convenience operations, update-set management, workflow creation, batch operations, natural-language search, schema discovery
Automated background-script execution via the
sys_triggertable (~1 second, trigger auto-deletes), with fix-script generation as fallback2 read-only MCP resources for server/instance details and the table catalogue
SSE keepalive heartbeat (15 s default) so connections survive Docker, Kubernetes and reverse-proxy idle timeouts
Bounded, expiring session store — sessions cap at 100 and expire after 1 hour idle
Frozen client contract with automated verification (
npm run contract:verify)
Quick start
Prerequisites: Node.js 18+, a ServiceNow instance with REST API access, and credentials for a user with the roles your operations need.
git clone https://github.com/ava-innersource/servicenow-mcp-server.git
cd servicenow-mcp-server
npm installConfigure at least one instance (see below), then:
npm run dev # HTTP + SSE server on :3000, with reload
# or
npm start # HTTP + SSE server on :3000
# or
npm run stdio # stdio transport, for Claude Desktop / IDE clientsVerify:
curl http://localhost:3000/health # server status
curl http://localhost:3000/instances # configured instances
npm run inspector # MCP Inspector against http://localhost:3000/mcpConfiguring instances
Multi-instance (recommended)
cp config/servicenow-instances.json.example config/servicenow-instances.json{
"instances": [
{
"name": "dev",
"url": "https://dev123456.service-now.com",
"username": "admin",
"password": "your-password",
"default": true
},
{
"name": "prod",
"url": "https://prod789012.service-now.com",
"username": "integration_user",
"password": "your-password"
}
]
}Exactly one instance should carry "default": true — that is the instance the server
starts on. Restart the server after editing the file.
Single instance via .env
Supported for backward compatibility and for container deployments where mounting a config file is inconvenient:
cp .env.example .envSERVICENOW_INSTANCE_URL=https://your-instance.service-now.com
SERVICENOW_USERNAME=your-username
SERVICENOW_PASSWORD=your-password
SERVICENOW_INSTANCE_NAME=default
PORT=3000
DEBUG=trueNever commit
.envorconfig/servicenow-instances.json. Both hold plaintext credentials and are git-ignored. Use your platform's secret store in deployed environments.
How instance selection works
Instance selection is per session, not per call. This matters when writing agent prompts — it is the most common source of "it wrote to the wrong instance" bugs.
1. Switch the session's active instance with SN-Set-Instance. Every subsequent
tool call in that session targets it until changed:
SN-Set-Instance({ instance_name: "prod" })
SN-Query-Table({ table_name: "incident", query: "active=true", limit: 10 }) // -> prod
SN-Set-Instance({}) // no argument: lists instances and shows the active one
SN-Get-Current-Instance({}) // confirm before any write2. Eleven convenience tools also accept a per-call instance, which overrides the
session instance for that call only:
SN-Add-Comment · SN-Add-Work-Notes · SN-Assign-Incident · SN-Resolve-Incident ·
SN-Close-Incident · SN-Add-Change-Comment · SN-Assign-Change · SN-Approve-Change ·
SN-Add-Problem-Comment · SN-Close-Problem · SN-Set-Instance
SN-Add-Work-Notes({
incident_number: "INC0012345",
work_notes: "Restarted application server.",
instance: "prod"
})The other 34 tools have no instance parameter. Passing one is silently ignored —
call SN-Set-Instance first instead.
Tool catalogue — all 45 tools
Tool names are frozen. See Client contract and compatibility.
Instance management (2)
Tool | Purpose |
| Switch the session's active instance; lists instances when called with no argument |
| Report the currently active instance |
Generic CRUD and schema — works on any table (7)
Tool | Purpose |
| Query any table with an encoded query, field selection, paging, ordering |
| Create a record in any table |
| Fetch one record by |
| Update a record by |
| Required fields, common fields, field descriptions |
| Deep schema including relationships |
| Browse the curated table catalogue |
SN-Query-Table({
table_name: "incident",
query: "active=true^priority=1",
fields: "number,short_description,assigned_to",
limit: 25,
order_by: "-sys_created_on"
})
SN-Create-Record({
table_name: "sys_properties",
data: { name: "x_custom.setting", value: "enabled" }
})
SN-Update-Record({
table_name: "incident",
sys_id: "abc123...",
data: { state: 6, close_notes: "Resolved" }
})Record listing — typed shortcuts (8)
Tool | Table |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ITSM convenience operations (10)
These take the record number, not a sys_id, and resolve user and group names
automatically — designed for agent conversations where the number is what the user says.
Tool | Required arguments |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SN-Assign-Incident({
incident_number: "INC0012345",
assigned_to: "Jane Doe",
assignment_group: "Network Support"
})
SN-Resolve-Incident({
incident_number: "INC0012345",
resolution_notes: "Replaced the failed switch port.",
resolution_code: "Solved (Permanently)"
})All ten accept an optional instance.
Natural-language search (1)
SN-Natural-Language-Search converts plain English into a ServiceNow encoded query,
runs it, and returns both the records and the query it generated — so you can inspect
and reuse the translation.
SN-Natural-Language-Search({
query: "unassigned P2 incidents created today",
table: "incident", // default: "incident"
limit: 25,
fields: "number,short_description,priority",
show_patterns: true // include how each phrase was parsed
})Recognised patterns: priority (P1-P5, high/low), assignment (assigned to me,
unassigned, assigned to <name>), dates (created today, last 7 days, recent),
states (new/open/closed/in progress), content (about SAP, containing error),
impact and urgency, and record numbers (number is INC0012345).
Use it for exploration. For automation, read the encoded query it returns and pass that
to SN-Query-Table — the translation is deterministic, but the phrasing is not.
Update-set management (6)
Tool | Purpose |
| Make an update set current (automated via |
| Report the active update set |
| List update sets with filtering |
| Move |
| Copy an update set and all its records |
| Component breakdown and missing-dependency check |
// Rescue records that landed in "Default"
SN-Move-Records-To-Update-Set({
update_set_id: "target_set_sys_id",
source_update_set: "Default",
time_range: { start: "2026-10-15 09:00:00", end: "2026-10-15 11:30:00" }
})SN-Move-Records-To-Update-Set, SN-Clone-Update-Set, SN-Batch-Create,
SN-Batch-Update and SN-Create-Workflow emit MCP progress notifications during
execution (progress: true by default).
Application scope (1)
Tool | Purpose |
| Set the active application scope for scoped-app development |
Script execution (2)
Tool | Purpose |
| Run server-side JavaScript |
| Generate a fix script for manual execution |
SN-Execute-Background-Script has three strategies:
sys_trigger(default, most reliable) — creates a scheduled job that runs in about a second and deletes its own triggerUI endpoint (
sys.scripts.do) — direct executionFix script — writes a script for manual execution in the ServiceNow UI
SN-Execute-Background-Script({
script: "gs.info('Hello from an automated script');",
description: "Smoke test"
})Output goes to ServiceNow System Logs → System Log → All. The call returns when the job is scheduled, not when it has finished — allow ~2 seconds before reading results.
Workflows (4)
Tool | Purpose |
| Full lifecycle: workflow → version → activities → transitions → optional publish |
| Add an activity (with embedded JavaScript) to an existing workflow version |
| Link two activities, with an optional condition script |
| Publish a workflow version |
SN-Create-Workflow({
name: "Auto-Approve Standard Changes",
table: "change_request",
condition: "type=standard",
activities: [
{ name: "Check Risk", script: "workflow.scratchpad.risk = current.risk;" },
{ name: "Approve", script: "current.approval = 'approved'; current.update();" }
],
transitions: [{ from: "Check Risk", to: "Approve" }],
publish: true
})Batch operations (2)
Tool | Purpose |
| Create related records in one call; |
| Update many records across tables |
SN-Batch-Create({
operations: [
{ table: "sc_cat_item", data: { name: "New Laptop" }, save_as: "item" },
{ table: "item_option_new", data: { cat_item: "${item}", name: "model" } }
],
transaction: true // all-or-nothing, default
})
SN-Batch-Update({
updates: [
{ table: "incident", sys_id: "id1", data: { priority: 2 } },
{ table: "incident", sys_id: "id2", data: { priority: 2 } }
],
stop_on_error: false
})Inspection and validation (2)
Tool | Purpose |
| Field type, constraints, choices, known issues, usage examples |
| Validate a catalog item's variables, UI policies and business rules |
SN-Explain-Field resolves inherited fields by walking the table hierarchy, so
incident.priority is found on task and reported with defined_on: task and
inherited: true. Note that it takes table and field, not the table_name naming
the other schema tools use.
MCP resources
Two read-only resources are registered, for lookups that do not warrant a tool call. Both were verified against a live instance.
URI | Returns |
| Server details and the connected instance |
| The full curated table catalogue (about 52 KB of JSON) |
src/resources.jsdefines a richer, instance-scoped set (servicenow://{instance}/incidentsand similar, 8 URIs in total). That module is not imported by the server and none of those URIs are advertised or readable. Treat it as unfinished work rather than as available surface.
Connecting a client
HTTP + SSE
Method | Path | Purpose |
|
| Open the SSE stream |
|
| Send a JSON-RPC message |
|
| Terminate a session |
|
| Health check |
|
| List configured instances |
In MCP Inspector choose transport Streamable HTTP (SSE) and URL
http://localhost:3000/mcp.
Claude Desktop (stdio)
Add to claude_desktop_config.json — macOS:
~/Library/Application Support/Claude/claude_desktop_config.json;
Windows: %APPDATA%\Claude\claude_desktop_config.json.
{
"mcpServers": {
"servicenow": {
"command": "node",
"args": ["/path/to/servicenow-mcp-server/src/stdio-server.js"],
"cwd": "/path/to/servicenow-mcp-server",
"env": {
"SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password"
}
}
}
}Restart Claude Desktop fully (quit, not just close the window) for the tools to appear. Full walkthrough: Client Setup.
With a multi-instance config file present, set SERVICENOW_INSTANCE to pick the
starting instance by name instead of supplying URL and credentials.
Running in Docker
docker compose up -d
docker compose logs -f
curl http://localhost:3000/healthdocker-compose.yml mounts config/servicenow-instances.json read-only and reads
credentials from your shell environment or a local .env. The image is a two-stage
Node 22 Alpine build running src/server.js, with a built-in healthcheck.
Behind nginx, Traefik or HAProxy, SSE needs buffering disabled and a read timeout longer than the keepalive interval. Full configurations: SSE and Reverse Proxies.
Environment variables
Variable | Default | Purpose |
| — | Instance URL (single-instance mode) |
| — | Username (single-instance mode) |
| — | Password (single-instance mode) |
|
| Name for the single configured instance |
| — | Select a named instance from the config file at startup |
|
| HTTP listen port |
| — | Verbose logging when |
|
| SSE heartbeat in ms; lower it if a proxy closes idle streams |
|
| Concurrent session cap |
|
| Idle time before a session is evicted |
The SERVICENOW_*, PORT, DEBUG and SSE_KEEPALIVE_INTERVAL names are part of the
frozen contract. The AVANADE_MCP_* settings are additive and carry their own prefix so
existing deployments are unaffected.
Architecture
src/
├── server.js # Express HTTP + SSE transport, keepalive, session lifecycle
├── stdio-server.js # Stdio transport (Claude Desktop, IDE clients)
├── mcp-server-consolidated.js # Tool registration and dispatch - all 45 tools
├── servicenow-client.js # ServiceNow REST client, auth, instance switching
├── config-manager.js # Multi-instance configuration loading and lookup
├── session-store.js # Bounded, TTL-expiring MCP session registry
├── natural-language.js # Plain English -> encoded query translation
├── resources.js # Unused: defines 8 resources, not imported (see MCP resources)
├── script-sync.js # Imported but unused: exports are never called
└── config/
├── table-definitions.json # 162-table curated catalogue
└── comprehensive-table-definitions.json # Detailed schema hints
config/servicenow-instances.json # Your instances (git-ignored)
contract/mcp-tool-contract.v1.json # Frozen client-facing surface
scripts/verify-tool-contract.js # Contract enforcement
scripts/smoke-tool-surface.js # Wire-level tool surface checkDesign notes
Metadata-driven. No per-table tool generation; generic tools take
table_name, so custom and scoped tables work without code changes.One client per session. Each MCP session holds its own server instance and ServiceNow client, which is why
SN-Set-Instanceis session-scoped.Bounded sessions. Abandoned SSE connections used to accumulate for the process lifetime.
session-store.jscaps and expires them, running cleanup on eviction.
Client contract and compatibility
This server is already deployed. Live agents, IDE configurations and the ARIA agent suite bind to its identifiers, so five things are frozen at contract version 1 (frozen 2026-11-02):
The 45 tool names, including the
SN-prefix — it is not rebranded toAVA-The server name
servicenow-serverThe bin name
servicenow-mcp-serverThe five HTTP endpoints
The eight contract environment variable names
npm run contract:verify # fails the build if any frozen identifier moved
npm run smoke:tools # checks the live tool surface over the wireRenaming any frozen identifier breaks every deployed client, and the failure surfaces as "tool not found" inside an agent conversation — far from the commit that caused it. If a change is genuinely required, follow the process in Frozen Contract. Do not edit the contract JSON to silence a failing check.
Rebranding is scoped to packaging and presentation: package name, author, licence, documentation, Docker image, repository URL. It does not touch the protocol surface.
Testing
npm test # 10 suites, 244 tests
npm run test:watch
npm run test:coverage # fails by design - see below
npm run contract:verify
npm run smoke:toolsRead this before trusting a green run. All 244 tests pass, but coverage of src/ is
11% of statements and very unevenly spread:
Module | Statements | Note |
| 100% | Tested against the real module |
| 100% | Tested against the real module |
| 11% | Partially exercised |
| 7% | Partially exercised |
| 0% | All 45 tool handlers are untested |
| 0% | Untested |
| 0% | Untested |
The uncovered suites assert against mocks and against logic reimplemented inline in the
test files rather than importing production code. The 80% coverageThreshold in
jest.config.js is kept as a target, so test:coverage fails by design.
A green suite is therefore not evidence that the tool handlers work.
contract:verify and smoke:tools are the only automated statement about the
client-facing surface, and they confirm that tools are registered and well-formed, not
that they return correct data. Tests against the handler layer are open work.
Known limitations
Not possible through the REST API
Flow Designer logic blocks (IF / FOREACH) — build in the UI or from templates
Flow compilation and validation — flows compile in the UI
UI Policy Action linking — needs a background script using
setValue()
Covered by workarounds
Background scripts execute automatically via
sys_triggerUpdate-set operations are fully automated through REST
Generic table tools work on custom and scoped tables
Workflow creation is fully supported programmatically
Detail: Platform Limitations.
Troubleshooting
Connection
curl -u user:pass "https://your-instance.service-now.com/api/now/table/incident?sysparm_limit=1"
curl http://localhost:3000/health
curl http://localhost:3000/instancesTools do not appear
Check the server log for registration errors, confirm the client is pointed at /mcp,
and confirm credentials are valid — an auth failure at startup can leave the tool list
empty.
Multi-instance not working
Confirm config/servicenow-instances.json exists and is valid JSON, that exactly one
instance has "default": true, and restart — the config is read at startup.
Wrong instance was written to
Call SN-Get-Current-Instance to see the active instance for the session. Remember that
only the eleven convenience tools honour a per-call instance; everything else follows
SN-Set-Instance.
403 Permission denied
Usually missing roles on system tables (sys_dictionary, sys_db_object) — typically
personalize_dictionary or security_admin. See
Permissions and 403s.
SSE connection drops in Docker or behind a proxy
Lower SSE_KEEPALIVE_INTERVAL below the proxy idle timeout and disable response
buffering. See SSE and Reverse Proxies.
Background script appears to do nothing
The tool returns when the sys_trigger job is scheduled, not when it completes. Wait
~2 seconds, then check System Logs → System Log → All.
Verbose logging
DEBUG=true npm run devDocumentation
Full documentation lives in the wiki — start at Home or the sidebar index.
Getting started
Installation — install and run locally
Configuration — point it at your instances
Client Setup — Claude Desktop, Claude Code, Copilot
Tools and resources
Tool Index — every tool and its parameters, generated from source
Usage Patterns — worked examples
Deployment and operations
Governance
Frozen Contract — the frozen surface and how to change it
Architecture
Platform Limitations — Flow Designer and other REST boundaries
Archive — disposition of the legacy documentation tree
The legacy
docs/tree is kept locally but is no longer in version control. Where it and the wiki disagree, the wiki is correct.
For AI agents working in this repo — CLAUDE.md.
Acknowledgments
Inspired by the Echelon AI Labs ServiceNow MCP Server. We are grateful for their pioneering work in bringing Model Context Protocol capabilities to ServiceNow, which informed this multi-instance, metadata-driven implementation.
What this implementation adds: multi-instance support with session-scoped routing,
automated background-script execution via sys_trigger, update-set management including
record movement and cloning, natural-language search, programmatic workflow creation,
and a frozen, machine-verified client contract.
License
Avanade Proprietary — Internal Use Only. See LICENSE for full terms.
Copyright © 2026 Avanade Inc. All rights reserved.
This software is the confidential and proprietary information of Avanade Inc. It is licensed for internal Avanade business purposes and for delivery of services to Avanade clients under a valid engagement. It must not be published to any public repository, package registry or container registry.
This is bespoke custom code, not an Avanade product or supported offering. There is no product support, service level or maintenance commitment — whoever deploys it owns its dependency updates, security patching and ServiceNow compatibility. See clause 4 of the LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Build and run AI workflows, agents, data tables and 1000+ API integrations from any MCP client.
Unified MCP Server is a remote MCP connector for AI agents and vertical AI products that provides access to 22,000+ authorized SaaS tools across 400+ integrations and 24 categories directly inside LLMs (Claude, GPT, Gemini, Cohere). Tools operate only on explicitly authorized customer connections, enabling agents to safely read and write against live third-party systems.
Connect any AI agent to 1,000+ apps and 27,000+ actions through one remote MCP server (OAuth).
Connect MCP clients to 2,000+ AI models without managing provider API keys.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables natural language interaction with ServiceNow instances for managing incidents, changes, CMDB, service catalog, users, groups, and knowledge base via MCP.4120 npm1MIT
- AlicenseAqualityBmaintenanceEnables AI agents to interact with ServiceNow through MCP, providing schema inspection, record CRUD, attachments, audit, Flow Designer, and platform investigations with policy-guarded access.152,105 PyPI4MIT
- AlicenseCqualityAmaintenanceEnables AI to interact with ServiceNow instances via MCP, providing 400+ tools across all modules for automation, development, and management.5004,466 npm17Elastic 2.0
- AlicenseBqualityAmaintenanceEnables natural language control of ServiceNow from AI clients like Claude and Cursor. Provides 400+ tools for incidents, changes, CMDB, and scripts via MCP protocol.500177 npm2MIT