Skip to main content
Glama
JoseanMeonez

servicenow-mcp

by JoseanMeonez

servicenow-mcp

Standalone MCP server for ServiceNow. Exposes the Table, Aggregate, and schema APIs as tools, with an in-process update-set write gate: write tools are only registered when explicitly enabled, and every write is refused while your current update set is "Default".

Works with Claude Code, Claude Desktop, Antigravity CLI, opencode, GitHub Copilot CLI, GitHub Copilot in VS Code, and any other MCP client (stdio transport).

Requirements

  • Node.js >= 20.6

  • A ServiceNow instance reachable with Basic Auth (username/password)

Related MCP server: servicenow-mcp

Install

git clone https://github.com/JoseanMeonez/servicenow-mcp.git
cd servicenow-mcp
npm install
npm run build

Configuration

All configuration is via environment variables (a .env file in the repo root is also loaded when present — see .env.example):

Variable

Default

Description

SN_BASE_URL

— (required)

Instance URL, e.g. https://dev12345.service-now.com

SN_USERNAME

— (required)

Basic Auth username

SN_PASSWORD

— (required)

Basic Auth password

SN_MCP_ALLOW_WRITES

false

Register write tools (create/update/delete/set update set)

SN_MCP_REQUIRE_UPDATE_SET

true

Refuse writes while the current update set is "Default"

SN_MCP_DEFAULT_LIMIT

50

Default query page size

SN_MCP_MAX_LIMIT

500

Hard ceiling on any requested limit

SN_MCP_REQUEST_TIMEOUT_MS

30000

Per-request timeout

SN_MCP_RETRY_MAX_ATTEMPTS

3

Attempts for 429/5xx responses (honors Retry-After)

Changing SN_MCP_ALLOW_WRITES requires restarting the MCP server process — tools are registered at startup, not per call.

Instance user setup (required per instance)

Recent ServiceNow releases do not accept Basic Auth on the REST API by default. For every instance you connect, the user in SN_USERNAME must be set up as follows:

  1. Roles: grant snc_basic_auth_api_access (mandatory for Basic Auth REST access) plus the roles needed for the tables you will touch. On a personal dev instance admin is fine; at work prefer least-privilege roles over admin.

  2. Integration user flag: on the sys_user record, set internal_integration_user = true.

  3. Recommended: use a dedicated integration user (e.g. api.tester), never a personal or the admin account, so API access can be rotated or revoked independently.

Symptom when this is missing: every request fails with 401 "User is not authenticated" even though the credentials are correct. The server's error output includes this hint automatically on 401 responses.

Register with Claude Code

claude mcp add servicenow-mcp -- node C:/Users/you/path/to/servicenow-mcp/dist/index.js

or copy .mcp.json.example into your project's .mcp.json and adjust path + env.

Register with Claude Desktop

Claude Desktop uses a different config file than Claude Code: %APPDATA%\Claude\claude_desktop_config.json (Windows). Add under mcpServers, using the full path to node.exe (Desktop may not inherit your shell PATH):

{
  "mcpServers": {
    "servicenow-dev": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": [
        "--env-file=C:/path/to/servicenow-mcp/instances/dev.env",
        "C:/path/to/servicenow-mcp/dist/index.js"
      ]
    }
  }
}

Then fully quit Claude Desktop (system tray → Quit, not just closing the window) and reopen it — the config is only read at startup.

Register with Antigravity CLI

Antigravity CLI's MCP config lives at ~/.gemini/antigravity-cli/mcp_config.json (it shares the Gemini CLI config layout, not ~/.antigravitycli). Add under mcpServers:

{
  "mcpServers": {
    "servicenow-dev": {
      "command": "node",
      "args": [
        "--env-file=C:/path/to/servicenow-mcp/instances/dev.env",
        "C:/path/to/servicenow-mcp/dist/index.js"
      ]
    }
  }
}

Start a new Antigravity CLI session afterward — config is only read at startup, an existing session won't pick it up.

Prompt to hand to Antigravity CLI (or any agent with file-edit access) to do this for you:

Add a new entry under mcpServers in ~/.gemini/antigravity-cli/mcp_config.json named servicenow-<instance>, pointing to node with args --env-file=<absolute path to instances/<instance>.env> and <absolute path to dist/index.js>. Keep existing entries intact. Then tell me to start a new session for it to take effect.

Register with opencode

Add under mcp in ~/.config/opencode/opencode.json (or your project's opencode.json):

{
  "mcp": {
    "servicenow-dev": {
      "enabled": true,
      "type": "local",
      "command": [
        "node",
        "--env-file=C:/path/to/servicenow-mcp/instances/dev.env",
        "C:/path/to/servicenow-mcp/dist/index.js"
      ]
    }
  }
}

Note the command and its args live together in a single array (unlike Claude's command/args split).

Prompt to hand to opencode:

Add a new entry under mcp in ~/.config/opencode/opencode.json named servicenow-<instance>, with "type": "local", "enabled": true, and command as an array: ["node", "--env-file=<absolute path to instances/<instance>.env>", "<absolute path to dist/index.js>"]. Keep existing entries intact.

Register with GitHub Copilot CLI

Copilot CLI's config lives at ~/.copilot/mcp-config.json (override via COPILOT_HOME). Add under mcpServers, with an explicit "type": "stdio":

{
  "mcpServers": {
    "servicenow-dev": {
      "type": "stdio",
      "command": "node",
      "args": [
        "--env-file=C:/path/to/servicenow-mcp/instances/dev.env",
        "C:/path/to/servicenow-mcp/dist/index.js"
      ],
      "env": {}
    }
  }
}

Or from the terminal: copilot mcp add (interactive), then verify with /mcp show inside a Copilot CLI session.

Prompt to hand to Copilot CLI:

Add a new entry under mcpServers in ~/.copilot/mcp-config.json named servicenow-<instance>, with "type": "stdio", command: "node", and args ["--env-file=<absolute path to instances/<instance>.env>", "<absolute path to dist/index.js>"]. Keep existing entries intact. Then run /mcp show to confirm it loaded.

Register with GitHub Copilot in VS Code

VS Code Copilot uses .vscode/mcp.json in the workspace (commit it to share with your team), with the root key serversnot mcpServers like the other clients:

{
  "servers": {
    "servicenow-dev": {
      "type": "stdio",
      "command": "node",
      "args": [
        "--env-file=C:/path/to/servicenow-mcp/instances/dev.env",
        "C:/path/to/servicenow-mcp/dist/index.js"
      ]
    }
  }
}

Saving the file with valid JSON restarts the Copilot agent and reloads servers automatically — no full VS Code restart needed.

Prompt to hand to Copilot Chat in VS Code:

Create or update .vscode/mcp.json in this workspace: add an entry under servers (not mcpServers) named servicenow-<instance>, with "type": "stdio", command: "node", and args ["--env-file=<absolute path to instances/<instance>.env>", "<absolute path to dist/index.js>"]. Keep existing entries intact.

Multiple instances

The recommended pattern is one server process per instance, each declared as its own entry in .mcp.json and pointed at a per-instance profile file via Node's native --env-file flag (before the script path):

{
  "mcpServers": {
    "servicenow-dev": {
      "command": "node",
      "args": ["--env-file=/path/to/instances/dev.env", "/path/to/dist/index.js"]
    },
    "servicenow-prod": {
      "command": "node",
      "args": ["--env-file=/path/to/instances/prod.env", "/path/to/dist/index.js"]
    }
  }
}

Node fails fast if the profile file is missing, and real environment variables take precedence over file values.

Why per-process instead of one multi-tenant server:

  • Zero shared state. The server holds no caches and no sessions (see below); separate processes make cross-instance leakage structurally impossible, not just avoided.

  • Per-instance write policy. A prod profile with SN_MCP_ALLOW_WRITES=false (or simply omitting it) never even registers write tools — the client cannot call what does not exist.

  • Clear tool naming. Tools surface as mcp__servicenow-dev__* vs mcp__servicenow-prod__*, so it is always explicit which instance a call targets.

  • Independent rate limits. ServiceNow enforces inbound REST rate limits per user per instance, so parallel processes against different instances never interact.

To add a new instance in one step, use the helper — it writes the profile file with correct password quoting and prints the registration command:

npm run add-instance -- work https://mycompany.service-now.com api.integration 'the-password'

Keep profile files (e.g. instances/*.env) out of git — the .gitignore already excludes .env and instances/.

Statelessness guarantees

  • No record, schema, or token caching — every tool call hits the instance fresh.

  • Basic Auth header is computed per request; response cookies are ignored (no cookie jar), so no ServiceNow session is retained between calls.

  • The write gate re-reads your current update set from the instance on every write.

If a metadata cache (e.g. table schemas) ever becomes worth it, it should be explicit, on-disk, and per-instance — never implicit in-process memory.

Tools

Read tools (always registered):

Tool

Description

servicenow_query_records

Query a table with an encoded query; paginated (hasMore/nextOffset)

servicenow_get_record

Fetch one record by sys_id

servicenow_get_aggregate

Stats API: count/avg/sum/min/max, optionally grouped

servicenow_get_table_schema

Field list from sys_dictionary, including inherited fields

servicenow_get_current_update_set

Show your current update set

Write tools (only when SN_MCP_ALLOW_WRITES=true):

Tool

Description

servicenow_create_record

Create a record (gated)

servicenow_update_record

Update a record (gated)

servicenow_delete_record

Delete a record (gated, requires confirm: true)

servicenow_set_current_update_set

Switch your current update set (how you move off "Default")

The write gate

Every write first resolves your current update set on the instance (sys_user_preferencesys_update_set). If it is "Default", the write is refused with a clear error telling you to switch sets first. This keeps AI-driven changes tracked in a real update set, the same discipline you'd apply by hand. Disable with SN_MCP_REQUIRE_UPDATE_SET=false (e.g. for non-development instances).

Development

npm run dev          # run from source (tsx)
npm test             # unit + in-memory integration tests
npm run test:live    # live smoke tests (needs SN_* env or .env; read-only)
npm run inspect      # MCP Inspector against dist/index.js
npm run ci           # lint + build + test

Known limitations

  • Basic Auth only (v1). If your instance enforces SSO/MFA for API access, requests fail with a "possible SSO/MFA redirect" error. An OAuth or session-based AuthStrategy is the planned phase 2 (src/client/auth.ts is the single seam to extend).

  • Schema from sys_dictionary. Portable to every instance (including PDIs), but virtual/ computed fields may be missing compared to /api/now/doc/table/schema.

  • PDI hibernation. Personal developer instances sleep after inactivity; wake yours in a browser before running live tests.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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/JoseanMeonez/servicenow-mcp'

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