Skip to main content
Glama
jshsakura

mfa-servicenow-mcp

by jshsakura

MFA ServiceNow MCP

🌐 English | 🇰🇷 한국어 | 🇯🇵 日本語 | 🇮🇳 हिन्दी | 🇨🇳 简体中文 | 🇪🇸 Español | 🚀 GitHub Pages

MFA-first ServiceNow MCP server. Authenticates via real browser (Playwright) so Okta, Entra ID, SAML, and any MFA/SSO login just works. Also supports API Key for headless/Docker environments.

PyPI version Python Version CI Docker License: Apache 2.0 GitHub Pages

WARNING

Built for personal use — use at your own risk. This project was created primarily for the author's own workflows. Risk is actively minimized (read-only defaults, write guards, dry-run previews, and confirm='approve' gates on every write), but it operates against live ServiceNow instances. You are solely responsible for what it does on your instances. Provided "as is", without warranty of any kind (Apache-2.0, see LICENSE). Review what a tool will do before approving it.


Table of Contents


Related MCP server: servicenow-mcp

Setup

Two steps: install, then add the server to your MCP client config. No installer command, no per-client flags.

1. Install

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
uvx --refresh --with playwright --from mfa-servicenow-mcp servicenow-mcp --version  # fetch + verify the server
uvx --with playwright playwright install chromium                                   # Chromium for MFA/SSO login
# Windows PowerShell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uvx --refresh --with playwright --from mfa-servicenow-mcp servicenow-mcp --version  # fetch + verify the server
uvx --with playwright playwright install chromium                                   # Chromium for MFA/SSO login

This installs uv, fetches+verifies the server, and downloads Chromium — once. The --with playwright on the fetch matches the runtime config below, so uvx caches the exact env and the first client start is instant.

Guided setup. Running servicenow-mcp setup with no flags walks you through numbered menus (pick clients and auth type by number or name — no free-text guessing), in English or Korean (auto-detected from your locale; force with SERVICENOW_MCP_LANG=ko|en).

2. Configure your MCP client

Add the server to your client's config file — pick yours below. Only two env vars are required; MCP_TOOL_PACKAGE defaults to standard, so leave it out unless you need a different package.

Claude Code.mcp.json (project root) / ~/.claude.json (global):

{
  "mcpServers": {
    "servicenow": {
      "command": "uvx",
      "args": ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp"],
      "env": {
        "SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
        "SERVICENOW_AUTH_TYPE": "browser"
      }
    }
  }
}

Codex.codex/config.toml (project) / ~/.codex/config.toml (global):

[mcp_servers.servicenow]
command = "uvx"
args = ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp"]

[mcp_servers.servicenow.env]
SERVICENOW_INSTANCE_URL = "https://your-instance.service-now.com"
SERVICENOW_AUTH_TYPE = "browser"

OpenCodeopencode.json (project root):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servicenow": {
      "type": "local",
      "command": ["uvx", "--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp"],
      "enabled": true,
      "environment": {
        "SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
        "SERVICENOW_AUTH_TYPE": "browser"
      }
    }
  }
}

Other clients (Cursor, VS Code, Antigravity, Zed, …) and full env options (auth types, tool packages) are in MCP Client Configuration.

Then restart the client. The first browser tool call opens a window for Okta/Entra ID/SAML/MFA login. Sessions persist — no re-login every time.

Prefer an AI to do it? Paste into Claude Code / Cursor / Codex / etc.: Install and configure mfa-servicenow-mcp following https://raw.githubusercontent.com/jshsakura/mfa-servicenow-mcp/main/docs/llm-setup.md Corporate network blocking uvx/PyPI? Use the release zip/exe.


Features

  • Browser authentication for MFA/SSO environments (Okta, Entra ID, SAML, MFA)

  • 4 auth modes: Browser, Basic, OAuth, API Key

  • 65 registered tools with 6 active package profiles plus disabled none — from minimal read-only to broad bundled CRUD

  • 16 workflow skills with safety gates, sub-agent delegation, and verified pipelines

  • Streamable HTTP transport — keep stdio as the default, or expose /mcp for HTTP-capable clients and bridges

  • Local source audit with HTML report, cross-reference graph, dead code detection, and auto-generated domain knowledge

  • Authoritative relationship graphs on disk_graph.json (widget→Angular Provider, from the live M2M) and _page_graph.json (page→widget, from sp_instance) let the LLM answer dependency questions offline instead of re-querying the instance

  • Incremental sync (incremental=True) — re-download only records changed since last sync (sys_updated_on watermark), like git pull; reconcile_deletions=True flags records deleted on the instance

  • Cross-scope dep auto-resolve in download_app_sources — pulls global-scope Script Includes, Widgets, Angular Providers, and UI Macros that the app references, so the local bundle is self-contained for analysis

  • Attachment download (download_attachment) — fetch a record's attachment file(s) (xlsx, PDF, Word, …) to local disk by attachment sys_id or by parent table+record; resolves a record's attachments automatically and writes bytes to disk so the LLM reads them from saved_path

  • Dry-run preview on every write tool (dry_run=True) — returns field-level diff, dependency counts, and precision notes before any side effect. Uses read-only APIs, works under all auth modes.

  • Write intent gate: every mutation requires an explicit confirm='approve' (accidental-write guardrail, not an adversarial boundary — see Safety Policy)

  • Payload safety limits, per-field truncation, and total response budget (200K chars)

  • Transient network error retry with backoff

  • Tool packages for core, standard, service desk, portal developers, and platform developers — full available for advanced users (see warning)

  • Developer productivity tools: activity tracking, uncommitted changes, dependency mapping, daily summary

  • Full coverage of core ServiceNow artifact tables (see Supported Tables)

  • CI/CD with auto-tagging, PyPI publishing, and Docker multi-platform builds

Supported ServiceNow Tables

Artifact Type

Table Name

Source Search

Developer Tracking

Safety (Heavy Table)

Script Include

sys_script_include

🛡️

Business Rule

sys_script

🛡️

Client Script

sys_script_client

🛡️

Catalog Client Script

catalog_script_client

UI Action

sys_ui_action

🛡️

UI Script

sys_ui_script

🛡️

UI Page

sys_ui_page

🛡️

UI Macro

sys_ui_macro

🛡️

Scripted REST API

sys_ws_operation

🛡️

Fix Script

sys_script_fix

🛡️

Scheduled Job

sysauto_script

Script Action

sysevent_script_action

Email Notification

sysevent_email_action

ACL

sys_security_acl

Transform Script

sys_transform_script

Processor

sys_processor

Service Portal Widget

sp_widget

🛡️

Angular Provider

sp_angular_provider

Portal Header/Footer

sp_header_footer

Portal CSS

sp_css

Angular Template

sp_ng_template

Metadata / XML Definitions

sys_metadata

🛡️

Update XML

sys_update_xml


Install (offline / corporate)

For most users the Setup above (uvx) is all you need. Two corporate-network variants:

  • PyPI reachable, but HTTPS is TLS-inspected (Zscaler / Netskope / corporate MITM) → see pip install (internal network behind TLS inspection) just below.

  • PyPI / uvx fully blocked → see Release zip/exe (local install) further down.

pip install (internal network behind TLS inspection — Zscaler etc.)

Use this when PyPI is reachable but a TLS-inspecting proxy re-signs HTTPS, so installs and runtime calls fail with SSL: CERTIFICATE_VERIFY_FAILED. Registering the proxy's root CA in the OS trust store is not enough — Python (pip, requests, httpx), curl_cffi, and Playwright each ship their own CA bundle (certifi / libcurl / node) and ignore the OS store unless you point them at the cert via env.

1. Get the proxy root CA as a PEM file (ask IT, or export it from the OS keychain). Assume it lands at /etc/ssl/zscaler-root.pem (Windows: C:\certs\zscaler-root.pem).

2. Install — point the installer at the cert:

pip install --cert /etc/ssl/zscaler-root.pem mfa-servicenow-mcp
python -m playwright install chromium     # NODE_EXTRA_CA_CERTS (step 3) covers its download

Prefer uvx? uv can use the OS trust store directly (where the proxy CA is already registered):

UV_NATIVE_TLS=1 uvx --with playwright --from mfa-servicenow-mcp servicenow-mcp --version

3. Runtime — set the CA path in your MCP client env. The non-obvious part: live ServiceNow calls go through curl_cffi (libcurl), which reads CURL_CA_BUNDLEnot REQUESTS_CA_BUNDLE. Set all of them so every layer trusts the proxy:

{
  "mcpServers": {
    "servicenow": {
      "command": "servicenow-mcp",
      "args": [],
      "env": {
        "SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
        "SERVICENOW_AUTH_TYPE": "browser",
        "CURL_CA_BUNDLE": "/etc/ssl/zscaler-root.pem",
        "REQUESTS_CA_BUNDLE": "/etc/ssl/zscaler-root.pem",
        "SSL_CERT_FILE": "/etc/ssl/zscaler-root.pem",
        "NODE_EXTRA_CA_CERTS": "/etc/ssl/zscaler-root.pem"
      }
    }
  }
}

Env var

Layer it fixes

CURL_CA_BUNDLE

curl_cffi / libcurl — the actual ServiceNow API + browser-login probe calls

REQUESTS_CA_BUNDLE

requests (OAuth / API-key token calls, fallback HTTP path)

SSL_CERT_FILE

Python stdlib ssl / httpx / uv

NODE_EXTRA_CA_CERTS

Playwright's Chromium download

PIP_CERT (install only)

pip fetching from PyPI (same as --cert)

In a fully-inspected network the proxy re-signs every host, so the single proxy-root PEM covers all HTTPS. If some hosts bypass the proxy, concatenate the proxy root with certifi's bundle (python -m certifi prints its path) into one PEM and point the env vars at that.

Last resort if you genuinely can't obtain the PEM: pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org mfa-servicenow-mcp skips verification for the install only — it does nothing for runtime ServiceNow calls, which still need CURL_CA_BUNDLE. Prefer the cert path; --trusted-host disables a security control.

Release zip/exe (local install)

Use this path when uvx or PyPI is blocked by corporate security. The release zip ships a PyInstaller-built single-file executable — no Python required, no installer script, no system-cache pollution. The executable auto-detects a ms-playwright/ directory sitting next to itself, so the entire install is "unzip and point your MCP client at it".

1. Download

The executable is on the latest release. The Chromium bundle — only needed when the network also blocks Playwright's own Chromium download — is not re-attached to every release (it's ~150 MB and only changes with Playwright); grab it from the long-lived chromium-bundle release.

Platform

Required (latest release)

Add this too if Chromium download is blocked (chromium-bundle release)

Windows x64

servicenow-mcp-windows-x64-<version>.zip

ms-playwright-chromium-windows-x64.zip

macOS (Intel / Apple Silicon)

servicenow-mcp-macos-<arch>-<version>.zip

ms-playwright-chromium-macos-<arch>.zip

Linux x64

servicenow-mcp-linux-x64-<version>.zip

ms-playwright-chromium-linux-x64.zip

2. Build this folder layout

Pick any directory you control (~/apps/servicenow-mcp/, D:\Tools\servicenow-mcp\, etc. — just keep it stable). *Extract both zips up front** — don't leave the .zip files lying next to the executable. The Chromium zip's extracted directory just has to start with ms-play and contain a `chromium-` subdirectory; whatever name your unzip tool produces is fine:

~/apps/servicenow-mcp/                                  (any directory you choose)
├── servicenow-mcp                                      ← from the platform zip (.exe on Windows)
└── ms-playwright-chromium-linux-x64-1.13.7/            ← default extracted name works
    └── chromium-1185/                                  (one of these is enough)
        └── …

Or, if you'd rather have a clean name, extract into a folder simply called ms-playwright/. Both work — the executable globs for any sibling ms-play* directory at startup and, on finding a chromium-* subdirectory inside, sets PLAYWRIGHT_BROWSERS_PATH to that path for the current process only. It does not write anywhere on disk, does not edit your MCP client config, and does not touch the system-wide Playwright cache (~/.cache/ms-playwright, %LOCALAPPDATA%\ms-playwright, …). If Chromium isn't bundled, Playwright falls back to its own discovery — set PLAYWRIGHT_BROWSERS_PATH in your MCP env yourself or run playwright install chromium somewhere reachable.

3. Sanity-check the binary

# macOS / Linux
~/apps/servicenow-mcp/servicenow-mcp --version

# Windows PowerShell
& "$HOME\apps\servicenow-mcp\servicenow-mcp.exe" --version

If the version prints, you're done with the binary half — every remaining step is just config.

4. Wire it up in your MCP client (copy-paste)

Reuse the same client config as the uvx path in Setup — only command changes to the absolute path of your executable, the env block stays identical. Claude Code example:

{
  "mcpServers": {
    "servicenow": {
      "command": "/home/you/apps/servicenow-mcp/servicenow-mcp",
      "args": [],
      "env": {
        "SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
        "SERVICENOW_AUTH_TYPE": "browser",
        "SERVICENOW_BROWSER_HEADLESS": "false",
        "SERVICENOW_USERNAME": "your-username",
        "SERVICENOW_PASSWORD": "your-password"
      }
    }
  }
}

On Windows replace "command" with "C:/Users/you/apps/servicenow-mcp/servicenow-mcp.exe".

SERVICENOW_USERNAME / SERVICENOW_PASSWORD are optional (MFA login pre-fill). If Chromium lives somewhere other than next to the executable, add "PLAYWRIGHT_BROWSERS_PATH": "/abs/path/to/ms-playwright" to the env block. Codex (TOML), OpenCode, Cursor, VS Code Copilot, Antigravity, Zed snippets: Client Setup Guide.

Chromium fallback (optional)

If you skipped the Chromium zip and Playwright's auto-download is blocked, pre-stage the directory on any machine with Python:

pip install playwright
PLAYWRIGHT_BROWSERS_PATH="$HOME/apps/servicenow-mcp/ms-playwright" python -m playwright install chromium

The result is the same ms-playwright/chromium-*/… layout the bundled zip produces, so the auto-detect picks it up with no extra config.

Windows users: see the Windows Installation Guide for PATH and antivirus notes.


MCP Client Configuration

Recommended: use Setup above. Use the copy-paste configs below when you need to inspect, repair, or hand-manage a client config file.

Each project can connect to a different ServiceNow instance. Set the config in your project directory so each project has its own instance URL and credentials.

Client

Project Config

Global Config

Format

Claude Code

.mcp.json

~/.claude.json

JSON

Cursor

.cursor/mcp.json

Project only

JSON

VS Code (Copilot)

.vscode/mcp.json

Project only

JSON

Zed

Global only

~/.config/zed/settings.json

JSON

OpenAI Codex

.codex/config.toml

~/.codex/config.toml

TOML

OpenCode

opencode.json

Project only

JSON

Windsurf

Global only

~/.codeium/windsurf/mcp_config.json

JSON

Claude Desktop

Global only

claude_desktop_config.json

JSON

AntiGravity

Global only

~/.gemini/antigravity/mcp_config.json

JSON

Docker

Env vars only

Env vars only

Env vars

Copy-paste configs for each client: Client Setup Guide

SERVICENOW_USERNAME / SERVICENOW_PASSWORD are optional — they prefill the MFA login form. On Windows, set these as system environment variables.

Multiple instances (dev / test / prod) in one client

The examples above are single-instance — that stays the default. To switch between several instances from one client, list them in SERVICENOW_INSTANCE_CONFIG (alias → settings) and pick the active one with SERVICENOW_ACTIVE_INSTANCE. Each alias can carry its own credentials (username / password / auth_type / api_key); ${ENV} references keep secrets out of the JSON. The single-instance SERVICENOW_INSTANCE_URL form still works as a fallback.

{
  "mcpServers": {
    "servicenow": {
      "command": "uvx",
      "args": ["mfa-servicenow-mcp@latest"],
      "env": {
        "MCP_TOOL_PACKAGE": "standard",
        "SERVICENOW_ACTIVE_INSTANCE": "dev",
        "SERVICENOW_INSTANCE_CONFIG": "{ \"dev\": { \"url\": \"https://acme-dev.service-now.com\", \"auth_type\": \"browser\", \"username\": \"dev_user\", \"password\": \"${SERVICENOW_DEV_PASSWORD}\", \"allow_writes\": true }, \"test\": { \"url\": \"https://acme-test.service-now.com\", \"auth_type\": \"browser\", \"username\": \"test_user\", \"password\": \"${SERVICENOW_TEST_PASSWORD}\" } }"
      }
    }
  }
}

SERVICENOW_ACTIVE_INSTANCE is where writes default; read tools peek at the others with instance="test", and a single write can be routed to a non-active instance with instance="test" confirm_instance="test" confirm="approve" (guarded, and verified after it lands). Full rules (write routing, gating, comparison, ${ENV}): Multi-Instance Mode.


Authentication

Choose the auth mode based on your ServiceNow environment.

Browser Auth (MFA/SSO) — Default

The Setup command uses browser auth by default. Optional flags:

Flag

Env Variable

Default

Description

--browser-username

SERVICENOW_USERNAME

Prefill login form username

--browser-password

SERVICENOW_PASSWORD

Prefill login form password

--browser-headless

SERVICENOW_BROWSER_HEADLESS

false

Run browser without GUI

--browser-timeout

SERVICENOW_BROWSER_TIMEOUT

120

Login timeout in seconds

--browser-session-ttl

SERVICENOW_BROWSER_SESSION_TTL

30

Session TTL in minutes

--browser-user-data-dir

SERVICENOW_BROWSER_USER_DATA_DIR

Override the Chromium profile path. Rarely needed — see the sandbox note below before setting it.

--browser-probe-path

SERVICENOW_BROWSER_PROBE_PATH

user-specific sys_user lookup when a username is known, otherwise /api/now/table/sys_user_preference?sysparm_limit=1&sysparm_fields=sys_id

Session validation endpoint (avoids 401 on non-admin sessions)

--browser-login-url

SERVICENOW_BROWSER_LOGIN_URL

Custom login page URL

Login sharing across hosts and instances — how it actually works

The server caches two things under ~/.mfa_servicenow_mcp/: the Playwright profile (Chromium SSO cookies) and a session JSON (parsed cookies reused on the next start). Both are scoped per instance + username — files are named profile_<host>_<user> and session_<host>_<user>.json.

That scoping does two things for you automatically, with no configuration:

  • Multiple hosts share one login. Claude Code and Codex on the same machine both resolve ~/.mfa_servicenow_mcp/, so whichever logs in first writes the session and the other reuses it — no second MFA prompt.

  • Different instances / different credentials stay isolated. Each instance+user gets its own profile and session file, so dev and test (or two accounts) never collide. For multiple instances, configure them in SERVICENOW_INSTANCE_CONFIG (JSON) — each alias gets its own scoped cache; you do not manage this with a profile path.

Do not set SERVICENOW_BROWSER_USER_DATA_DIR to "share" logins. It overrides the profile path verbatim — the per-instance scoping is bypassed, so every instance you run is forced into one Chromium profile and their cookies collide. The only legitimate use is a narrow one: a sandboxed host (e.g. Claude Desktop on macOS) that remaps HOME to a container path, so its ~/.mfa_servicenow_mcp/ no longer matches the terminal's. In that single-instance case, point the sandboxed host at the real home path:

# Only when a sandbox remapped HOME, and only for a single-instance host
export SERVICENOW_BROWSER_USER_DATA_DIR="/Users/you/.mfa_servicenow_mcp/profile_acme"

If you run more than one instance, leave this unset and let the per-instance scoping do its job.

Basic Auth

Use this for PDIs or instances without MFA.

uvx --from mfa-servicenow-mcp servicenow-mcp \
  --instance-url "https://your-instance.service-now.com" \
  --auth-type "basic" \
  --username "your_id" \
  --password "your_password"

OAuth

Current CLI support expects OAuth password grant inputs.

uvx --from mfa-servicenow-mcp servicenow-mcp \
  --instance-url "https://your-instance.service-now.com" \
  --auth-type "oauth" \
  --client-id "your_client_id" \
  --client-secret "your_client_secret" \
  --username "your_id" \
  --password "your_password"

If --token-url is omitted, the server defaults to https://<instance>/oauth_token.do.

API Key

uvx --from mfa-servicenow-mcp servicenow-mcp \
  --instance-url "https://your-instance.service-now.com" \
  --auth-type "api_key" \
  --api-key "your_api_key"

Default header: X-ServiceNow-API-Key (customizable with --api-key-header).


Tool Packages

MCP_TOOL_PACKAGE controls which tools the server exposes. Default: standard — no config needed for most users.

WARNING

Any package above standard grants write access and is an advanced option. service_desk, portal_developer, platform_developer, and full all let an AI agent create, update, and delete records — full does so across every domain at once. Most users should stay on the read-only default standard and only opt up to the narrowest write package their task actually requires.

Read-only (safe defaults):

Package

Tools

Description

none

0

Disabled profile for intentionally turning tools off

core

12

Minimal read-only essentials for health, schema, discovery, and key artifact lookups

standard

27

(Default) Read-only across incidents, changes, portal, logs, and source analysis

⚠️ Write-capable (advanced — grants create/update/delete):

Package

Tools

Description

service_desk

29

⚠️ standard + incident and change operational writes

portal_developer

38

⚠️ standard + portal, changeset, script include, and local-sync delivery writes

platform_developer

43

⚠️ standard + workflow, Flow Designer, UI policy, incident/change, and script writes

full

57

⚠️ Most advanced — all write tools across all domains at once

Each server process binds to one active ServiceNow instance for ordinary tools. A write to a different configured instance is possible per call, but only through an explicit, guarded acknowledgement (below) — never a silent switch.

Multi-Instance Mode (comparison + guarded single-call writes)

When you need to compare dev/test/prod or deploy to a chosen one, opt into named instances with SERVICENOW_INSTANCE_CONFIG. SERVICENOW_ACTIVE_INSTANCE is still required.

Two things are global, one is per-instance:

  • Tool surface is global — set once with MCP_TOOL_PACKAGE. Only one instance is ever active per server process, so there is no per-instance tool package.

  • Write permission is per-instance — each alias carries allow_writes. It is enforced at call time against the active instance: a write tool can be loaded but still refused if the active instance has allow_writes: false. Writes are opt-in: omit allow_writes and the instance is read-only.

  • Credentials are per-instance with global fallback — put username / password / api_key (and auth_type) on an alias to override; omit them and the alias inherits the global SERVICENOW_USERNAME / SERVICENOW_PASSWORD / etc. So if every instance shares one login, set it once globally and leave the alias entries credential-free.

Other rules:

  • Read tools accept an instance argument to run a single read against a non-active instance — e.g. sn_query(instance="test", table="incident", ...) or sn_health(instance="test") while dev stays active. Every read tool in your package exposes it in its schema (enum of configured aliases). This is how you peek at another instance's data without restarting.

  • A single write can be routed to a non-active instance, but never silently. Pass instance="test" confirm_instance="test" confirm="approve" (target named twice — as intent and acknowledgement) and the target must have allow_writes=true. Only that one write goes there; the active instance is restored immediately after. A target/confirm mismatch or a read-only target is refused with an explicit message, so a dev/test/prod mix-up cannot land on the wrong instance. The write is then re-read on the target and reported as landed (or WRITE_NOT_LANDED), with target_instance echoed — "success" means the content is confirmed present on the intended instance, not just a 200.

  • list_instances reports configured aliases plus the active one and each one's write flag. compare_instances performs read-only table comparisons across aliases.

  • Switching the default active instance requires restarting the MCP client — it is read once at server startup, not refreshed live. (Per-call instance= routing above does not need a restart.)

Example — shared global login, per-instance write gating:

export MCP_TOOL_PACKAGE=standard
export SERVICENOW_USERNAME=svc_account
export SERVICENOW_PASSWORD='...'
export SERVICENOW_ACTIVE_INSTANCE=dev
export SERVICENOW_INSTANCE_CONFIG='{
  "dev":  { "url": "https://acme-dev.service-now.com",  "allow_writes": true },
  "test": { "url": "https://acme-test.service-now.com", "allow_writes": true },
  "prod": { "url": "https://acme-prod.service-now.com", "allow_writes": false }
}'

To give an instance its own login instead, add the fields to that alias (a ${ENV} reference is resolved, so you can keep secrets out of the JSON):

"prod": { "url": "https://acme.service-now.com", "username": "prod_user", "password": "${SERVICENOW_PROD_PASSWORD}" }

Use compare_instances for dev/test drift checks. For promoting MANY records (especially Service Portal / scoped tables), prefer an Update Set (commit on source, retrieve + commit on target in the UI) over per-record cross-instance writes — it bypasses the per-table/SP ACLs that single Table-API writes hit.

If a tool is not available in your current package, the server tells you which package includes it.

For the full reference (all packages, inheritance details, config syntax): Tool Packages Advanced Guide.


CLI Reference

Server Options

Flag

Env Variable

Default

Description

--instance-url

SERVICENOW_INSTANCE_URL

required

ServiceNow instance URL

--auth-type

SERVICENOW_AUTH_TYPE

basic

Auth mode: basic, oauth, api_key, browser

--tool-package

MCP_TOOL_PACKAGE

standard

Tool package to load

--transport

SERVICENOW_MCP_TRANSPORT

stdio

MCP transport: stdio or http

--http-host

SERVICENOW_MCP_HTTP_HOST

127.0.0.1

Host for --transport http

--http-port

SERVICENOW_MCP_HTTP_PORT

8000

Port for --transport http

--http-path

SERVICENOW_MCP_HTTP_PATH

/mcp

Streamable HTTP endpoint path

--http-allowed-hosts

SERVICENOW_MCP_HTTP_ALLOWED_HOSTS

loopback hosts

Comma-separated Host allowlist for DNS rebinding protection

--http-disable-dns-rebinding-protection

SERVICENOW_MCP_HTTP_DISABLE_DNS_REBINDING_PROTECTION

false

Disable DNS rebinding protection behind trusted network controls

--http-json-response

SERVICENOW_MCP_HTTP_JSON_RESPONSE

false

Return JSON responses instead of SSE streams

--timeout

SERVICENOW_TIMEOUT

30

HTTP request timeout (seconds)

--debug

SERVICENOW_DEBUG

false

Enable debug logging

HTTP transport example:

servicenow-mcp --transport http --http-host 127.0.0.1 --http-port 8000

The MCP endpoint is http://127.0.0.1:8000/mcp; /health returns a lightweight health response.

Basic Auth

Flag

Env Variable

--username

SERVICENOW_USERNAME

--password

SERVICENOW_PASSWORD

OAuth

Flag

Env Variable

--client-id

SERVICENOW_CLIENT_ID

--client-secret

SERVICENOW_CLIENT_SECRET

--token-url

SERVICENOW_TOKEN_URL

--username

SERVICENOW_USERNAME

--password

SERVICENOW_PASSWORD

API Key

Flag

Env Variable

Default

--api-key

SERVICENOW_API_KEY

--api-key-header

SERVICENOW_API_KEY_HEADER

X-ServiceNow-API-Key

Script Execution

Flag

Env Variable

--script-execution-api-resource-path

SCRIPT_EXECUTION_API_RESOURCE_PATH


Keeping Up to Date

uvx caches the last version it downloaded and keeps reusing it. To get a new release you must explicitly refresh — it will NOT update on its own.

# Refresh the uvx cache to the latest PyPI release
uvx --refresh --from mfa-servicenow-mcp servicenow-mcp --version

After refreshing, restart your MCP client (Claude Code, Cursor, etc.) to load the new version.

First browser call downloads Chromium

uvx resolves the latest mfa-servicenow-mcp and Playwright, and a new Playwright release ships a new Chromium build. The first browser tool call then has to fetch ~150 MB of browser binaries — which on a slow link can blow past the MCP host's handshake timeout and surface as:

MCP startup failed: handshaking with MCP server failed: connection closed: initialize response

Avoid it by installing Chromium before the first call (the setup commands above already do this):

uvx --with playwright playwright install chromium

Upgrading

uvx auto-resolves the latest mfa-servicenow-mcp and playwright — there are no versions to bump in your config. To refresh:

# Re-install Chromium in case a newer Playwright shipped a new build, then
# restart your MCP client
uvx --with playwright playwright install chromium

Why we no longer auto-install Chromium inside the MCP server: that download used to run during the first tool call. On a slow link the subprocess outlived the host's handshake deadline and the client reported "connection closed". v1.13.1 changed this — the MCP server now only warns if Chromium is missing. Install it ahead of time with uvx --with playwright playwright install chromium (out-of-band, no handshake timer).


Safety Policy

All mutating tools require an explicit confirm='approve' argument.

Rules:

  1. Mutating tools with prefixes such as create_, update_, delete_, remove_, add_, move_, activate_, deactivate_, commit_, publish_, submit_, approve_, reject_, resolve_, reorder_, and execute_ require confirmation.

  2. You must pass confirm='approve'.

  3. Without that parameter, the server rejects the request before execution.

This policy applies regardless of the selected tool package.

What this gate is — and is not. The server enforces confirm='approve' before any write, but the argument is supplied by the same LLM that issued the call. So the gate is an intent checkpoint that stops accidental or ambiguous mutations — it forces a deliberate, auditable "yes" and a preview hint. It is not a defense against a determined or prompt-injected agent, which can simply include confirm='approve'. Treat it as guardrails, not a security boundary: review what a tool will do before approving, run against a least-privilege ServiceNow account, and do not rely on confirmation alone in adversarial settings. For high-stakes automation, add out-of-band approval.

Write Guards

Beyond the confirm gate, every write runs through deterministic guards that block unsafe writes before they reach ServiceNow. The concurrent-edit and duplicate-create checks run after the confirm gate, so an unconfirmed write never touches the network. Each guard fails open on a denied/failed pre-read — it never blocks a legitimate write just because it couldn't look first. The intent is simple: you should never be able to silently clobber a teammate's change — if someone else touched the record, the write stops and tells you, rather than overwriting and moving on.

Fail-open vs fail-closed. The default (fail-open) favors availability: if the pre-write audit read cannot run (network error, ACL denial, 5xx), the write proceeds. That means the concurrent-edit guard can silently no-op exactly when the instance is unreachable. Security-sensitive deployments can set SERVICENOW_WRITE_GUARDS_FAIL=closed so a guard that could not verify blocks instead — trading availability for the guarantee that a lost-update check never silently passes. Scoped to genuine read failures; a successful read that finds no conflict still proceeds.

Guard

Protects against

Override / toggle

Concurrent edit (G3/G8)

Blindly overwriting a record a different user edited within the last 10 min. Covers sn_write, manage_portal_component, and the manage_* update tools — including manage_script_include, manage_flow_designer, manage_workflow, manage_kb_article, manage_portal_layout, and manage_widget_dependency. Decided by a live remote read of sys_updated_by/sys_updated_on — never the local copy.

SERVICENOW_CONCURRENT_EDIT_GUARD=off; window via SERVICENOW_CONCURRENT_EDIT_WINDOW_MIN (default 10); fail-closed via SERVICENOW_WRITE_GUARDS_FAIL=closed

Source push drift (baseline + update-set HOLD)

Pushing edited source back with update_remote_from_local adds two checks the time-window can't catch: a time-independent compare of the remote's current sys_updated_on against the value recorded at download (catches an overwrite hours or days later), and a live check for the record being held in another user's uncommitted update set.

force=true to push past a detected drift

Duplicate create (G9)

Silently creating a second record with a name that already exists, on tables ServiceNow does not make unique (sys_update_set, wf_workflow, sys_user_group, sys_user).

pass allow_duplicate='true' to create anyway

Flow Designer raw write (G6)

Raw sn_write to sys_hub_* tables that corrupt flow snapshots — forces manage_flow_designer.

Publish-class (G7)

Accidental publish/commit/push — needs a second confirm_publish='approve'.

Cross-instance push

Pushing local source downloaded from instance A into instance B (origin read from _settings.json / _manifest.json).

re-download from the correct instance

Disable the whole layer with SERVICENOW_WRITE_GUARDS=off. In multi-instance mode, every write response also carries an instance_target field (and reads routed elsewhere an instance_source) so the instance a call hit is always visible.

Portal Investigation Safety

Portal investigation tools are conservative by default:

  • search_portal_regex_matches starts with widget-only scanning, linked expansion off, and small default limits.

  • trace_portal_route_targets is the preferred follow-up for compact Widget -> Provider -> route target evidence.

  • download_portal_sources does not pull linked Script Includes or Angular Providers unless explicitly requested.

  • Large portal scans are capped server-side and return warnings when the request exceeds safe defaults.

Pattern matching modes:

Mode

Behavior

auto (default)

Plain strings treated literally, regex-looking patterns remain regex

literal

Always escape the pattern first; safest for route/token strings

regex

Use only when you intentionally need regex operators


Performance Optimizations

The server includes several layers of performance optimization to minimize latency and token usage.

Serialization

  • orjson backend: All JSON serialization uses json_fast (orjson when available, stdlib fallback). 2-4x faster than stdlib json for both loads and dumps.

  • Compact output: Tool responses are serialized without indentation or extra whitespace, saving 20-30% tokens per response.

  • Double-parse avoidance: serialize_tool_output detects already-compact JSON strings and skips re-serialization.

Caching

  • OrderedDict LRU cache: Query results are cached with O(1) eviction using OrderedDict.popitem(). 256 max entries, 30-second TTL (600s for stable metadata: schema/scope/choice tables), thread-safe.

  • Tool schema cache: Pydantic model_json_schema() output is cached per model type, avoiding repeated schema generation.

  • Lazy tool discovery: Only tool modules required by the active MCP_TOOL_PACKAGE are imported at startup. Unused modules are skipped entirely.

Network

  • Browser-grade TLS by default: The HTTP layer routes through curl_cffi with a Chrome impersonation profile (chrome120 by default), so the TLS handshake is byte-for-byte like a real browser — instances behind Cloudflare/Akamai or JA3 bot-detection that reject stock Python requests work with no extra config. Opt out with SERVICENOW_TLS_IMPERSONATE=off.

  • Session keep-alive (browser auth): While you're actively working, a background thread pings the instance every 5 minutes with the same lightweight probe the restore path uses, so ServiceNow's sliding idle timeout never kills the session between tool calls — no more surprise MFA windows after a lunch break. It never opens a browser (a dead session just waits for the next real call to re-auth), stops after 6 hours without real activity, and is tunable via SERVICENOW_SESSION_KEEPALIVE=off, SERVICENOW_SESSION_KEEPALIVE_INTERVAL_S (default 300, min 60), and SERVICENOW_SESSION_KEEPALIVE_MAX_IDLE_S (default 21600).

  • HTTP session pooling: Persistent session with TCP keep-alive and gzip/deflate compression (60-80% payload reduction on large JSON). The stock-requests opt-out path mounts a 20-connection HTTPAdapter.

  • Parallel pagination: sn_query_all fetches the first page sequentially for total count, then retrieves remaining pages concurrently via ThreadPoolExecutor (up to 4 workers).

  • Dynamic page sizing: When remaining records fit in a single page (<=100), the page size is enlarged to avoid extra round-trips.

  • Batch API: sn_batch combines multiple REST sub-requests into a single /api/now/batch POST, with automatic chunking at the 150-request limit.

  • Parallel chunked M2M queries: Widget-to-provider M2M lookups split into 100-ID chunks are executed concurrently rather than sequentially.

Schema & Startup

  • Shallow-copy schema injection: Confirmation schema (confirm='approve') is injected via lightweight dict copy instead of copy.deepcopy, reducing list_tools overhead.

  • No-count optimization: Subsequent pagination pages use sysparm_no_count=true to skip server-side total count computation.

  • Payload safety: Heavy tables (sp_widget, sys_script, etc.) have automatic field clamping and limit restrictions to prevent context window overflow.

Local Source Audit

Download and analyze your entire ServiceNow application locally — no repeated API calls, no context waste.

Step 1: download_app_sources(scope="x_company_app")    → All server-side code + cross-scope deps to disk
Step 2: audit_local_sources(source_root="temp/...")     → Analysis + HTML report

Step 1 runs auto_resolve_deps=True by default: after the in-scope download it scans every .js/.html/.xml file and fetches any referenced sys_script_include, sp_widget, sp_angular_provider, or sys_ui_macro records not already in the bundle — no matter what scope they live in. Pulled deps are saved into the same tree with "is_dependency": true in their _metadata.json, so the audit in Step 2 sees the complete call graph. Set auto_resolve_deps=False if you only want in-scope records.

Tip — pull a whole scope, including global: pass scope="global" to dump every global-scope record, or keep your app scope and let auto_resolve_deps reach into global for the records you actually reference. Either way the local bundle is self-contained, so analysis runs entirely offline against disk.

Incremental Sync

Re-downloading a large app on every run is slow and risks timeouts. Pass incremental=True to fetch only what changed since the last download — like git pull instead of a fresh clone. Works on both download_app_sources and download_portal_sources.

download_app_sources(scope="x_company_app")                      # 1st run: full download
download_app_sources(scope="x_company_app", incremental=True)    # later: changed records only
  • How it works: the first download records each record's sys_updated_on into _sync_meta.json. On an incremental run, every source family queries sys_updated_on >= <latest seen> (server-side timestamps, no clock skew), re-downloads just those records, and leaves unchanged local files untouched.

  • Deletions: timestamp deltas can't see deleted records. Add reconcile_deletions=True to list records present locally but gone on the instance — reported as warnings under deletion_candidates, never deleted automatically.

  • First run / no prior data: falls back to a full download automatically.

  • Run a full (non-incremental) download periodically to stay fully in sync.

Download Safety & Completeness

The download is the source of truth for offline analysis, so it is built to be deterministic and to never look complete when it isn't:

  • Scope auto-resolution. Pass the app namespace (x_company_app), its display name ("My App"), or a sys_scope sys_id — all resolve to the canonical namespace, so the local folder (temp/<instance>/<namespace>/) and every query are identical every run. The resolved value is echoed as scope_resolution.

  • No silent caps. If a source family hits max_records_per_type, it is flagged loudly: a per-family capped: true in source_types, the family in incomplete_types, and a top-level complete: false. A truncated download can never masquerade as a full one.

  • Cross-instance / stale guards. Pushing back (update_remote_from_local) checks the local tree's recorded origin against the connected instance; a resume re-download that keeps a stale local copy preserves the real sync watermark and warns instead of hiding the drift.

  • Relationship metadata at download time. Widget→Angular-Provider edges (_graph.json) and widget→CSS/JS-dependency edges (_dependency_graph.json) are captured from the live M2M tables during the portal download — analysis reads the real graph instead of guessing from code.

  • Transitive dependency depth. Cross-scope deps resolve 2 passes deep by default (conservative). Raise with SERVICENOW_DEP_MAX_DEPTH (clamped to 1–6) to chase longer A→B→C→D chains.

  • One-call graph build. Pass build_graph=True to download_app_sources to run the offline relationship audit right after the download — no extra API cost.

  • Create → local sync nudge. When you create a widget/page on the instance and a local tree exists for that scope, the create response adds a local_out_of_sync message with the exact download_portal_sources(...) command to pull the new record into local. It never writes local files for you.

What Gets Generated

File

Purpose

_audit_report.html

Self-contained dark-theme HTML report — open in browser

_cross_references.json

Who calls who — Script Include chains, GlideRecord table refs

_graph.json

Authoritative widget→Angular Provider edges from the live M2M (not text-guessed)

_dependency_graph.json

Authoritative widget→CSS/JS dependency edges from m2m_sp_widget_dependency

_page_graph.json

Page→widget placements derived locally from sp_instance (no API call)

_orphans.json

Dead code candidates — unreferenced SIs, unused widgets

_execution_order.json

Per-table BR/CS/ACL execution sequence with order numbers

_domain_knowledge.md

Auto-generated app profile — table maps, hub scripts, warnings

_schema/*.json

Field definitions for every referenced table

_sync_meta.json

Per-family sys_updated_on watermark powering incremental sync

Individual Download Tools

Use the orchestrator for a full dump, or download_server_sources for a targeted single-family refresh:

Tool

Sources

download_app_sources

Full app dump (all families + portal + schema + cross-scope deps)

download_portal_sources

Widgets, Angular Providers, linked Script Includes

download_server_sources (families=)

Targeted refresh — script_includes, server_scripts (BR/Client/Catalog Client), ui (Actions/Scripts/Pages/Macros), api (Scripted REST/Processors), security (ACLs, script-only by default), admin (Fix Scripts/Scheduled Jobs/Script Actions/Notifications/Transforms)

download_table_schema

sys_dictionary field definitions

All downloads write full source to disk with zero truncation. Only a summary is returned to the LLM context.


Skills

Tools are raw API calls. Skills are what make your LLM actually useful — verified pipelines with safety gates, rollback, and context-aware sub-agent delegation. MCP server + skills is the complete setup for LLM-driven ServiceNow automation.

4 skills today, more coming with every release.

Tools Only

Tools + Skills

Safety

LLM decides

Gates enforced (snapshot → preview → apply)

Tokens

Source dumps in context

Delegate to sub-agent, summary only

Accuracy

LLM guesses tool order

Verified pipeline

Rollback

Might forget

Snapshot mandatory

Install Skills

# Claude Code
uvx --from mfa-servicenow-mcp servicenow-mcp-skills claude

# OpenAI Codex
uvx --from mfa-servicenow-mcp servicenow-mcp-skills codex

# OpenCode
uvx --from mfa-servicenow-mcp servicenow-mcp-skills opencode

# Antigravity
uvx --from mfa-servicenow-mcp servicenow-mcp-skills antigravity

The installer downloads 24 skill files from this repository's skills/ directory and places them in a project-local LLM directory. No authentication or configuration needed.

Client

Install Path

Auto-Discovery

Claude Code

.claude/commands/servicenow/

/servicenow slash commands appear on next startup

OpenAI Codex

.codex/skills/servicenow/

Skills loaded on next agent session

OpenCode

.opencode/skills/servicenow/

Skills loaded on next session

Antigravity

.gemini/antigravity/skills/servicenow/

Skills activated on next session

How it works: Each skill is a standalone Markdown file with YAML frontmatter (metadata) and pipeline instructions. The LLM client reads these files from the install path and exposes them as callable commands or skill triggers.

Update: Re-run the same install command — it replaces all existing skill files (clean install, no merge).

Remove skills only: delete the skill install directory manually (for example rm -rf .claude/commands/servicenow/).

Skill Categories

Category

Skills

Purpose

analyze/

6

Widget analysis, portal diagnosis, provider audit, dependency mapping, ESC audit, local source audit

fix/

3

Widget patching (staged gates), debugging, code review

manage/

8

Page layout, script includes, source export, app source download, changeset workflow, local sync, workflow management, skill management

deploy/

2

Change request lifecycle, incident triage

explore/

5

Health check, schema discovery, route tracing, flow trigger tracing, ESC catalog flow

Skill Metadata

Each skill includes metadata that helps LLMs optimize execution:

context_cost: low|medium|high    # → high = delegate to sub-agent
safety_level: none|confirm|staged # → staged = mandatory snapshot/preview/apply
delegatable: true|false           # → can run in sub-agent to save context
triggers: ["위젯 분석", "analyze widget"]  # → LLM trigger matching

For the full skill reference, see skills/SKILL.md.

MCP Resources (Built-in Skill Guides)

Skills are also exposed as MCP resources directly from the server — no client-side installation required. Any MCP-compliant client can discover and read them on demand.

# List available skill guides
list_resources → skill://manage/local-sync, skill://manage/app-source-download, ...

# Read a specific guide
read_resource("skill://manage/local-sync") → full pipeline with safety gates

Tools that have a matching skill guide show a → skill://... hint in their description. The guide content is pull-based — zero token cost until the client actually reads it.

Feature

Client-side Skills

MCP Resources

Availability

Requires install command

Built-in, any client

Token cost

Loaded by client

Pull on demand (0 until read)

Discovery

Slash commands / triggers

list_resources

Best for

Power users, slash commands

Universal guidance

Docker

API Key auth only (MFA browser auth requires GUI, not available in containers).

docker run -it --rm \
  -e SERVICENOW_INSTANCE_URL=https://your-instance.service-now.com \
  -e SERVICENOW_AUTH_TYPE=api_key \
  -e SERVICENOW_API_KEY=your-api-key \
  ghcr.io/jshsakura/mfa-servicenow-mcp:latest

See Client Setup Guide for local build options.

Developer Setup

If you want to modify the source locally:

git clone https://github.com/jshsakura/mfa-servicenow-mcp.git
cd mfa-servicenow-mcp

uv venv
uv pip install -e ".[browser,dev]"
uvx --with playwright playwright install chromium

Running Tests

uv run pytest

Linting & Formatting

uv run black src/ tests/
uv run isort src/ tests/
uv run ruff check src/ tests/
uv run mypy src/

Building

uv build

Windows: see Windows Installation Guide


Documentation


  • This repository includes tools consolidated and refactored from earlier internal / legacy ServiceNow MCP implementations. The current surface is organized around bundled manage_* tools (see tool_utils.py).

  • This project is focused on safe, diff-first MCP server use cases: every write goes through confirm + write-guards (concurrent-edit, duplicate-create, publish, Flow Designer), and source edits are diffed against the live remote before they are pushed.


License

Apache License 2.0

Install Server
F
license - not found
C
quality
A
maintenance

Maintenance

Maintainers
1hResponse time
0dRelease cycle
400Releases (12mo)
Commit activity
Issues opened vs closed

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

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