ServiceNow MCP Server
ServiceNow Developer Actions — MCP Server
An MCP server that connects Claude Desktop (or any MCP client) to a ServiceNow instance, exposing guarded developer actions as natural-language tools. Claude can read incidents, inspect CMDB and table schemas, and create records — with every write isolated to a scoped Update Set, restricted to an allowlist of tables, and protected by a dry-run preview.
Built with FastMCP, a typed REST client, and an explicit guardrail layer — the difference between an LLM API wrapper and an agent tool you'd let near a production instance.
Why this project
This demonstrates two skill sets at once:
AI engineering / agent architecture — agentic tool design with safety rails: input schemas (Pydantic), an allowlist + blocklist for writable tables, automatic Update Set scoping, and a
dry_runmode on risky tools so the model previews intent before mutating anything.ServiceNow integration architecture — a resilient Table API client with OAuth2 (with Basic-auth fallback), token refresh on 401, and exponential backoff on 429/5xx, plus CMDB and
sys_dictionaryintrospection tools.
Architecture
Claude Desktop / Claude Code
│ (MCP protocol over stdio)
▼
FastMCP Server ── server.py
│
├── tools (read) get_incidents · query_table · query_records · get_record
│ describe_table · get_cmdb_cis · get_update_set
├── tools (write) create_incident · create_script_include · create_record
│ update_record · execute_background_script ← guarded
│
├── guardrails.py allowlist · blocklist · update-set enforcement · dry-run
▼
ServiceNowClient ── client.py (OAuth2 / Basic · retry · backoff · typed errors)
│ REST (Table API + a companion Scripted REST resource for script execution)
▼
ServiceNow instance (PDI)Tools
Tool | Type | Notes |
| read | Newest-first, active filter |
| read | GlideRecord-style query on any table |
| read | Like |
| read | Fetch a single record by |
| read | Field inspection via |
| read | List CIs from a CMDB class |
| read | List changes currently inside an Update Set, for review |
| write | Allowlisted, Update-Set enforced, |
| write |
|
| write | Any allowlisted table (Business Rules, UI Actions, Flows…) |
| write | Patch a field (e.g. |
| write | Preview-only by default; |
Guardrails
Guardrail | What it does | Enforced by |
Writable allowlist | Only dev-artifact tables ( |
|
Blocklist wins | Security/identity tables ( |
|
Update Set enforcement | Before any write, the server resolves |
|
Dry-run by default |
|
|
Background script two-step |
|
|
Confirmation summary | Every successful write returns table, |
|
Setting up execute_background_script
The Table API has no native endpoint for ad hoc script execution, so this tool POSTs to a small Scripted REST resource you create once on the PDI (Studio → Scripted REST APIs):
// Resource: POST /api/x_snc/mcp_dev/execute (path must match SN_SCRIPT_EXEC_PATH)
(function process(request, response) {
var body = request.body.data;
var result;
try {
result = String(eval(body.script));
} catch (e) {
result = "ERROR: " + e.message;
}
return { result: result };
})(request, response);Scope this resource's ACL to an admin-only role — it is equivalent to background script access and should never be broadly exposed.
Build a Business Rule by chat
"Show me the field name for the incident summary." → Claude calls
describe_table("incident")to confirm before writing code against it."Create a Business Rule called 'Auto-assign VPN incidents' on
incident, before insert, that setsassignment_groupwhenshort_descriptioncontains 'VPN'. Dry run first." → Claude callscreate_record("sys_script", {...}, dry_run=True)and shows you the payload it would insert."Looks right, go ahead." → Claude re-calls with
dry_run=False.ensure_update_set()confirmsMCP_DEV_UPDATE_SET_IDis current, the record is inserted, and you get back table /sys_id/ fields / Update Set in the response."What's in the Update Set so far?" → Claude calls
get_update_set()and lists every change recorded, so you can review before promoting or exporting it.
Setup
git clone <your-repo-url> && cd servicenow-mcp
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
cp .env.example .env # fill in your PDI URL, credentials, and MCP_DEV_UPDATE_SET_ID
pytest # all greenConnect to Claude Desktop
Add to claude_desktop_config.json (Developer → Open App Configuration File), then restart:
{
"mcpServers": {
"servicenow": {
"command": "python",
"args": ["-m", "servicenow_mcp.server"],
"cwd": "/absolute/path/to/servicenow-mcp/src",
"env": {
"SN_URL": "https://dev.service-now.com/",
"SN_USER": "user",
"SN_PASS": "pass",
"SN_UPDATE_SET": "MCP Automated Changes",
"MCP_DEV_UPDATE_SET_ID": "sys_id_of_your_dev_update_set"
}
}
}
}A 🔧 indicator confirms the tools loaded. Then try:
"Show me the 5 newest active incidents" "Describe the cmdb_ci_server table" "Create an incident: VPN gateway down, urgency high — dry run first"
Tech
Python 3.10+ · FastMCP · Pydantic v2 · requests · pytest + responses
Roadmap
OAuth client-credentials grant (service account)
commit_to_source_controltool wrapping Studio's Git integrationRead-through cache for schema/CMDB lookups
Structured audit log of every write the server performs
Author: Pavithra Kumaran S — ServiceNow Senior Consultant exploring AI agent architecture.