HR Leave · Practical AI

Practical AI with ServiceNow — from zero

Open the pictures first. Each block maps to a real screen on your instance. Ship a read-only agent before you let tools touch state fields.

Scope (HR lab)x_786686_hr_leave
URL token{BASE}
Alt table labincident (ITSM)
Time~90 min hands-on

Read the diagram, then click the URL

Zero → prerequisites → open consoles → one read-only agent → governance → RAG/MCP pointers → SDK boundaries.

0License + roles
→
1Control Tower
→
2Agent Studio
→
3Read tool
→
4Register
→
5Test ACLs

1. Prerequisites — before you open Agent Studio

Core app existsTables, BR, Flow, ACLs green (ch.01–12). AI sits on top — it does not replace Studio create steps.
Now Assist / Otto SKUsLicensed on the instance. PDI may lack AI Agents or Control Tower — plan a licensed lab for Parts that 404.
PluginsSearch filter navigator: Now Assist AI Agents, AI Agent Studio, AI Control Tower as your contract allows.
Rolessn_aia.admin (author), sn_aia.user (run), platform admin for plugin activation only — not for production agent runtime.
AI Control TowerGovernance plane: inventory, approval posture, runs. Register every agent before wider rollout.
Update SetScoped capture under HR Leave (or your app). Script Include tools belong in application scope.

Minimum green light: you can open Agent Studio without 404, ACLs enforce row-level access on your lab table, and you have two test users (employee + manager) to prove invoking-user behavior.

Licensing reality: Now Assist, AI Agents, Agent Studio, and Control Tower are separate SKUs. If a URL fails, use filter navigator search — do not paste admin credentials into external LLM tools to “work around” missing product.

✓ ACLs before AI ✓ Invoking user first ✓ Control Tower register ✗ AI before tables ✗ admin as AI User

2. Direct URLs — bookmark these

Replace {BASE} with your instance (use the bar at top after page load). All links open in a new tab once applied.

3. Lab — one read-only agent (leave or incidents)

Pick one track. Both use the same pattern: Script Include tool → register in Agent Studio → data access = invoking user → GlideRecordSecure so ACLs apply as the human who asked.

TrackTableWhat the agent may do (v1)
A — HR Leavex_786686_hr_leave_req_v2List/count my requests; summarize dates and state — no insert, no u_state writes.
B — Security / ITSMincidentList open incidents caller can see; get one by number — read-only, no assignment/state changes.
Script IncludeCreate LabAIReadTools in your app scope. Client callable: false.
Tool methodReturn JSON only. Use GlideRecordSecure + encoded query scoped to caller.
Agent Studio → ToolsNew → Script → pick include + method. Describe inputs in plain English.
New AI AgentInstructions: refuse writes; cite record numbers; say when ACL hides data.
Data accessInvoking user (not AI User) for v1.
TestRun as employee1 vs manager — different row counts must appear.
Agent Studio · New tool (Script) {BASE}/now/ai-agent-studio
Agents
Tools
Workflows
Evaluations
Namelist_my_leave_requests
TypeScript Include
IncludeLabAIReadTools
MethodlistLeaveForUser
ExecutionInvoking user (via agent data access)
You are here: tool wiring — business logic stays in the Script Include, not in the prompt.

3.1 Script Include — Track A (leave, read-only)

var LabAIReadTools = Class.create();
LabAIReadTools.prototype = {
    initialize: function() {
        this.T_REQ = 'x_786686_hr_leave_req_v2';
        this.T_EMP = 'x_786686_hr_leave_employee_v2';
    },

    _employeeSysIdForUser: function(userId) {
        var gr = new GlideRecordSecure(this.T_EMP);
        gr.addQuery('user', userId);
        gr.setLimit(1);
        gr.query();
        return gr.next() ? gr.getUniqueValue() : null;
    },

    /** AI Agent tool — list leave requests visible to invoking user via ACLs */
    listLeaveForUser: function() {
        var userId = gs.getUserID();
        var empId = this._employeeSysIdForUser(userId);
        if (!empId) return { ok: true, rows: [], note: 'no_employee_profile' };

        var out = [];
        var req = new GlideRecordSecure(this.T_REQ);
        req.addQuery('u_employee_ref', empId);
        req.orderByDesc('sys_created_on');
        req.setLimit(25);
        req.query();
        while (req.next()) {
            out.push({
                number: req.getValue('number'),
                state: req.getDisplayValue('u_state'),
                type: req.getDisplayValue('u_leave_type'),
                start: req.getValue('u_start_date'),
                end: req.getValue('u_end_date'),
                days: req.getValue('u_days')
            });
        }
        return { ok: true, count: out.length, rows: out };
    },

    type: 'LabAIReadTools'
};

3.2 Script Include — Track B (incidents, read-only)

var LabAIReadTools = Class.create();
LabAIReadTools.prototype = {
    initialize: function() {},

    /** AI Agent tool — open incidents the invoking user may read (ACL-enforced) */
    listOpenIncidents: function(limit) {
        var max = Math.min(parseInt(limit, 10) || 10, 25);
        var out = [];
        var inc = new GlideRecordSecure('incident');
        inc.addActiveQuery();
        inc.orderByDesc('opened_at');
        inc.setLimit(max);
        inc.query();
        while (inc.next()) {
            out.push({
                number: inc.getValue('number'),
                short_description: inc.getValue('short_description'),
                state: inc.getDisplayValue('state'),
                priority: inc.getDisplayValue('priority'),
                assignment_group: inc.getDisplayValue('assignment_group')
            });
        }
        return { ok: true, count: out.length, rows: out };
    },

    getIncidentByNumber: function(number) {
        if (!number) return { ok: false, error: 'number_required' };
        var inc = new GlideRecordSecure('incident');
        inc.addQuery('number', number);
        inc.setLimit(1);
        inc.query();
        if (!inc.next()) return { ok: false, error: 'not_found_or_denied' };
        return {
            ok: true,
            number: inc.getValue('number'),
            description: inc.getValue('description'),
            state: inc.getDisplayValue('state'),
            opened_at: inc.getValue('opened_at')
        };
    },

    type: 'LabAIReadTools'
};

v1 rule: do not register requestLeave, state changes, or Flow actions yet. First release = read + summarize only. Add writes only after ACL review and Control Tower registration.

Pass criteria: test trace shows tool call → JSON rows → natural language answer; impersonating a user with no rows returns empty list, not an error stack; no field named u_state or incident.state is updated by the tool.

4. Agentic workflow vs single agent

SINGLEOne LLM + tools
→
PLANPick tool per turn
→
WORKFLOWOrchestrated graph
→
ROUTESpecialists
PatternWhen to useWhere in UI
Single AI AgentOne domain, few tools, conversational Q&A (this lab).Agent Studio → AI Agents
Agentic workflowMulti-step process with branches, hand-offs, or separate “specialist” agents (triage → enrich → notify).Agent Studio → Agentic Workflows
Virtual Agent topicFixed dialog, predictable utterances, compliance scripts.VA Designer / topics

Start with a single agent. Promote to agentic workflow when you can draw the steps on a whiteboard and name which agent owns each step.

5. RAG — practical steps (high level)

Retrieval Augmented Generation = give the model relevant text at question time; do not rely on training memory. On ServiceNow you usually implement retrieval first, generation second.

IngestKB articles, policy docs, scoped tables — only published/current sources.
IndexAI Search application + sources + relevancy profile.
RetrieveTop-k chunks with minimum score; respect user criteria + ACLs.
GenerateNow Assist skill, AI Search gen answer, or agent tool (search_retrieval / custom Script Include).
✓ Filter kb_knowledge published ✓ Citations in answers ✗ Dump entire KB into prompt ✗ RAG before source hygiene

Deep implementation (three patterns A/B/C): AI Full Demo Master — Part 7 (RAG).

6. MCP — practical pointer

Model Context Protocol connects AI clients (Cursor, Claude Desktop, agent frameworks) to tools through a standard interface. For ServiceNow labs, the security boundary stays on the instance: allow-listed Script Includes or Scripted REST, not ad-hoc SQL from a laptop.

Do this next on a PDI: follow AI_SERVICENOW_FULL_DEMO_MASTER.html — Part 8 (MCP) — Scripted REST tool surface on the instance, thin local MCP server, editor config. Control Tower can inventory MCP servers as governed AI assets when licensed.

IDEMCP client
→
LOCALstdio adapter
→
SNREST + ACLs
→
DATAAllow-list

7. Define data access — invoking user vs AI User

ModeRuntime identityUse whenRisk
Invoking userSame roles/ACLs as the human who triggered the agent.Default for employee/manager self-service (this lab).Low — behavior matches portal ACLs.
AI UserDedicated service account with its own roles.Scheduled/unattended jobs where no human is in session.High if over-roled — never admin for convenience.
AI Agent · Data access Agent definition
General
Instructions
Tools
Triggers
AccessInvoking user ✓
AI User(empty — add only with written justification)
Toolslist_my_leave_requests
Traceshows gs.getUserID() context
Rule: switch to AI User one role at a time only after invoking-user tests document a real gap.

Production incident pattern: AI User granted admin to unblock a demo → agent summarizes restricted records into a public channel. Default invoking user unless you can explain why not in one sentence.

8. Best practices — do / don’t

✓ GlideRecordSecure in tools ✓ Read-mostly first release ✓ Register in Control Tower ✓ Export test traces ✓ Narrow tool descriptions ✗ Tools write u_state / assignment ✗ Bypass ACLs in Script Include ✗ Shadow API keys in chat ✗ Unregistered agents in prod ✗ GlideRecord without Secure

First release contract: agents may read and explain; humans or approved Flows still commit state changes until security sign-off.

9. ServiceNow SDK — what it CAN and CANNOT do (AI agents)

From SDK 4.4+, Fluent APIs include AiAgent, agent tools, triggers, and agentic workflow artefacts — same governance tables as UI (sn_aia_*), expressed as TypeScript in your repo.

SDK CANSDK CANNOT
Define agents, tools, triggers as code; review in PRs; deploy with npx @servicenow/sdk build/deploy.
Follow official topics: building-ai-agents-guide, building-ai-agents-tools-guide, building-ai-agents-advanced-guide (see SDK Cursor documentation topic list).
Replace AI Control Tower governance (inventory, policy, monitoring still required).
Invent arbitrary tools without ACL allow-list and scoped Script Includes — runtime still enforces platform security.
Skip licensing — SKUs must exist on the target instance.
// Conceptual Fluent shape — see building-ai-agents-guide in SDK docs
import { AiAgent } from '@servicenow/sdk/core'

export const leaveReadAgent = AiAgent({
  name: 'Leave Read Assistant (lab)',
  description: 'Read-only leave visibility for invoking user',
  // tools, instructions, triggers: defined alongside Script Include APIs in repo
})

Full SDK + MCP pairing: Full Demo Part 10 · Local SDK walkthrough: 16 SDK from scratch.

Core → Practical AI → Full demo