servicenow-platform-mcp
servicenow-platform-mcp
servicenow-platform-mcp is an asynchronous Python 3.12+ server that gives AI
tools access to ServiceNow. It uses the Model Context Protocol (MCP) as the
AI and tool access layer. It still uses ServiceNow REST APIs underneath.
Use it to discover schemas, read bounded record data, inspect attachments and Flow Designer records, inspect audit configuration and history, run investigations, analyse fulfilled catalog requests, and perform gated writes.
Contents
Capabilities
Schema discovery. Describe ServiceNow tables, inherited fields, field types, documentation, and dictionary provenance.
Bounded reads. Query records and aggregates with encoded queries, explicit projections, pagination, and display values.
Generic table support. The same tools work with Incident, Problem, REQ/RITM,
sc_task,task_sla, CMDB relations such ascmdb_rel_ci, and custom tables and fields. These are examples, not a hardcoded table list.Attachments. List, inspect, and download attachments. Upload and delete operations are in a separate, explicit tool group.
Flow inspection. Read Flow Designer flows and subflows from their table records, including triggers, inputs, outputs, variables, actions, logic, and warnings.
Audit inspection. Check table and field audit posture and read a masked, date-bounded audit trail.
Investigations. Run registered investigations and explain findings.
Read-only analysis. Compose submitted variables for one fulfilled RITM and read dictionary-confirmed journal history.
Gated writes. Create, update, delete, and write script-bearing records through a preview/apply workflow by default. Service Catalog ordering and state-changing cart actions are also gated.
Choice resolution. Map a human-readable choice label to its stored value.
Code search. Search script-bearing artifacts through ServiceNow Code Search.
Architecture and transport
The server uses MCP SDK v2 (mcp>=2.1.1) and MCPServer. It runs over stdio.
MCP clients launch or connect to the process and call its tools.
ServiceNow calls are asynchronous and use httpx. The server shares one HTTP
pool for its lifetime. Choice, dictionary, and audit configuration use shared
metadata registries and a bounded TTL cache. Records, query results, previews,
attachments, and audit row counts are not metadata-cache entries.
Each operational tool wrapped by @tool_handler receives a generated
correlation ID and returns a serialized JSON response envelope. The envelope
has a stable status, data, and correlation_id shape. The bootstrap
list_tool_packages tool returns the preset-to-group registry directly and is
the only public tool that does not use this envelope.
Install and run
The current project version is 0.11.0. Supported Python versions are 3.12,
3.13, and 3.14. The project uses uv.
git clone https://github.com/Xerrion/servicenow-platform-mcp.git
cd servicenow-platform-mcp
uv sync --group devFor local development, run the installed editable entry point:
uv run servicenow-platform-mcpRun this command with the cloned project as the working directory, unless the package is installed in another managed environment.
The entry point is servicenow_mcp.server:main. To build a distribution:
uv buildThe process uses stdio. Do not start it as an HTTP endpoint for an MCP client.
Configuration and authentication
Settings use environment variables. The process also reads .env and
.env.local from its working directory. With the current pydantic-settings
configuration, later dotenv sources override earlier ones, and process
environment variables override dotenv values. Start the process from the
directory that contains the intended dotenv files. A client that starts the
process with another working directory will not read the files you expect.
Environment variable | Required | Default | Valid range or values | Purpose |
| Yes | None | Must start with lowercase | ServiceNow instance base URL. Trailing |
| Conditional | Empty | Must contain a non-whitespace character when used | API-key authentication. Takes precedence over Basic Auth. |
| Conditional | Empty | Required when API key is empty | Basic Auth username. |
| Conditional | Empty | Required when API key is empty | Basic Auth password. |
| No |
| Preset or comma-separated groups | Selects loaded tool groups. |
| No |
| Any string; | Local environment label and write policy input. |
| No |
|
| Maximum row count for bounded generic and query-oriented tool paths that use this setting. It is not a universal response or egress cap. |
| No |
| Comma-separated table names | Tables that require date-bounded queries. |
| No |
|
| ServiceNow HTTP timeout. |
| No |
|
| Metadata freshness window. |
| No | Empty | String accepted by the Sentry SDK as a DSN | Enables optional Sentry error reporting. |
| No | Empty | Any string | Sentry environment; empty uses |
The instance URL and usable authentication settings are validated at startup,
even when no selected tool will perform a request. URL validation requires the
literal https:// prefix; use a complete instance base URL such as
https://your-instance.service-now.com. Authentication validation requires
either a usable API key or both a username and password.
For API-key authentication, configure a key placeholder only. The server sends
the exact x-sn-apikey header. It does not send an Authorization header in
this mode. If the API key is empty, the server sends HTTP Basic Auth from the
username and password.
Never place a real key or password in this README, a committed configuration file, or a log. Restart the full server process after changing environment variables. Settings are loaded at startup.
MCP client configuration
MCP clients normally start the command below and communicate over stdio. The following generic shape avoids client-specific fields. Use the equivalent stdio configuration fields supported by your client.
API key variant:
{
"command": "uv",
"args": ["run", "servicenow-platform-mcp"],
"env": {
"SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
"SERVICENOW_API_KEY": "${SERVICENOW_API_KEY}",
"MCP_TOOL_PACKAGE": "readonly"
}
}Basic Auth variant:
{
"command": "uv",
"args": ["run", "servicenow-platform-mcp"],
"env": {
"SERVICENOW_INSTANCE_URL": "https://your-instance.service-now.com",
"SERVICENOW_USERNAME": "${SERVICENOW_USERNAME}",
"SERVICENOW_PASSWORD": "${SERVICENOW_PASSWORD}",
"MCP_TOOL_PACKAGE": "readonly"
}
}${...} is a placeholder pattern. Whether a client expands it depends on that
client. Prefer its documented environment forwarding feature, or start the
client from a shell where the variables already exist. Do not commit a file
with substituted secrets.
Tool packages
A tool group is a loader module. A public MCP tool is a callable tool
registered by a group. The record_write group registers two public tools.
The server always registers list_tool_packages. The preset package counts
below include that tool.
Preset | Groups | Public MCP tools | Purpose |
| All 13 groups | 15 | Complete surface, including all writes. |
|
| 11 | Read-only operational and analysis surface. |
|
| 4 | Small read-only core. |
| No groups | 1 | Only |
full includes both attachment and attachment_write. attachment is
read-only. attachment_write is explicit opt-in in custom packages.
readonly and core_readonly exclude attachment writes. analysis is in
full and readonly, but not core_readonly.
Custom packages use comma-separated group names:
MCP_TOOL_PACKAGE=query,describe,record_read,attachment uv run servicenow-platform-mcpValid groups are query, describe, record_write, record_read,
attachment, attachment_write, investigate, resolve_choice,
service_catalog, analysis, audit, flow, and code_search.
list_tool_packages reports the preset-to-group mapping. It does not expand
groups into the public tool names shown below.
Tool reference
All tools return JSON strings. The correlation_id argument is generated by
the server and is not part of the client-facing schema.
Tool | Purpose and important actions | Essential inputs and behavior | Packages |
| Lists preset packages and their groups. | No inputs. Always available. Returns the registry as JSON without the standard response envelope. | All |
| Reads records or aggregates. |
|
|
| Describes fields, tables, or script fields. | Default table description; |
|
| Reads one record by |
|
|
| Creates, updates, or deletes a record. |
|
|
| Applies a record-write preview. |
|
|
| Reads attachment metadata and content. |
|
|
| Uploads or deletes attachments. |
|
|
| Runs or explains investigations. |
|
|
| Resolves choice labels. |
|
|
| Reads catalogs and performs catalog/cart actions. | Actions are listed below. Reads use IDs, filters, and paging. |
|
| Inspects audit posture and history. |
|
|
| Inspects Flow Designer data. |
|
|
| Searches ServiceNow script artifacts. |
|
|
| Composes RITM variables or reads journal history. |
|
|
Use each tool's describe action where available for the runtime action
registry. The public tool schemas are the authoritative input contract.
Schema defaults are empty strings for optional string inputs unless stated otherwise. Important exceptions and effective defaults are:
query:limit=20,offset=0, anddisplay_values=false;describe: emptyactionselects table description,field_limit=25,field_offset=0,verbose=false, andinclude_docs=false;record_write:preview=true;attachment_write:content_type="application/octet-stream";investigate:params="{}";service_catalog:limit=20,offset=0, andtop_level_only=false;code_search:action="search"andlimit=20;analysis: schema valueslimit=0andwindow_days=0select the effective defaults described below;audit: schema valueslimit=0andwindow_days=0selectMAX_ROW_LIMITand 90 days where the action uses them; andflow: schema valueslimit=0andsection_limit=0select effective defaults of 100, with section limits still capped byMAX_ROW_LIMIT.
All other required inputs and action-specific combinations are shown in the
tool table or the detailed sections below. Optional booleans not listed above
default to false.
Analysis details
Fulfilled RITM variables
Call analysis(action="ritm_variables", sys_id="<32-char-sys-id>"). Optional
limit and offset are bounded by MAX_ROW_LIMIT. The tool first confirms
the sc_req_item, then composes submitted answers through:
sc_item_option_mtomfor submitted-answer links;sc_item_optionfor submitted values; anditem_option_newfor variable definitions.
The response contains data.table, data.sys_id, entry_count, and
entries. A resolved entry includes answer and definition IDs, name,
label, type, raw_value, display_value, reference_target,
variable_set, multi_value, masked, and status. Degraded entries for
missing options or definitions are intentionally sparse and identify their
condition through status. The response also contains pagination and
selection metadata.
Variable names and labels that indicate a password, token, secret, credential, API key, or private key cause masking. If either the name or label is missing, the affected answer is masked conservatively.
Variable types 21, list_collector, and List Collector are all treated as
List Collectors. Unmasked List Collector values retain their raw identifiers.
The response includes a warning, sets multi_value=true when a
comma-separated value contains more than one non-empty identifier, and sets
display_value to null. Reference values also keep raw sys_ids and do not
receive generic display-value resolution.
Every successful ritm_variables response contains:
{
"unsupported_features": {
"multi_row_variable_sets": {
"present": false,
"payload_fields_retrieved": false
}
}
}The present value reflects a bounded presence query on
sc_multi_row_question_answer. MRVS payload fields are not retrieved or
decoded. This metadata does not change answer pagination.
An inaccessible or missing submitted option produces an orphaned_option
entry and a warning. An inaccessible or missing definition produces an
inaccessible_definition entry, masked values, and a warning. Duplicate
submitted-answer links are preserved and reported. Row ACLs, field ACLs,
missing definitions, and instance data affect completeness.
Journal history
Call analysis(action="journal_history", table="incident", sys_id="<32-char-sys-id>"). Optional inputs are:
fields_csv: comma-separatedcomments,work_notes, andclose_notes. The default iscomments,work_notes.since:YYYY-MM-DD; it overrideswindow_days.window_days: non-negative integer. The default is 90 days.limitandoffset: bounded pagination. The default limit isMAX_ROW_LIMIT.
Each requested field must exist in the resolved dictionary and have a journal
type. Entries come from sys_journal_field and are ordered by
sys_created_on, then sys_id, ascending. The response reports the effective
date window, fields, entries, selection, pagination, and an ACL/retention
warning.
This is journal history. It is different from audit(action="history"),
which reads field changes from sys_audit.
Query, selection, pagination, and schema discovery
query has three modes: exact-record mode when sys_id is set, aggregate
mode when aggregate is set, and list mode otherwise. List-mode calls require
an explicit fields projection. Use
fields="*" only when all masked fields are intentional. sys_id is always
included. Exact-record mode defaults to sys_id,sys_updated_on and accepts an
explicit projection or *.
query and code_search report the effective row cap in pagination.limit,
without a redundant limit-cap warning. Query offsets, totals, and selection
metadata remain available for continuing bounded reads. Empty warning lists
are omitted from response envelopes; non-empty warnings are preserved.
code_search defaults to extended_matching=false to avoid additional
context fields from the search group's configuration. Set
extended_matching=true to request that context. Search result fields and
platform metadata are otherwise passed through unchanged. Its pagination
reports only the effective limit; it does not imply offset support or a known
total. Keep platform completeness signals and narrow the search when needed.
record_read with empty fields returns compact identity and update fields
plus all discovered script-bearing fields. fields="*" returns the full
masked record. record_read always includes script_fields and sys_id.
describe walks sys_db_object.super_class child-first. Child declarations
override ancestor declarations. Each field includes inherited_from where
the response shape supports provenance. Empty fields returns an alphabetical
page of 25 fields by default. field_offset continues the page and
field_limit accepts 1-100. fields="*" requests all fields. Use
action=list_script_fields to return discovered script fields and their
resolved chain.
The default and verbose describe shapes include a choice_count. Choice
counts are read from the queried table first and then from each inherited
field's declaring table when needed. include_docs=true adds matching
sys_documentation records for the selected fields, with the same fallback to
the declaring table. Choice-count failures produce a warning and zero counts;
documentation failures follow normal tool error handling.
Choice, dictionary, and audit-configuration caches use
METADATA_CACHE_TTL_SECONDS. Each metadata cache is limited to 1,000 entries,
uses least-recently-used eviction, shares one in-flight load for the same key,
and permits different keys to load concurrently. Expired entries are reloaded
before the requesting call returns. These caches do not store records, query
results, previews, attachments, or audit row counts.
Encoded queries are passed to ServiceNow. Identifiers are validated and query
safety caps the effective limit at MAX_ROW_LIMIT. Tables in
LARGE_TABLE_NAMES_CSV require a structural date constraint such as
sys_created_on>=YYYY-MM-DD. Aggregate requests use the Aggregate API.
MAX_ROW_LIMIT applies only to bounded generic and query-oriented paths that
use it. It is not a universal response or egress cap. Service Catalog actions
have action-specific limits. The attachment list has a fixed maximum of 100
metadata records and no caller-controlled offset or pagination.
Successful bounded reads can include selection and pagination metadata.
Use next_offset, truncated, total, and returned-field metadata to
continue a read. A tool may add warnings when a platform or local limit caps a
request.
Writes and safety
The policy layer blocks these tables:
sys_user_has_password, oauth_credential, oauth_entity, sys_certificate,
sys_ssh_key, sys_credentials, discovery_credentials, and
sys_user_token.
Key-name masking for names containing password, token, secret, credential,
api_key, or private_key applies only on specific record-oriented paths that
call the local masking helpers. It is not a global output filter. Query
aggregate mode returns Stats API results directly, without local field-value
masking. Code Search, Flow, Service Catalog, and other arbitrary payload
surfaces are not universally masked. Do not group or aggregate sensitive
fields. Enforce ServiceNow field ACLs as the primary control. Audit rows use
the audit field name to mask old and new values.
Writes are blocked when SERVICENOW_ENV is prod or production. This local
gate does not replace ServiceNow ACLs. ServiceNow remains the authority for
authorization.
record_write defaults to preview mode. A preview returns a single-use
preview_token and a masked preview. record_apply consumes the token and
re-checks policy before applying it. Tokens expire after five minutes and are
single-use, are held only in the server process that created them, and are
consumed before the application attempt. A failed attempt cannot be retried
with the same token. Set preview=false only when an immediate write is
appropriate.
record_write.data is the only field-value input. Supply a JSON string such as
{"script":"run();\n","active":true}. Include the complete value for each
field you change; omitted fields stay unchanged on update. Multiple script
fields can be changed in one payload. Use record_read or
describe(action="list_script_fields", table=...) to discover field names.
The server does not read local script files.
The complete UTF-8 JSON input is limited to 256 KiB (262144 bytes), including field names and JSON escaping. Before staging or writing, the server queries dictionary types for supplied fields only, resolving inherited fields child-first. Values for XML fields must be strings containing well-formed XML; empty, null, and malformed values are rejected. Metadata request errors block the write. Dictionary visibility depends on ServiceNow ACLs; fields not returned by the dictionary cannot receive local type validation. These checks do not validate script syntax or replace ServiceNow authorization.
Attachment upload and delete are in attachment_write, which is separate from
read-only attachment and is gated again at runtime. Attachment transfer size
is limited to 10 MiB.
Service Catalog write actions are order_now, add_to_cart, cart_submit,
and cart_checkout. They apply write gates to the relevant request or cart
table. A read of fulfilled RITM variables through analysis is read-only and
does not order or change a catalog item.
For a true read-only deployment, combine all of the following:
MCP_TOOL_PACKAGE=readonly, or a smaller custom package containing only read groups;GET-only ServiceNow REST API resources;
read-only table and field ACLs; and
a production environment label so local writes are blocked.
Package selection is not a replacement for ServiceNow authorization.
ServiceNow permissions
Authentication and authorization are separate controls. An API key must be permitted to use the required REST API resources. Table ACLs and field ACLs then control the records and fields that those resources can return or change.
The registered tools use these ServiceNow APIs and resources as applicable. API titles match the local OpenAPI specifications:
API title | Paths and methods used by registered tools | Use |
Table API |
| Query, describe metadata reads, record reads and writes, Flow inspection, analysis composition, and attachment-by-name metadata lookup. |
Aggregate API |
| Query aggregates and audit positive-control counts. |
Attachment API |
| Attachment metadata, downloads, uploads, and deletes. |
Code Search |
|
|
Service Catalog API | GET under | Catalog, item, variable, cart, and order actions. |
For a read-only package, allow GET on the Table, Aggregate, Attachment metadata/download, Code Search, and read-only Service Catalog paths used by the selected tools. For writes, add only the POST, PATCH, and DELETE resource permissions needed by the selected Table, Attachment, and Service Catalog actions. The client retains methods for some APIs that no registered tool uses; those endpoints are not required for the tool surface documented here. The exact API-key REST-resource policy depends on the instance and must be configured in ServiceNow.
Analysis needs Table API access and applicable read ACLs for sc_req_item,
sc_item_option_mtom, sc_item_option, item_option_new,
sc_multi_row_question_answer, sys_journal_field, sys_db_object, and
sys_dictionary. General tools also need read access to the target tables and
their selected fields. Flow inspection uses Table API records. It does not
use Workflow Studio APIs or undocumented processflow endpoints.
Dynamic table access and instance-specific ACL design must be configured in ServiceNow. The MCP package cannot grant access that the instance denies.
Flow, audit, investigations, and Service Catalog
Flow
flow supports contract, inspect, find_by_table, decode_values,
list_triggers, and describe. It reads both V1 and V2 Flow Designer tables.
It joins V2 record-trigger conditions through the remote trigger ID. The
decoder handles gzip plus Base64 plus JSON values blobs. A decode failure is
reported on the affected node while the enclosing inspection can still
succeed.
The implementation deliberately does not call undocumented
/api/now/processflow/* endpoints. It also skips sys_hub_flow_snapshot, an
opaque compiled cache.
Audit
audit supports check_field, check_fields, check_table, history, and
describe. Audit reads use a default 90-day window because sys_audit is a
large table. since on history overrides window_days.
Verdicts include audited, not_audited_field_flag,
not_audited_table_flag, audited_but_inactive, and inconclusive. Field
configuration is resolved child-first. The no_audit=true attribute vetoes a
field audit flag. Positive-control counts distinguish configured but inactive
fields from cases that cannot be determined.
Investigations
investigate supports run, explain, and describe. The seven registered
modules are:
stale_automations- finds unused or stale automation rules;deprecated_apis- detects deprecated API usage;table_health- analyses table structure and data quality;acl_conflicts- finds conflicting ACL rules;error_analysis- analyses error patterns;slow_transactions- identifies slow transactions; andperformance_bottlenecks- identifies performance issues.
Service Catalog
service_catalog supports catalogs_list, catalog_get,
categories_list, category_get, items_list, item_get,
item_variables, order_now, add_to_cart, cart_get, cart_submit, and
cart_checkout. List actions support text, catalog/category filters, limits,
offsets, and top-level category selection. order_now and cart actions that
change state are write-gated. This surface is separate from read-only
inspection of fulfilled RITM answers through analysis.
Attachments
The read-only attachment tool supports:
list- list metadata for a parent table and record;get- return masked metadata for one attachment;download- return masked metadata and Base64 content; anddownload_by_name- resolve metadata by parent and file name, then download the earliest-created match when multiple rows match.
Reads validate parent table access and attachment metadata. Downloads check the
declared and received size. The maximum supported transfer size is 10 MiB.
Attachment content is returned as data and is not content-classified by MCP.
attachment(action="list") returns at most 100 metadata records. It has no
caller-controlled offset or pagination, so do not assume that a list is
complete beyond that fixed bound.
The separate attachment_write tool supports upload and delete. Uploads
use Base64 content and a default MIME type of
application/octet-stream. Among presets, upload and delete are available
only in full; a custom package can opt in with attachment_write. Both
actions are subject to write gates and ServiceNow authorization.
Responses, errors, and observability
The standard success envelope is:
{
"correlation_id": "generated-id",
"status": "success",
"data": {}
}Depending on the tool, the envelope can also contain pagination, selection,
and warnings. An error envelope has status: "error", data: null, and an
error object with a message field:
{
"correlation_id": "generated-id",
"status": "error",
"data": null,
"error": {"message": "reason"}
}@tool_handler generates correlation IDs, records redacted tool context for
Sentry, and routes exceptions through safe tool handling. Tool functions do
not leak Python exceptions to MCP callers. If Sentry is enabled, unexpected
exceptions are captured before the error envelope is returned.
Troubleshooting
Missing instance URL: set
SERVICENOW_INSTANCE_URLto a full HTTPS URL. Startup validation errors list setting names and constraints without input values.401
User Not Authenticated: verify the API key or Basic Auth values, the exact instance URL, and the authentication policy on the instance. API key mode usesx-sn-apikey.API key policy failure: check API-key REST-resource permissions. A valid key does not automatically grant table or field access.
Table or field denial: check the target table ACL and field ACL. The selected MCP package only controls which tools are exposed.
Changed environment values have no effect: restart the full MCP process.
-32000: this can be a client-level wrapper. Inspect the MCP client's stderr and the underlying server process error before choosing a cause.
Development and verification
uv sync --group dev
uv run pytest
uv run pytest tests/test_client.py
uv run pytest -m integration
uv run ruff check .
uv run ruff format --check .
uv run mypy src/
uv buildIntegration tests use a live instance and require credentials in .env.local.
Do not use production credentials for tests.
Source uses a src/servicenow_mcp/ layout. Tool groups live in
src/servicenow_mcp/tools/. Tests live in tests/ and use pytest,
pytest-asyncio, and respx for HTTP mocking. The default test command
excludes tests marked integration.
Known limitations and non-goals
Custom fields require dictionary discovery and suitable ServiceNow ACLs.
RITM reference and List Collector answers retain raw sys_ids. Generic display-value resolution is not provided.
List Collector display values are not fabricated from raw identifiers.
MRVS payload fields are not retrieved or decoded.
RITM results can contain orphaned options or inaccessible definitions.
Journal and audit completeness depends on row ACLs, field ACLs, and instance retention.
Flow inspection reads documented table records and does not inspect opaque compiled snapshots.
Attachment content is not classified by MCP. Treat downloaded content as untrusted.
ServiceNow instance configuration, API-key resource policy, ACLs, and row visibility can limit results beyond the local tool limits.
Security
Use least-privilege API keys and ServiceNow ACLs. Expose only the tool groups
that operators need. Prefer readonly or a smaller custom package for read
workflows. Keep write operations in a non-production environment until they
are understood and tested.
Do not commit .env, .env.local, credentials, API keys, or generated files
that contain sensitive values. Do not log secrets, tokens, passwords, or PII.
Review attachment content and submitted catalog values before forwarding them
to other systems.
Contributing and license
Open an issue for a bug or feature request: https://github.com/Xerrion/servicenow-platform-mcp/issues.
The project is licensed under the MIT License.