Showing posts with label ServiceNow. Show all posts
Showing posts with label ServiceNow. Show all posts

Sunday, October 25, 2026

ServiceNow Build Agent vs Traditional Development: Hidden Records, keys.ts and the Source-of-Truth Inversion

ServiceNow Build Agent vs Traditional Development

Traditional ServiceNow development has one comfortable rule: you create a record, you see the record, you move the record. A Business Rule is one row in sys_script. A Script Include is one row in sys_script_include. Whatever you can open on the instance is what goes into the update set.

Build Agent changes that rule quietly. Some things it creates still map one-to-one to a platform record. Others come with a set of companion records you never opened, never named and may not notice until a promoted page loads blank on the next instance. This article puts the two approaches side by side: what stays the same, what is different, how to spot hidden dependencies, and how to deploy without leaving pieces behind.

QuestionShort answer
Is Build Agent different for simple metadata?No. Script Includes, Business Rules, ACLs, properties and similar items are still one source definition = one platform record
Where do the surprises come from?UI Pages (BYOUI / React), UX pages and workspaces, Flow Designer artifacts, Record() seed data and table definitions
What is the single most important file?keys.ts, the inventory of every record the SDK manages
What is the biggest mindset change?The source files become the source of truth. Instance records become compiled output
What is the biggest practical risk?Promoting only the record you can see and leaving its companion records behind

1. Metadata That Behaves the Same Way (1:1)

Start with the reassuring part. For a large set of common metadata, it makes no difference whether you create it traditionally or through Build Agent. One definition becomes one platform record, and that record is safe to share, review and move on its own.

Metadata typeTableNotes
Script Includesys_script_includeOne file, one record. The script goes into the script field
Business Rulesys_scriptCondition and script, one record
Scheduled Jobsys_triggerOne record
System Propertysys_propertiesOne record
Table / Fieldsys_db_object / sys_dictionaryFields become individual dictionary rows, as they always have. Not a Build Agent difference
UI Actionsys_ui_actionOne record
ACLsys_security_aclOne record, plus sys_security_acl_role for role conditions, which is standard
REST Messagesys_rest_messageParent plus HTTP methods, the usual structure
Notificationsysevent_email_actionOne record

What to actually learn: do not treat Build Agent as universally risky. For this list, your existing update set habits still work as they are.

2. Metadata That Creates Hidden Records (1:Many)

Now the part that catches people off guard. In these cases one thing you define in source produces several platform records, and some of them are records you would never think to look for.

UI Page (BYOUI / React): the biggest offender

Hidden recordTableWhy it exists
JS bundlesys_ux_lib_assetThe compiled React app
Source mapsys_ux_lib_assetDebug mapping
CSS assetsys_ux_theme_assetCompiled styles
Source archivesn_glider_source_artifact + sys_attachmentSource code for IDE editing
Link recordssn_glider_source_artifact_m2mConnects page to source

Risk if missing: a blank page. The HTML shell loads, but the actual application never runs. This is the failure that looks the most mysterious, because the sys_ui_page record is present and looks perfectly healthy.

UX Page / Workspace Experience (UI Builder)

If you use Build Agent to define UX Framework pages such as UI Builder pages or workspace experiences, expect a different set of companions.

Hidden recordTableWhy it exists
Macroponent definitionssys_ux_macroponentComponent definitions
Data broker transformssys_ux_data_broker_transformData wiring
Component registrationssys_ux_lib_componentComponent registry
Source resourcessys_ux_lib_source_resourceComponent source code
Client scriptssys_ux_client_scriptEvent handlers

Risk if missing: the workspace page loads, but components are missing, broken or showing errors.

Flow / Subflow / Action

When Build Agent creates Flow Designer artifacts, the flow definition is only part of the picture.

Hidden recordTableWhy it exists
Action type definitionssys_hub_action_type_definitionCustom action schemas
Step inputs / outputssys_hub_step_ext_input / sys_hub_step_ext_outputStep interface definitions
Variable valuessys_variable_valueDefault values and configurations
Flow entriessys_hub_flowThe flow definition itself

Risk if missing: the flow fails at runtime, actions report "definition not found," and steps have missing inputs.

Seed data with Record()

When you use Record() in Fluent to seed data, it creates real data records in whatever table you target: sys_choice, sys_user_role, sys_properties, custom tables and so on. It is also tracked in keys.ts, so if you remove a Record() from source, the SDK may want to delete it on the next deploy.

Risk if missing: the configuration data your app depends on does not exist. Choice values are missing, roles are undefined, properties are absent.

Table definitions

RecordTableWhy it exists
Table recordsys_db_objectThe table definition
Column recordssys_dictionaryOne per field
Number recordsys_numberAuto-number configuration
Modulesys_app_moduleNavigation module
Default ACLssys_security_aclTable and field-level ACLs
Label entriessys_documentationField labels and help text

This is similar to the traditional approach, because creating a table has always created child records. The difference to watch for is that Build Agent may also generate ACLs and modules that you did not explicitly define.

What to actually learn: the record you can see in a list view is not always the whole artifact. Before you call something "one record," check what it brings with it.

3. How to Tell Whether Something Has Hidden Dependencies

You do not need to memorize the tables above. These signals tell you when to stop and look closer.

SignalWhat it means
HTML contains @fluent-import-htmlBYOUI page, with external bundle assets
HTML references a .jsdbx fileThe compiled JS bundle lives in sys_ux_lib_asset
Record has entries in sn_glider_source_artifact_m2mIt has linked source artifacts
Created by Build Agent / admin with sys_mod_count: 0It was deployed as a batch, so it likely has companion records
keys.ts references multiple IDsEach ID is a separate managed record

4. keys.ts: Your Source of Truth

Every Build Agent workspace has a keys.ts file that lists every record the SDK manages. Treat it as the definitive inventory. A simplified example:

// Example keys.ts
export default {
  'sys_ui_page:8d935077...': 'incident_xyz',
  'sys_ux_lib_asset:abc123...': 'global/incident-xyz',
  'sys_ux_lib_asset:def456...': 'global/incident-xyz.js.map',
  'sys_ux_theme_asset:ghi789...': 'styles',
  'sys_script_include:1a9582...': 'XyzUtil',
  // ... every managed record listed here
}

One page, one Script Include, and already four more records than you would have counted by eye. The rule is simple: if it is in keys.ts, it needs to travel with the solution.

5. The Source-of-Truth Inversion

This is the deepest difference between the two approaches, and it is a mindset change more than a technical one.

Traditional:
  Instance record IS the source of truth
        |
        v
  Export to update set for transport

Build Agent:
  Source files in the workspace ARE the source of truth
        |
        v
  Build compiles to instance records
        |
        v
  Instance records are "compiled output"
QuestionTraditional answerBuild Agent answer
Where do I look to understand the current state?The instance recordThe source files in the workspace
What happens if instance and source disagree?Instance winsSource wins (the next deploy overwrites the instance)
Can I safely edit on the instance?Yes, alwaysOnly if you remove the @fluent-import-html marker or the record from keys.ts
Where is the history?sys_update_version on the instanceYour source files, plus whatever version control you use

What to actually learn: a hotfix made directly on the instance is a normal move in traditional development. With Build Agent-managed records it can be silently overwritten by the next deploy. Decide up front which side owns each record.

6. The Packaging Gap

Put the pieces together and a real tooling gap appears, particularly in Global scope.

What existsWhat is missing
Build Agent tracks dependencies internallyNo "export everything for this solution" button
Update sets capture individual recordsNo automatic "include all related Build Agent artifacts"
keys.ts lists all managed recordsNo UI to visualize this outside the IDE
Scoped apps package automaticallyGlobal scope has no equivalent

The platform assumes you are either in a scoped app, where packaging is automatic, or doing traditional development, where one record is one artifact. Build Agent in Global scope sits in an awkward middle ground between the two. Until that gap closes, the discipline has to come from your process.

7. A Practical Deployment Habit: Name the Update Set at Install Time

One thing that helps right away: Build Agent supports an updateSetName parameter when it runs build_install. If your naming convention is known at build time, the deployment can go straight into a properly named update set instead of the SDK's default. For example, if your convention is change number plus description:

build_install with updateSetName = "CHG0012345 - Incident XYZ Fix"

The SDK then creates or uses that update set name. For teams with a strict change process, this is probably the cleanest fit.

A short checklist that works alongside it:

  • Open keys.ts before every promotion and count the managed records
  • Confirm every ID listed is present in the update set you are about to move
  • Check for the signals in section 3 on any UI Page or workspace page
  • Decide whether a record is owned by the source or by the instance, and write it down
  • After promotion, load the page or run the flow on the target instance. A blank page is the classic sign of a missing bundle asset

8. Test Yourself

Knowing the tables is one thing. Spotting the problem under pressure is another. Try these two before reading the answers.

Question 1. A Build Agent-created BYOUI page works on Dev. After promotion, Test shows a blank page, although the sys_ui_page record is present. What is the most likely cause?

Question 2. A developer fixes a typo directly on the instance for a record that Build Agent manages. Two days later the fix disappears. Why?

Answers: (1) The companion records did not travel, most likely the JS bundle in sys_ux_lib_asset and the CSS in sys_ux_theme_asset. The HTML shell loads but the app never runs. (2) Source wins. The next deploy rebuilt the instance record from the workspace source files and overwrote the manual edit.

If you enjoy thinking through situations like these, I turned this style of thinking into a full practice book: ServiceNow Build Agent Mastery Quiz: 120 Scenario-Based Questions with Detailed Answers and Explanations for Developers, Admins, Architects and Teams. Every question has its own page, and the answer with a full explanation sits on the next page, so you can commit to an answer before you check it. It covers hidden records, keys.ts, team dynamics, troubleshooting and role-specific scenarios. It is available on Amazon KDP if you would like to take a look. It is entirely optional, and everything in this article stands on its own.

9. Frequently Asked Questions

Is Build Agent riskier than traditional development?
Not across the board. For common 1:1 metadata such as Script Includes and Business Rules it behaves the same way. The extra care is needed for UI Pages, UX pages, Flow artifacts, seed data and table definitions.

What is a hidden record?
A platform record created alongside the thing you defined, such as the compiled JS bundle behind a BYOUI page, which you did not create or name yourself.

Where do I find the full list of records Build Agent manages?
In keys.ts in the workspace. It is the inventory of every managed record.

Can I edit a Build Agent record directly on the instance?
Only if you first remove the @fluent-import-html marker or the record from keys.ts. Otherwise the next deploy overwrites your change.

Does this problem exist in scoped apps?
Scoped apps package their records automatically, so the gap described here is most visible in Global scope.

Wrap-Up

Build Agent does not replace what you already know about ServiceNow. It adds a layer on top of it: a source-first workflow, a manifest in keys.ts, and a set of records that arrive in groups instead of one at a time. Most of your habits survive. The ones that do not are the ones that assume the visible record is the whole story.

If there is one habit to take from this article, it is the question to ask before every promotion: what else came with this record? If you want to sharpen that instinct with realistic scenarios, the Build Agent Mastery Quiz is there for exactly that purpose. Either way, ask the question before you deploy, not after the page goes blank.


Note: Table names, record behavior and tooling details can change between releases. Verify them on your own instance and in the official ServiceNow documentation before relying on them. ServiceNow is a trademark of ServiceNow, Inc. This blog is independent and not affiliated with or endorsed by ServiceNow, Inc.





Sunday, September 20, 2026

glide.db.atomic_counter_all in ServiceNow: Fix Record-Number Bottlenecks in High-Volume Inserts (Australia Release Guide)

glide.db.atomic_counter_all in ServiceNow

Some of the most stubborn ServiceNow performance problems don't come from a slow script or an unindexed query. They come from something small that every insert has to do first: generate the next record number. On most instances that step is invisible. On an instance ingesting large volumes concurrently — Vulnerability Response (VR) feeds, several data sources landing on the same table, overlapping scheduled imports — it can quietly become the thing everything else waits on.

This article covers a system property aimed at exactly that situation: glide.db.atomic_counter_all. In the Australia release, if the property isn't already present on your instance, it has to be created manually, and ServiceNow is expected to enable it by default in future releases. Below: what problem it addresses, what it changes, whether it's safe to turn on, and how to confirm it actually helped.

Question Short answer
What does it do? Changes how new record numbers (INC, VUL, and so on) are generated — an atomic database operation instead of a lock, read, increment, write cycle
Who benefits most? Instances with heavy, concurrent inserts on the same table from multiple nodes
Does it touch existing records? No
Does the number format change? No
Can it be rolled back? Yes — set it to false or remove it; no data changes needed
Should it be tested first? Yes — validate in sub-production under your normal change process

1. How ServiceNow Generates Record Numbers

Every table with auto-numbering — Incident, Change, or a VR table such as Vulnerable Item — has a counter kept in the database. Creating a record means the platform has to:

  1. Lock the counter row for that table
  2. Read the current value
  3. Increment it
  4. Write the new value back
  5. Release the lock

At normal volumes this cycle is fast enough that nobody notices it. The cost only shows up when many inserts want the same counter at the same moment.

2. The Hot-Row Contention Problem

The detail that matters is that the counter is per table. Every insert on a given table, from every node, needs the same single row. On a multi-node instance, those inserts queue for that row and are served one at a time.

Example — an illustrative VR ingestion scenario (not a benchmark):

Several scheduled jobs pull data into the same underlying table from different nodes at the same time. They use multiple data sources and import templates, and their iterations overlap with one another. Each insert needs the next number, so they all converge on one counter row:

Node A  (Data Source 1 -> Import Template X)  --+
Node B  (Data Source 2 -> Import Template Y)  --+
Node C  (Scheduled job, same target table)    --+--> ONE counter row
Node D  (Overlapping import iteration)        --+    lock -> read -> +1 -> write -> unlock

Result: inserts wait in line for the counter, one at a time,
        no matter how many nodes are available to do the work.

To put a rough number on it: 25 million records in a day works out to about 290 inserts per second on average, and considerably more during ingestion peaks. The 25 million figure is only an illustration — the pattern matters more than the number.

The symptoms tend to look like this:

  • Import and VR processing jobs take longer than expected
  • Iterations run past their window and start overlapping the next run, so delays cascade from one job to the next
  • Insert throughput doesn't scale even when more nodes are available
  • Nothing looks obviously wrong in the scripts or queries themselves

What to actually learn: when ingestion jobs slow down and start overlapping without any single script or query standing out, insert serialization on the target table is worth ruling in or out — not just the job logic.

3. What glide.db.atomic_counter_all Does

With the property set to true, number generation moves from the application-level lock, read, increment, write, unlock pattern to a database-native atomic increment. In simplified terms, the database performs the increment as a single operation, instead of the platform holding a lock across several steps.

Aspect Traditional Atomic counter
Mechanism Application-level lock, read, increment, write, unlock Single atomic database operation
Lock duration Held for the whole read-increment-write cycle Very short, handled inside the database
Contention under load High — nodes queue up Much lower — the database engine manages concurrency
Number uniqueness Guaranteed Guaranteed
Number format Prefix and padding as configured Unchanged

Nothing about the numbers themselves changes. What changes is how the platform obtains the next one.

4. Why the Australia Release and RaptorDB Bring This Into Focus

ServiceNow's knowledge article on this behavior (KB2462993) notes that the contention is observed more often on RaptorDB. RaptorDB is PostgreSQL-based, and its row-level locking and MVCC behavior means concurrent updates to the same row are serialized: each transaction waits for the previous one to commit. A hot counter row therefore becomes visible at lower volumes than many teams saw before.

The pattern itself isn't new. Under RaptorDB's concurrency behavior it simply surfaces sooner — which is why the property is now something Australia-release instances need to look at deliberately. It also fits the broader shift in database infrastructure that platform teams are already tracking.

5. Is It Safe? Impact Analysis

This is the first concern most administrators raise, so it's worth taking in three parts: existing data, new data, and scope.

Existing data

Concern Answer
Existing records No impact. Existing numbers stay exactly as they are
Number continuity The counter continues from its current value. If the last number was VUL0025000000, the next is VUL0025000001 — no gap, no reset
Historical integrity Untouched. This changes how numbers are generated, not stored data

New data

Concern Answer
Uniqueness Atomic operations exist specifically to prevent duplicates
Format Same prefix, same padding
Reporting and queries Number fields behave the same in filters, reports and lookups
Ordering Effectively sequential. Records created at nearly the same instant may not receive numbers in exact commit order, but they will not receive the same number

Scope

Concern Answer
Where does it apply? Instance-wide, to tables using number maintenance
Low-volume tables No noticeable difference
High-volume tables This is where the benefit shows up
Reversibility Set the property back to false and the traditional mechanism returns

A note on sequencing: ServiceNow numbering has never been strictly gap-free — numbers can be consumed by records that are opened and never saved. If a business process genuinely depends on gap-free, commit-ordered numbers, treat that as a requirement to confirm with stakeholders before changing anything.

6. Is ServiceNow Hiding a Glitch?

A fair question, and the honest answer is no. This is an architectural scaling limitation that appears at high concurrency, not a defect in the usual sense. Publishing a knowledge article and providing a property to address it is the transparent way to handle a known limit.

The rollout has also been gradual: atomic behavior has been available as an opt-in, and ServiceNow is expected to make it the default in future releases as RaptorDB adoption grows. Enabling it now simply brings your instance in line with where the platform is heading.

What to actually learn: a property that ServiceNow intends to make the default is a low-regret change — but low-regret still isn't zero-testing. Treat it like any other instance-wide change.

7. How to Set It Up

  1. In the navigation filter, enter sys_properties.list to open the System Properties table.
  2. Search for glide.db.atomic_counter_all.
  3. If it exists, check its current value. If it doesn't, click New.
  4. Enter the values shown below and save.
  5. Capture the change in an update set or your change record, as your process requires.
Name:         glide.db.atomic_counter_all
Type:         true | false
Value:        true
Description:  Use atomic DB operation for number generation. See KB2462993.

Rollback: set the value to false, or delete the property. No data changes are needed either way.

8. How to Validate the Improvement

Enabling the property is only half the job. Confirm it did what you expected by comparing before and after, using the same data volumes wherever possible:

  • Job duration: run time of your VR ingestion or import jobs over comparable volumes
  • Insert throughput: records created per minute on the affected table
  • Transaction logs: average response time of insert-heavy transactions
  • Overlap: how often scheduled jobs collide or run past the start of their next iteration
  • Database wait behavior: lock waits on the number counter, if your monitoring exposes them

Record baselines before the change. Without them, an improvement — or the absence of one — is only an impression.

9. Best-Practice Checklist

  • Enable it in a sub-production instance first and run a realistic load
  • Get the change approved through your normal process
  • Capture baselines before and after
  • Confirm nothing downstream depends on gap-free, commit-ordered numbers
  • Review overlapping job schedules regardless — removing one bottleneck doesn't excuse poorly staggered jobs
  • Recheck after upgrades, since future releases may set the property by default

10. Frequently Asked Questions

What is glide.db.atomic_counter_all?
A ServiceNow system property that makes record-number generation use an atomic database operation, reducing lock contention on high-volume tables.

Does it change existing record numbers?
No. It only affects numbers generated after it is enabled.

Can it create duplicate numbers?
No. Uniqueness is the purpose of the atomic operation.

Do I need it on a low-volume instance?
Probably not for performance. It is harmless to enable, but the benefit appears under heavy, concurrent inserts.

Can I undo it?
Yes. Set it to false or remove the property.

Wrap-Up

Record numbering is easy to overlook until it becomes the bottleneck. If your instance ingests large volumes concurrently, glide.db.atomic_counter_all removes a serialization point at very low risk — but the discipline that makes it low-risk is the same one that applies to any instance-wide property: test it, measure it, and roll it out deliberately.

If there's one habit worth taking from this, it's the question underneath the whole article: when jobs get slow and nothing in the code explains it, what are they all waiting on? Ask that before adding nodes, not after.


Reference:

  • ServiceNow Knowledge Article KB2462993 — number generation contention and the glide.db.atomic_counter_all property

Wednesday, August 12, 2026

Governance & Platform Impact: The AI Accountability Layer on ServiceNow (Chapter 6)

Governance & Platform Impact: The AI Accountability Layer on ServiceNow

This is Chapter 6, the final chapter in a series walking through ServiceNow's AI stack from the ground up. The first five chapters moved from platform fundamentals through Now Assist, agentic AI, and integration layers — each one adding capability. This chapter is different in kind: it's not about what's possible, it's about what you're accountable for once it's running.

Every capability chapter so far ended with "here's what to review before trusting the output." This chapter is what happens when that review discipline has to scale past one person checking one script — to an enterprise instance, multiple modules, and an audit trail someone else will eventually read.

1. Data Residency: Where Does It Actually Go?

"Where does the data go" isn't one answer for "AI on ServiceNow" — it depends on which feature, which model, and how it's configured. A useful habit is running every new AI feature through the same short decision tree before enabling it against real data.

Is the model hosted within your instance's own tenant boundary,
or a shared multi-tenant service?
  ├─ Own tenant → confirm data doesn't leave your compliance boundary
  └─ Shared service → ask: is the data used for further model training?
        ├─ Yes → this needs explicit compliance sign-off, not assumption
        └─ No, per vendor terms → confirm that contractually, not verbally

Does the feature send full record content, or only specific fields?
  ├─ Full record → higher exposure if the record has sensitive fields
  └─ Scoped fields → lower exposure, but confirm the scope is enforced,
        not just documented

Is there a regional/geographic processing constraint on this data?
  ├─ Yes (e.g. data residency law, contractual clause) → verify the
        processing region matches before enabling
  └─ No known constraint → still worth documenting that you checked

What to actually learn: run this tree per feature, not once for "AI" as a category. Now Assist's summarization skill and a custom Agent Studio agent calling an external model may have entirely different answers to the same three questions, even on the same instance.

2. License & Consumption Impact

AI features typically consume against separate entitlements from the base platform license, and consumption can scale in ways that aren't obvious until you've enabled a few modules at once.

Example — a sample cost-impact view across three modules:

Module AI Feature Enabled Consumption Driver
ITSM Incident summarization Per-summary generation, scales with ticket volume
CSM Case summarization + Virtual Agent Case volume plus conversational turn count
HRSD Case summarization Case volume, typically lower than CSM but seasonal spikes (open enrollment, etc.)

The point of a table like this isn't the exact numbers — those depend on your actual contract and usage — it's the shape: consumption drivers differ by module, and a module with a seasonal spike (HR case volume around open enrollment, for instance) can produce a consumption spike that looks alarming in isolation but is entirely predictable if someone mapped it in advance.

What to actually learn: build this kind of module-by-module consumption map before rollout, not after the first invoice surprises someone. It's also useful ammunition for exactly the ROI conversations that come up when justifying AI feature adoption to budget holders.

3. How AI-Generated Changes Show Up in the Audit Trail

This is worth checking directly rather than assuming, because it affects whether an AI-assisted change is actually distinguishable later from a manual one.

Example — a human-made change vs. an AI-drafted one, in the audit log:

Human-made change (sys_audit record):
  fieldname: close_notes
  oldvalue: (empty)
  newvalue: "Replaced faulty network cable, confirmed connectivity restored."
  user: jane.smith
  reason: (not typically captured)

AI-drafted, human-approved change (sys_audit record):
  fieldname: close_notes
  oldvalue: (empty)
  newvalue: "Replaced faulty network cable, confirmed connectivity restored."
  user: jane.smith
  reason: (not typically captured — the record looks identical to the
           human-made one above)

That similarity is the actual finding worth sitting with: unless your instance is specifically configured to tag AI-assisted edits — for instance, logging that a field's content originated from a generative action before a human edited and submitted it — the standard audit trail doesn't distinguish "an agent drafted this and a human approved it" from "a human wrote this from scratch." For most day-to-day work that's fine. For anything falling under a compliance framework that cares about decision provenance, it's a gap worth closing deliberately, not discovering during an audit.

What to actually learn: if AI-assisted decision provenance matters for your compliance posture, verify explicitly whether your instance captures that distinction, and if it doesn't, treat that as a configuration gap to raise — don't assume the audit trail is telling a more complete story than it is.

4. Infrastructure Dependencies Underneath It All

AI workloads don't run in a vacuum — they add query and processing load to the same platform infrastructure everything else depends on, and that infrastructure is itself changing underneath ServiceNow with RaptorDB's rollout as the HTAP replacement for MariaDB/MySQL.

A real example of why this isn't a footnote: a prior IRE (Identification & Reconciliation Engine) schema mismatch incident occurred when VR USEM, Rapid7 AppSec, and Discovery/Service Mapping were upgraded simultaneously — the resulting log volume ran to roughly 6 million entries per hour. That's not an AI-specific incident, but it's exactly the kind of platform-level fragility that matters more, not less, once AI features are layered on top and depending on the same CMDB reconciliation pipeline for grounding data. An agent summarizing "related CIs" during a period like that isn't just slow — it may be working against data mid-reconciliation.

What to actually learn: treat plugin upgrade governance and infrastructure changes like RaptorDB as directly relevant to AI feature reliability, not a separate platform-ops concern. Before rolling out or expanding an AI feature that depends on CMDB or reconciliation data, ask whether there are any upgrades scheduled that touch the same pipeline — the two workstreams should be coordinated, not siloed.

Series Wrap-Up

Six chapters, one thread running through all of them: every layer of ServiceNow's AI stack is only as trustworthy as your ability to review what it hands you, and that review discipline has to scale as the stakes go up — from a single generated script in Chapter 3, to an agent composing its own tool-call sequence in Chapter 4, to a governance posture that holds across an entire instance in this chapter.

If there's one habit worth carrying forward past this series, it's the question this chapter kept returning to: where does this data actually go, and who's accountable for what it did? Ask that before enabling a feature, not after.


This series:

  • Chapter 1 — Platform Fundamentals for AI-Ready ServiceNow
  • Chapter 2 — Now Assist as a Consumer
  • Chapter 3 — Now Assist for Creator
  • Chapter 4 — Agentic AI: Otto, Agent Studio, Build Agent
  • Chapter 5 — Action Fabric & MCP
  • Chapter 6 — Governance & Platform Impact (this chapter)

Tuesday, August 11, 2026

Action Fabric & MCP on ServiceNow: Integration Layer for AI Agents (Chapter 5)

Action Fabric & MCP on ServiceNow: Integration Layer for AI Agents

This is Chapter 5 in a series walking through ServiceNow's AI stack from the ground up. Chapter 4 covered agents — goal-driven systems that decide their own sequence of tool calls. This chapter covers the layer underneath that: how those tools actually get exposed and called in the first place. That's Action Fabric and MCP (Model Context Protocol), and it's the layer where an agent stops being a conceptual diagram and starts being something with real, executable access to your instance.

This chapter ends with a real hands-on finding from testing a community MCP server against a PDI — not a hypothetical risk, but something that actually happened during testing.

1. MCP vs. REST — the Same Integration, Built Two Ways

The clearest way to understand MCP is to see the same integration task built the traditional way and the MCP way, side by side.

Task: let an external AI tool look up and update incident records.

Built as a traditional REST integration:

GET  /api/now/table/incident?sysparm_query=number=INC0010001
PATCH /api/now/table/incident/{sys_id}
      Body: { "state": "6", "close_notes": "Resolved via automation" }

Here, the external system needs to know the table API shape, the correct sys_id lookup pattern, and the exact field names and value mappings (state 6 = Resolved) in advance. Every capability it has is whatever the integrating developer explicitly wired up and documented. An AI model consuming this would need those specifics fed into its context as documentation or examples every time.

Built as an MCP server:

Tool: get_incident(number: string) → returns incident record
Tool: resolve_incident(number: string, notes: string) → sets state to
      Resolved and applies notes

Description exposed to the model: "resolve_incident: Marks the given
incident as Resolved with the provided close notes. Use this only after
confirming the underlying issue is fixed."

The MCP server does the translation work — the model never sees a raw table API or a state code. It sees a named, described action and calls it with plain arguments. This is the actual value proposition: MCP standardizes how a model discovers and invokes capabilities, so the same model can work against ServiceNow, a ticketing tool, and a monitoring platform through the same protocol shape, rather than needing bespoke integration knowledge baked into its prompt for each one.

What to actually learn: MCP isn't a replacement for REST underneath — most MCP servers call REST APIs internally, this one included. What MCP replaces is the need to hand-document and hand-feed API specifics into a model's context every time. The tradeoff, covered next, is that the server now decides on the model's behalf what's "safe" to expose as a callable action — and that decision can be wrong.

2. How Action Fabric Exposes Capabilities

Action Fabric is ServiceNow's own layer for defining what capabilities are callable by agents and external tools — conceptually similar to the MCP tool definitions above, but native to the platform and integrated with its role and ACL model rather than sitting outside it.

Example — one action defined and called:

  • Define the action's inputs and outputs explicitly — for a "resolve incident" action, that's an incident number in, a success/failure status and updated record out. No open-ended access to arbitrary tables.
  • Attach the action to a scoped role, the same discipline from Chapter 1 and Chapter 4 — the account executing this action should have exactly enough access to resolve incidents, not general write access to the incident table.
  • Write the action's description for the consuming agent, not for a developer reading the code — this is the same lesson from Chapter 4's tool-scoping discussion, because Action Fabric actions and Agent Studio tools are ultimately the same kind of artifact wearing different names.
  • Test the action being called with an invalid or already-resolved incident number before trusting it inside an agent's tool set — the failure behavior here becomes the agent's problem to reason about once it's live.

What to actually learn: Action Fabric and MCP are solving the same underlying problem — giving a model a well-defined, well-scoped action instead of raw API access — from two different directions. Action Fabric is ServiceNow-native and governed by the platform's own role model; MCP is protocol-level and platform-agnostic, meant to work the same way regardless of what system sits behind it.

3. Hands-On Caution: a Real Finding From Testing an MCP Server Against a PDI

Everything above is the theory. Here's what actually happened testing a community MCP server — nowaikit — connecting Claude Code to a ServiceNow PDI inside GitHub Codespaces.

Finding 1 — write operations executed without confirmation prompts. Section 2 above described a well-designed action model as one where the exposed action's description and scope are deliberate, tested guardrails. In this hands-on test, that guardrail was missing at the point that matters most: when the connected AI tool issued a write operation against the PDI, it went through directly, with no confirmation step in between the model's decision and the actual change landing on the instance. This is precisely the failure mode Chapter 4 warned about in the abstract — an agent composing its own action sequence is only as safe as the friction (or lack of it) between "the model decided to do this" and "this actually happened." A missing confirmation step removes that friction entirely.

Finding 2 — a hidden local dependency. nowaikit's slash-commands required a local Ollama instance running separately from Claude Code itself. This is a practical, easy-to-miss setup gap: the MCP server's advertised capabilities weren't fully available through Claude Code alone, and the dependency wasn't obvious until testing the slash-commands specifically. Worth calling out because it's exactly the kind of gap that "reading the documentation" doesn't always surface — it surfaces from actually running the thing.

What to actually learn: before connecting any community MCP server to an instance that matters — even a PDI — test write operations specifically and deliberately, with data you can afford to lose, and confirm whether a confirmation/approval step exists before the server can execute a change. Don't assume it does just because a well-designed system in principle would have one. And separately, budget testing time for a server's full advertised feature set, including anything gated behind local tooling that isn't part of the core install.

Checkpoint Before Moving to Chapter 6

You should be able to: explain what MCP standardizes versus what it doesn't, using the get_incident/resolve_incident example rather than protocol jargon; define an Action Fabric action with a properly scoped role and a description written for the consuming agent, not a developer; and — most importantly — know to test any new MCP server's write behavior explicitly before trusting it near real data, because "it should have a confirmation step" is a design assumption, not a verified fact. Chapter 6 takes this same skepticism and applies it at the governance level — data residency, licensing, audit trails, and the infrastructure underneath all of it.

Next in this series: Chapter 6 — Governance & Platform Impact, including a data-residency decision tree and how AI-generated changes actually show up in the audit log.