ServiceNow Managed MCP Server
The ServiceNow managed MCP server lets agents work with a ServiceNow instance: search, create, update, and transition incidents, append work notes, order items from the service catalog, search the knowledge base, look up users and assignment groups, search the configuration management database (CMDB) for configuration items, discover table schema and field choices, and read allow-listed tables.
After reading this page, you will be able to:
-
Configure the ServiceNow managed MCP server with Basic authentication, OAuth client credentials, or User OAuth
-
Use projections and hierarchies to shape responses and gate table reads
-
Manage incidents, order service catalog items, and read allow-listed records from the Inspector or an agent
What this MCP server does
Wraps the ServiceNow REST Table API, plus the Service Catalog API for catalog submissions. The MCP is vendor-generic: ServiceNow records are returned as opaque JSON objects (their field set varies by instance), and the agent learns each table’s shape and valid field values at runtime through the discovery tools (get_table_schema, get_field_choices).
Customer-specific structure is supplied as configuration, not code:
-
Projections set the default field list returned per table (a ServiceNow incident carries about 100 fields, so projections keep responses small) and gate which tables the generic
query_recordstool may read. -
Hierarchies declare dependent-field chains (for example, service area, then category, then subcategory). The agent reads the chain order with
describe_field_hierarchiesand resolves each field’s valid values at runtime withget_field_choices. No field values are stored in the configuration.
It is not a general SQL interface, and it does not delete records. Writes are limited to the typed incident tools and the two catalog submission tools; everything else reads.
Schema discovery and write verification together reduce the risk of a write that looks like it succeeded but changed nothing. The get_table_schema tool reports which fields the data dictionary marks read-only or filtered, so an agent can avoid setting them. The incident write tools then compare the values they sent against the record ServiceNow returned from the write, and submit_record_producer re-reads the record it created. Both attach a report of what the instance held. See Confirm what a write stored.
Prerequisites
Before you create the server, make sure you have:
-
A ServiceNow instance and its URL (for example,
https://acme.service-now.com). -
A ServiceNow service account, or an OAuth client, with the roles your workflows need (typically
itilfor incidents,knowledgefor the knowledge base, and read access tosys_dictionaryandsys_choicefor schema discovery). For the catalog tools, add read access tosc_cat_itemanditem_option_new. Without it, a catalog search skips its option-value pass, a request form comes back partial, and the server refuses a submission rather than sending it blind. -
For User-OAuth mode: an OAuth Provider configured in Redpanda Agentic Data Plane. See Configure an OAuth Provider.
Choose an authentication method
The MCP authenticates to ServiceNow as a single principal; it does not impersonate end-users. When creating an incident, the agent passes the requesting user’s sys_id in caller_id. Pick one of three methods:
-
Basic authentication (simplest): A dedicated ServiceNow user (for example,
svc_redpanda) and its password. The username is plaintext; the password lives in the Agentic Data Plane secret store. -
OAuth client credentials (service account): An OAuth API client registered under System OAuth > Application Registry. ServiceNow’s token endpoint is
https://<instance>.service-now.com/oauth_token.do. -
User OAuth: Per-user delegation through an OAuth Provider. ServiceNow’s recommended integration model is the service account, so prefer Basic authentication or OAuth client credentials unless you specifically need per-user attribution.
Configure
Create a new ServiceNow MCP server in Agentic Data Plane:
-
Open MCP Servers > Add MCP server.
-
Pick
ServiceNowfrom the marketplace picker. -
Fill in identity fields (
name,description). -
In the ServiceNow configuration form:
| Field | Notes |
|---|---|
|
Base URL of your ServiceNow instance (for example, |
|
|
|
|
|
|
|
|
|
Per-table default field lists and the read allow-list for |
|
Dependent-field chains the agent resolves at runtime. |
|
The subset of tool names this server exposes, matched exactly and case-sensitively (for example, |
|
Companion fields an incident must be written with when it moves to a named state, for example a close code when closing. Give each entry a |
|
The incident column that |
+ . Click Create.
Configure from the CLI
For a managed server, set the authentication method inside the --managed.config JSON. The auth field is required.
-
Basic authentication
-
OAuth client credentials
-
User OAuth
rpk ai mcp-server create acme-servicenow --enabled --managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.servicenow.v1.ServiceNowMCPConfig",
"instance_url": "https://acme.service-now.com",
"basic_auth": {
"username": "svc_redpanda",
"password_secret_ref": "SERVICENOW_PASSWORD"
},
"projections": [
{
"table": "incident",
"default_fields": ["number", "short_description", "state", "priority", "assignment_group", "caller_id"],
"queryable": true
},
{ "table": "change_request", "queryable": true }
],
"hierarchies": [
{
"table": "incident",
"name": "service",
"fields": ["u_service_area", "u_service_category", "u_service_subcategory"]
}
]
}'
rpk ai mcp-server create acme-servicenow-oauth --enabled --managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.servicenow.v1.ServiceNowMCPConfig",
"instance_url": "https://acme.service-now.com",
"oauth": {
"client_id": "<oauth-client-id>",
"client_secret_ref": "SERVICENOW_CLIENT_SECRET",
"token_url": "https://acme.service-now.com/oauth_token.do"
}
}'
rpk ai mcp-server create acme-servicenow-user --enabled --managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.servicenow.v1.ServiceNowMCPConfig",
"instance_url": "https://acme.service-now.com",
"user_oauth": {
"provider_name": "servicenow-prod"
}
}'
Replace <oauth-client-id> with the client ID from your ServiceNow OAuth application registry, and servicenow-prod with the name of the OAuth Provider you configured.
Tools
The ServiceNow MCP exposes tools across incidents, the service catalog, knowledge, schema discovery, lookups, and generic reads:
| Tool | Description |
|---|---|
|
Search incidents with a ServiceNow encoded query. |
|
Fetch one incident by |
|
Create an incident (typed fields plus an |
|
Update assignment, notes, urgency, impact, or resolution details on an incident. It can’t change the state: use |
|
Move an incident to a state named in words, such as |
|
Append a work note (internal) or comment (customer-visible) to an incident. |
|
Full-text search over published knowledge articles. |
|
Fetch one knowledge article by |
|
Find orderable service catalog items from the words a user used, matching both the item’s own text and the option values on its request form. See Order from the service catalog. |
|
Return one catalog item’s request form: its variables, which of them are mandatory, and the values each one allows. |
|
Order a catalog item with the values for its request form, and return the requested-item number. |
|
Submit a record producer, a catalog form that writes a record directly instead of raising a request. |
|
Field metadata for a table, from |
|
Valid choice values for a field from |
|
Declared dependent-field chains for a table, from the server configuration. |
|
Find users by name, email, or username; returns their |
|
Find assignment groups by name; returns their |
|
Search configuration items ( |
|
Read any allow-listed table with an encoded query. |
Example: Search open incidents
curl -s https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/acme-servicenow \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_incidents",
"arguments": {
"query": "active=true^priority=1"
}
}
}'
Replace <cluster-id> with your cluster ID and $TOKEN with a gateway access token.
Example: Create an incident
The agent typically calls lookup_user and get_field_choices first to resolve caller_id and a valid category value.
curl -s https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/acme-servicenow \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "create_incident",
"arguments": {
"short_description": "VPN drops every few minutes in Dresden",
"caller_id": "a1b2c3d4e5f6...",
"urgency": 2,
"impact": 2,
"category": "network"
}
}
}'
Transition an incident
State changes go through transition_incident, which takes the target state by name, such as Resolved, Canceled, or On Hold. The server matches names case-insensitively against the state labels your instance defines, so nothing has to know a state number in advance, and a renumbered state can’t send an incident to the wrong place.
Pass the name, not the stored number. The server refuses a numeric value and names the state that value belongs to, so an agent can retry with the name. It refuses a name that matches nothing and lists the states your instance offers. Run get_field_choices on the incident table’s state field for the exact names, or read them from a record that get_incident or search_incidents returned.
The tool also carries a close code, close notes, a work note, a comment, and an additional_fields map for anything else the transition needs, such as a hold reason.
Require companion fields for a state
Instances commonly require a close code before an incident can be resolved or closed. That requirement isn’t part of the table schema get_table_schema reads, so the server can’t discover it. Declare it in incident_state_requirements instead. An unconfigured requirement isn’t enforced.
A declared requirement is checked before anything is sent, so a missing field comes back naming the field, and the agent can ask a person for it instead of interpreting an upstream rejection. The check has a wider reach than one tool, and it is forgiving about case:
-
The requirement applies to the state column, not to a single tool. A state that reaches
update_incidentthroughadditional_fieldsis checked the same way, so the requirement can’t be bypassed by the call that follows a refused transition. On that path the state matches by stored value only, because no choice list is read there. -
The server matches names case-insensitively on both sides: the state matches either its label or its stored value, and a required field name matches whatever case the write used.
Write to a different state column
Some instances don’t use state as the column that actually drives an incident’s state. On those, a write to state can report success while the incident doesn’t move. Set incident_state_field to the column that does drive state, commonly incident_state, and transition_incident writes that column instead.
Only the column changes. The agent still passes a state name in words, and the name resolves against the choices that column defines. Because a state can still reach the server as a raw state key in additional_fields, a configured requirement covers both column names, and transition_incident drops every state key from additional_fields before it assigns the resolved value, so one call can’t carry two states.
A misspelled column doesn’t fail quietly. Write verification treats the configured column like any other incident field, so a returned record that holds the write’s other fields but not that column reports the state under not_applied.
Order from the service catalog
Ordering an item takes three calls, in order. Skipping the middle call means collecting values for a form that the submission then rejects.
-
Run
search_catalog_itemswith the words the user used, such aslaptoporAdobe Acrobat. Pass product or service words rather than a sentence, because the server matches substrings. -
Run
get_catalog_itemon the chosen item to read its request form. Every item asks for something different. -
Submit the form with
submit_catalog_requestfor an ordinary item, or withsubmit_record_producerfor a record producer, which writes a record such as an incident or an idea directly instead of raising a request.
Search the catalog
The search runs two passes: one over each item’s name, short description, and category, and one over the option values on items' request forms. The second pass is what reaches a product that is a choice on a general item rather than an item of its own. A result found that way carries the matching option, so an agent can pre-fill it instead of asking again.
Treat the results as candidates rather than as the whole catalog. The response caps them (10 by default, 50 at most) and sets results_truncated when more items matched than it returned, so an item missing from a truncated result is not an item the catalog lacks. Narrow the query to a product name, or pass a category to scope a second pass the way the portal’s own navigation does.
A separate flag, option_search_unavailable, reports that the option-value pass couldn’t run at all. A false value there is weaker than it looks: an instance that filters rows out by permission answers the query normally, which looks the same as nothing matching.
Read the request form
The form that get_catalog_item returns lists each variable with its name, the question the form asks, its type, whether it’s mandatory, its default value, help text, the table a reference value must come from, its allowed values, and its position on the form. Asking in that order follows the form the user would otherwise have filled in.
The mandatory flag comes from the variable’s definition, so a UI policy can make a further variable mandatory once the form is being filled in. An unexpected rejection doesn’t mean the list was wrong.
The response also reports whether the item is still active. A retired item keeps a readable form but can’t be ordered, so check this before collecting values. Search only ever returns live items, so it matters when a sys_id came from somewhere else, or the item retired since.
Submit the form
Key every submitted value by the variable’s name rather than by the label the form shows, and pass the stored value rather than its display label. A mandatory variable that has a default value still needs that value passed explicitly, so the form that reaches ServiceNow is the form the agent reasoned about.
Both submission tools check the values against the form before sending anything, so a missing mandatory value or a name the form doesn’t carry comes back naming the variable, rather than as a rejection that identifies no field. A submission refused this way sent nothing, so correcting the arguments and calling again is safe.
When the form came back partial, only the mandatory-value half of that check runs. An unrecognized name then reaches ServiceNow, because refusing it would block a submission that the item would have accepted.
To order more than one of an item, pass a quantity with the submission. It defaults to 1, and 100 is the most you can order in a single call.
| Neither submission tool is safe to retry. A repeat call raises a second request, or writes a second record, under a second number that someone has to cancel. When a submission reports that it couldn’t read back what it created, the request or record exists: have the agent report that the number isn’t available yet rather than submitting again. |
What a submission returns
The submit_catalog_request tool returns the requested-item numbers, which begin with RITM. Those are the identifiers to give the user. The response also carries the request number, which begins with REQ, but that’s the container the requested items hang off, and isn’t what a user is asking for.
The submit_record_producer tool returns the table the producer wrote to, along with the created record and its number. Check the table before describing what was created, because a producer chooses its own target, and an idea and an incident reach different teams.
Two limits apply before you rely on either result. A catalog request confirms only that the request and its items exist. It says nothing about whether the form holds the values that were sent, because those live on separate option rows that this server doesn’t read. A record producer does compare the values it sent against the record it read back, because it writes a real record whose columns come back on a read. Even then, a producer script can map a variable to a different column, and the report marks a variable remapped that way as unconfirmed rather than as stored.
When a form couldn’t be read completely, get_catalog_item marks the response as incomplete. A variable missing from an incomplete form isn’t a variable the item doesn’t ask for, and an empty allowed-value list isn’t evidence that a value is invalid. The same applies to a catalog search whose option-value pass couldn’t run: the result is partial, not proof that the catalog holds no such thing.
Confirm what a write stored
An accepted write doesn’t always mean a stored value. Schema discovery tells you which fields to leave alone, and write verification tells you what the instance kept.
Before a write, get_table_schema reports what the data dictionary holds for each field, including:
-
Whether the dictionary marks the field read-only. An agent should report that such a field can’t be set, rather than trying to set it.
-
Whether a reference field is filtered by a reference qualifier, and which of the three qualifier kinds it declares (Simple, Dynamic, or Advanced). For a filtered field, resolve a value that satisfies the filter, and read an emptied field as the filter rejecting the value rather than as a bad identifier. The qualifier’s own condition, script, or filter record isn’t returned, so ask an administrator about the construct the field names.
A false value on either attribute doesn’t guarantee that the field is writable. Write access control lists, ServiceNow data policies, and business rules aren’t read here, and aren’t in the data dictionary at all. A sys_dictionary_override row is in the data dictionary but still isn’t read here, so an inherited field made read-only on one child table reports false.
After a write, the incident write tools compare the values they sent against the record that ServiceNow returned from the write itself, and attach a report to that record under _redpanda_write_verification. The submit_record_producer tool re-reads the record it created and reports on that instead. A write response is synchronous, so a confirmed value is what the instance held at that moment. Business rules, flows, and scheduled jobs that run afterwards can still change it.
The report always carries a plain-language summary. It adds applied and not_applied as lists of field names, for the values the response held and the values the instance didn’t store, and unconfirmed for fields it can’t settle either way, where each entry pairs the field with the reason. Journal fields such as work notes and comments are never echoed back, so they arrive unconfirmed. Each of the three is left out when it’s empty. Have an agent read the report before it tells a user what changed. A state that appears under not_applied after transition_incident means the transition didn’t happen.
The submit_catalog_request tool attaches no such report. Its response carries the request and requested-item numbers rather than the record, so nothing confirms the values that the request stored.
Troubleshooting
Common symptoms and fixes:
| Symptom | What to check |
|---|---|
|
Confirm the service-account credentials. For Basic authentication, check the username and |
|
The service account lacks the role for the operation (for example, |
|
The table is not listed in |
|
It can’t change an incident’s state, and nothing was sent. Call |
|
The instance didn’t store the state. If another column drives state on this instance, set |
|
A configured |
A catalog search or request form comes back partial |
The account can’t read |
A submission is refused naming a variable |
The refusal is local, so nothing was sent. Supply the named variable’s value (a mandatory variable needs its value even when the form has a default), or drop a name the form doesn’t carry, then call again. |
A write reports a field under |
The instance didn’t store that field. Check the field with |
|
First call from a user with no stored token. The user completes the ServiceNow OAuth consent flow, the token lands in the vault, and later calls reuse it. See User-delegated OAuth. |
Limitations
This MCP server does not cover:
-
Record deletion: Writes are limited to the typed incident tools and the two catalog submission tools; the MCP never deletes records.
-
Arbitrary SQL: Reads go through the REST Table API and encoded queries, not a SQL interface. Use
query_recordsagainst allow-listed tables instead. -
Order guides, content items, and order wizards: The server refuses these three classes rather than submitting one, and refuses a record producer on the ordering path, because
submit_record_produceris what submits it. Any other class is submitted, including an item whose class couldn’t be read, because most orderable items belong to a subclass rather than to the base class. A catalog search reports each item’s class, so an agent can tell a user that a given item can’t be ordered here. -
Ordering on behalf of someone else: Neither submission tool takes an argument naming a different requester. You can still set a requester through a form’s own requester variable, where the item has one.
-
Two-step checkout: The server detects an instance configured to place an ordered item in a cart instead of raising a request, rather than working around it. The submission then reports an error saying the item is already submitted, so don’t submit it again.