MCP Client Configuration¶
Detailed setup for each MCP client. All clients use the same MCP server — only the config format differs.
Start here:
uvxis the default install on every platform. Ifuvxwon't run — Windows Smart App Control is the usual reason — fall back topip. Those are the two install paths.
Before You Start¶
Use uvx by default. It keeps install and client config consistent across macOS, Linux, and Windows.
1. Install uv¶
macOS / Linux:
Windows PowerShell:
2. Fetch the server + install Chromium¶
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
The first command pre-fetches and verifies the server in the exact --with playwright env the client uses, so the first start is instant. The second downloads Chromium; uvx reuses a matching Chromium already in the standard cache.
If uvx is blocked — pip¶
Windows Smart App Control stops uvx from running at all: uvx unpacks an unsigned temporary executable on every run, and SAC blocks it. If uvx stopped working right after a Windows update, this is almost certainly why. Install with pip instead:
A Python from the python.org installer (signed, 3.10+) passes SAC as-is. Start the server with python -m servicenow_mcp — not the servicenow-mcp console script, which is an unsigned .exe shim pip generates and SAC blocks too.
On macOS/Linux the one pip caveat is that Homebrew and distro Pythons refuse global installs under PEP 668 (
externally-managed-environment). Use the python.org installer, or just stay on uvx.
If PyPI itself is unreachable — a corporate network that blocks the package index — neither path can fetch anything. Ask IT to allowlist pypi.org and files.pythonhosted.org, or to mirror the package on an internal index you can point at with pip install --index-url.
Windows users: see Windows Installation Guide for step-by-step details and proxy/antivirus notes.
3. Add the server to your MCP client config¶
Add an entry to your client's config file (no installer command needed). The env block is identical no matter how you installed — only command/args follow the path you picked above:
| Install | command |
args |
|---|---|---|
| uvx (default) | uvx |
["--with","playwright","--from","mfa-servicenow-mcp","servicenow-mcp"] |
| pip (uvx blocked) | python |
["-m","servicenow_mcp"] |
Every per-client example below shows the uvx form. On pip, swap those two keys and leave everything else untouched.
{
"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"
}
}
}
}
Per-client file paths and formats (Codex TOML, etc.) are below; restart the client afterward.
Quick Test¶
Verify the server starts before configuring your client:
uvx --with playwright --from mfa-servicenow-mcp servicenow-mcp \
--instance-url "https://your-instance.service-now.com" \
--auth-type "browser" \
--browser-headless "false"
# pip install: replace the first line with
python -m servicenow_mcp \
--instance-url "https://your-instance.service-now.com" \
--auth-type "browser" \
--browser-headless "false"
If the server starts and a browser window opens for login, you're ready to configure your client below.
Configuration Guide¶
argsis for the package only — instance URL, auth, credentials all go inenv(orenvironment). This keeps args clean and makes it easy to swap instances per project.Project-local recommended: Use project-scoped config so each project can connect to a different ServiceNow instance.
Everything below varies only inside env. command/args stay exactly as they were in step 3, whichever install path you took.
Profiles — start here¶
If you touch more than one ServiceNow instance, configure profiles rather than running a server per instance. Name each environment as an alias and pick the active one:
"env": {
"MCP_TOOL_PACKAGE": "standard",
"SERVICENOW_ACTIVE_INSTANCE": "dev",
"SERVICENOW_INSTANCE_CONFIG": "{ \"dev\": { \"url\": \"https://acme-dev.service-now.com\", \"auth_type\": \"browser\", \"allow_writes\": true }, \"test\": { \"url\": \"https://acme-test.service-now.com\", \"auth_type\": \"browser\", \"allow_writes\": true }, \"prod\": { \"url\": \"https://acme-prod.service-now.com\", \"auth_type\": \"browser\" } }"
}
That one block replaces SERVICENOW_INSTANCE_URL, and it is what makes the rest of this guide work:
- Production is protected by omission. An alias without
allow_writesis read-only.prodabove cannot be written to at all — a forgotten flag can never enable a production write. - Reach another instance without restarting. Read tools take an
instanceargument:sn_query(instance="prod", …)whiledevstays active. - Compare environments directly.
compare_instancesdiffs the same record across two aliases;list_instancesreports every alias and its write flag. - One browser login. The session is shared across aliases instead of one login per server process.
- Writes to a non-active instance are guarded, never silent — see Multi-Instance Mode for the routing rules, the
confirm_instancegate, and${ENV}secret references.
Single instance¶
One instance only? Skip profiles entirely — two variables is the whole configuration:
"env": {
"SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
"SERVICENOW_AUTH_TYPE": "browser"
}
This form keeps working and is not deprecated; it is simply the degenerate case of the profile setup above.
One connection or several?¶
Profiles put every instance behind one client connection, which is what almost everyone wants. If instead you need connections that are visually distinct in the client UI — a separate snow-dev and snow-prd entry — see Naming multiple server entries. That trades away compare_instances, the shared login, and the allow_writes gate, so choose it only for the UI separation.
Streamable HTTP¶
The default transport is stdio. For remote MCP clients or a local HTTP bridge, start the server with Streamable HTTP:
servicenow-mcp --transport http --http-host 127.0.0.1 --http-port 8000
# pip install: python -m 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 status response. Keep the default loopback host unless the server is behind trusted network controls.
Multi-Instance Mode (comparison + guarded single-call writes)¶
Configure named instances (e.g. dev / test / prod aliases) with SERVICENOW_INSTANCE_CONFIG so one session can both compare across environments AND deploy to a chosen one — without switching the active instance or restarting the server. Route a single call with the instance=<alias> argument:
- Read-only calls route freely:
instance=testreadstestwhiledevstays active. - Writes to a non-active instance are allowed but never silent. The one call must name the target and approve it —
instance=test confirm_instance=test confirm=approve— and the target must haveallow_writes=true. Only that one write is routed 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 verified on the target. The result echoes
target_instanceand alandedverdict: the tool re-reads the pushed fields on the target and returnsWRITE_NOT_LANDEDif the content did not persist (e.g. ansp_*Service Portal field silently dropped). "Success" means the content is confirmed present on the intended instance — not merely that the request returned 200. compare_instancescompares records across aliases (read-only);list_instancesreports the configured aliases and each one's write flag.- Keep
prodatallow_writes=falseunless you deliberately intend production writes — then a forgotten flag can never enable one.
For promoting MANY records (especially Service Portal / scoped tables), prefer an Update Set — commit on the source, retrieve + commit on the target in the UI — over per-record cross-instance writes; it bypasses the per-table/SP ACLs that single Table-API writes hit.
SERVICENOW_ACTIVE_INSTANCE=dev
SERVICENOW_INSTANCE_CONFIG='{
"dev": { "url": "https://acme-dev.service-now.com", "auth_type": "browser", "allow_writes": true },
"test": { "url": "https://acme-test.service-now.com", "auth_type": "browser", "allow_writes": true },
"prod": { "url": "https://acme-prod.service-now.com", "auth_type": "browser", "allow_writes": false }
}'
Per-instance credentials, in an MCP client env block (each alias can carry its own username / password / auth_type / api_key; ${ENV} keeps secrets out of the JSON; the single-instance SERVICENOW_INSTANCE_URL form still works as a fallback):
{
"mcpServers": {
"servicenow": {
"command": "uvx",
"args": ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp"],
"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}\" } }"
}
}
}
}
Example comparison:
{
"source": "dev",
"target": "test",
"table": "sys_script_include",
"key_field": "api_name",
"fields": "api_name,name,active,script",
"query": "sys_scope.scope=x_company_app"
}
For a single write against a non-active instance, use the guarded instance=<alias> confirm_instance=<alias> confirm=approve routing above. For promoting MANY records, prefer an Update Set over per-record cross-instance writes.
Naming multiple server entries (--server-name)¶
This is a different topology from the multi-instance mode above. Multi-instance = one connection that can reach several instances. This section = several separate connections, one process per instance, each pinned to its own instance — worth it only when you want dev/stg/prd visibly split apart in the client UI.
The catch: every entry advertises itself as ServiceNow by default, so the client disambiguates them by load order — mcp_servicenow, mcp_servicenow2, mcp_servicenow3. That numbering can shift between restarts, which makes it untrustworthy for telling which connection is production. Give each one a name with --server-name:
{
"mcpServers": {
"snow-dev": {
"command": "uvx",
"args": ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp", "--server-name", "snow-dev"],
"env": {
"SERVICENOW_INSTANCE_URL": "https://acme-dev.service-now.com",
"SERVICENOW_AUTH_TYPE": "browser"
}
},
"snow-prd": {
"command": "uvx",
"args": ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp", "--server-name", "snow-prd"],
"env": {
"SERVICENOW_INSTANCE_URL": "https://acme.service-now.com",
"SERVICENOW_AUTH_TYPE": "browser",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
}
Tool names are then pinned to mcp_snow-dev_* / mcp_snow-prd_*. SERVICENOW_MCP_SERVER_NAME does the same thing as an env var, and the flag wins if both are set. Unset, the name stays ServiceNow, so existing configs keep working.
Prefer profiles when you can. For moving between instances inside one connection, Multi-Instance Mode is the recommended approach: only it gives you compare_instances, a single shared browser login, and the per-alias allow_writes gate. Separate processes get none of that — each one only knows its own instance, logs in on its own, and the tool package is the only thing standing between you and a production write.
Claude Desktop¶
| Scope | Path |
|---|---|
| Global | ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) |
| Global | %APPDATA%\Claude\claude_desktop_config.json (Windows) |
{
"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",
"SERVICENOW_BROWSER_HEADLESS": "false",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
}
Claude Desktop does not support project-local config. Use Claude Code for per-project setup.
Claude Code¶
| Scope | Path |
|---|---|
| Global | ~/.claude.json |
| Project | .mcp.json in project root |
{
"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",
"SERVICENOW_BROWSER_HEADLESS": "false",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
}
Zed¶
| Scope | Path |
|---|---|
| Global | ~/.config/zed/settings.json |
Add via Settings > MCP Servers in Zed:
{
"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",
"SERVICENOW_BROWSER_HEADLESS": "false",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
OpenAI Codex (CLI & App)¶
Both Codex CLI (codex command) and Codex App (chatgpt.com/codex) read from the same config.toml.
| Scope | Path | Note |
|---|---|---|
| Global | ~/.codex/config.toml |
Shared across all projects |
| Project | .codex/config.toml |
Overrides global (trusted projects only) |
[mcp_servers.servicenow]
command = "uvx"
args = ["--with", "playwright", "--from", "mfa-servicenow-mcp", "servicenow-mcp"]
enabled = true
[mcp_servers.servicenow.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"
MCP_TOOL_PACKAGE = "standard"
# Login is shared across hosts automatically (scoped per instance + user under
# ~/.mfa_servicenow_mcp). Only set SERVICENOW_BROWSER_USER_DATA_DIR if a sandboxed
# host remapped HOME — see the README "Login sharing" note. Do NOT set it when you
# run multiple instances; it collapses them into one Chromium profile.
OpenCode¶
| Scope | Path |
|---|---|
| Project | opencode.json in project root |
OpenCode uses
environment(notenv).
{
"$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",
"SERVICENOW_BROWSER_HEADLESS": "false",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
}
AntiGravity¶
| Scope | Path |
|---|---|
| Global | ~/.gemini/antigravity/mcp_config.json (macOS/Linux) |
| Global | %USERPROFILE%\.gemini\antigravity\mcp_config.json (Windows) |
Edit via agent panel: ... > Manage MCP Servers > View raw config. Click Refresh after saving.
{
"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",
"SERVICENOW_BROWSER_HEADLESS": "false",
"SERVICENOW_USERNAME": "your-username",
"SERVICENOW_PASSWORD": "your-password",
"MCP_TOOL_PACKAGE": "standard"
}
}
}
}
Docker (API Key only)¶
Browser auth (MFA/SSO) requires a GUI browser and does not work inside containers.