servicenow-mcp 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2759 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ServiceNow MCP Server
4
+ *
5
+ * Comprehensive MCP for ServiceNow ITSM operations:
6
+ * - Incidents, Changes, Problems
7
+ * - Service Catalog & Requests
8
+ * - CMDB Configuration Items
9
+ * - Knowledge Base
10
+ * - Users, Groups, Approvals
11
+ *
12
+ * Supports:
13
+ * - Browser-based SSO authentication (recommended for enterprise)
14
+ * - REST Table API (basic auth)
15
+ * - GraphQL API (session-based)
16
+ */
17
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
20
+ import { loadCookies, authenticateViaBrowser } from "./auth-browser.js";
21
+ // Configuration from environment
22
+ const INSTANCE_URL = process.env.SERVICENOW_INSTANCE_URL || "";
23
+ const USERNAME = process.env.SERVICENOW_USERNAME || "";
24
+ const PASSWORD = process.env.SERVICENOW_PASSWORD || "";
25
+ const SESSION_TOKEN = process.env.SERVICENOW_SESSION_TOKEN || "";
26
+ const USER_TOKEN = process.env.SERVICENOW_USER_TOKEN || "";
27
+ // Load browser auth cookies if available (mutable to allow hot-reload)
28
+ let browserAuth = loadCookies();
29
+ // Function to reload cookies without restarting the server
30
+ function reloadBrowserAuth() {
31
+ const newAuth = loadCookies();
32
+ if (newAuth) {
33
+ browserAuth = newAuth;
34
+ console.error("✅ Browser auth cookies reloaded successfully");
35
+ return true;
36
+ }
37
+ console.error("❌ Failed to reload browser auth cookies");
38
+ return false;
39
+ }
40
+ // HTTP client with connection pooling
41
+ import { Agent } from "node:https";
42
+ const httpsAgent = new Agent({ keepAlive: true, maxSockets: 10 });
43
+ // ============================================================================
44
+ // Tool Definitions
45
+ // ============================================================================
46
+ const TOOLS = [
47
+ // -------------------------------------------------------------------------
48
+ // AUTHENTICATION
49
+ // -------------------------------------------------------------------------
50
+ {
51
+ name: "auth_browser",
52
+ description: "Launch browser for SSO authentication. Opens a browser window where you can log in with your enterprise SSO credentials. Cookies are captured and saved for subsequent API calls. Use this when you don't have API keys and need to use SSO.",
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: {
56
+ instance_url: {
57
+ type: "string",
58
+ description: "ServiceNow instance URL (e.g., https://mycompany.service-now.com). If not provided, uses SERVICENOW_INSTANCE_URL env var.",
59
+ },
60
+ },
61
+ },
62
+ },
63
+ {
64
+ name: "auth_status",
65
+ description: "Check current authentication status. Shows which auth method is configured and whether credentials/cookies are valid.",
66
+ inputSchema: {
67
+ type: "object",
68
+ properties: {},
69
+ },
70
+ },
71
+ // -------------------------------------------------------------------------
72
+ // UNIFIED WORK QUEUE (Priority tools for finding actionable items)
73
+ // -------------------------------------------------------------------------
74
+ {
75
+ name: "my_context",
76
+ description: "Get current user's identity, groups, and roles. Returns cached user info including sys_id, name, email, department, group memberships, and roles. Use this first to understand who you're working as.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {},
80
+ },
81
+ },
82
+ {
83
+ name: "my_work_queue",
84
+ description: "Get ALL actionable items for current user in a single call: assigned tasks, pending approvals, and unassigned group queue items. This is the recommended first tool to use when user asks 'what do I need to work on?' or 'what are my open tasks?'",
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ include: {
89
+ type: "array",
90
+ items: {
91
+ type: "string",
92
+ enum: ["assigned", "approvals", "group_queue"],
93
+ },
94
+ description: "Which categories to include (default: all three)",
95
+ default: ["assigned", "approvals", "group_queue"],
96
+ },
97
+ keyword: {
98
+ type: "string",
99
+ description: "Filter results by keyword in short_description (e.g., 'firewall', 'SAP')",
100
+ },
101
+ limit: {
102
+ type: "number",
103
+ description: "Max results per category (default 20)",
104
+ default: 20,
105
+ },
106
+ },
107
+ },
108
+ },
109
+ // -------------------------------------------------------------------------
110
+ // INCIDENTS
111
+ // -------------------------------------------------------------------------
112
+ {
113
+ name: "incidents_list",
114
+ description: "List incidents with optional filtering. Returns incident number, short description, state, priority, assigned_to.",
115
+ inputSchema: {
116
+ type: "object",
117
+ properties: {
118
+ query: {
119
+ type: "string",
120
+ description: "ServiceNow encoded query (e.g., 'state=1^priority=1' for new P1s)",
121
+ },
122
+ limit: {
123
+ type: "number",
124
+ description: "Max results (default 20)",
125
+ default: 20,
126
+ },
127
+ offset: {
128
+ type: "number",
129
+ description: "Pagination offset",
130
+ default: 0,
131
+ },
132
+ fields: {
133
+ type: "string",
134
+ description: "Comma-separated fields to return",
135
+ },
136
+ },
137
+ },
138
+ },
139
+ {
140
+ name: "incidents_get",
141
+ description: "Get a single incident by sys_id or number",
142
+ inputSchema: {
143
+ type: "object",
144
+ properties: {
145
+ id: {
146
+ type: "string",
147
+ description: "Incident sys_id or number (e.g., INC0012345)",
148
+ },
149
+ },
150
+ required: ["id"],
151
+ },
152
+ },
153
+ {
154
+ name: "incidents_create",
155
+ description: "Create a new incident",
156
+ inputSchema: {
157
+ type: "object",
158
+ properties: {
159
+ short_description: {
160
+ type: "string",
161
+ description: "Brief description of the issue",
162
+ },
163
+ description: { type: "string", description: "Detailed description" },
164
+ caller_id: {
165
+ type: "string",
166
+ description: "User sys_id or email of the caller",
167
+ },
168
+ category: { type: "string", description: "Incident category" },
169
+ subcategory: { type: "string", description: "Incident subcategory" },
170
+ priority: { type: "number", description: "Priority 1-5 (1=Critical)" },
171
+ assignment_group: {
172
+ type: "string",
173
+ description: "Assignment group sys_id or name",
174
+ },
175
+ cmdb_ci: { type: "string", description: "Configuration item sys_id" },
176
+ },
177
+ required: ["short_description"],
178
+ },
179
+ },
180
+ {
181
+ name: "incidents_update",
182
+ description: "Update an existing incident",
183
+ inputSchema: {
184
+ type: "object",
185
+ properties: {
186
+ id: { type: "string", description: "Incident sys_id or number" },
187
+ data: {
188
+ type: "object",
189
+ description: "Fields to update (state, priority, assigned_to, work_notes, etc.)",
190
+ additionalProperties: true,
191
+ },
192
+ },
193
+ required: ["id", "data"],
194
+ },
195
+ },
196
+ {
197
+ name: "incidents_add_comment",
198
+ description: "Add a work note or comment to an incident",
199
+ inputSchema: {
200
+ type: "object",
201
+ properties: {
202
+ id: { type: "string", description: "Incident sys_id or number" },
203
+ comment: { type: "string", description: "The comment/work note text" },
204
+ type: {
205
+ type: "string",
206
+ enum: ["work_notes", "comments"],
207
+ description: "work_notes (internal) or comments (customer visible)",
208
+ default: "work_notes",
209
+ },
210
+ },
211
+ required: ["id", "comment"],
212
+ },
213
+ },
214
+ {
215
+ name: "incidents_resolve",
216
+ description: "Resolve an incident",
217
+ inputSchema: {
218
+ type: "object",
219
+ properties: {
220
+ id: { type: "string", description: "Incident sys_id or number" },
221
+ resolution_code: { type: "string", description: "Resolution code" },
222
+ resolution_notes: {
223
+ type: "string",
224
+ description: "Resolution notes/close notes",
225
+ },
226
+ },
227
+ required: ["id", "resolution_notes"],
228
+ },
229
+ },
230
+ // -------------------------------------------------------------------------
231
+ // CHANGE REQUESTS
232
+ // -------------------------------------------------------------------------
233
+ {
234
+ name: "changes_list",
235
+ description: "List change requests with optional filtering",
236
+ inputSchema: {
237
+ type: "object",
238
+ properties: {
239
+ query: { type: "string", description: "ServiceNow encoded query" },
240
+ limit: { type: "number", default: 20 },
241
+ offset: { type: "number", default: 0 },
242
+ type: {
243
+ type: "string",
244
+ enum: ["standard", "normal", "emergency"],
245
+ description: "Filter by change type",
246
+ },
247
+ },
248
+ },
249
+ },
250
+ {
251
+ name: "changes_get",
252
+ description: "Get a single change request by sys_id or number",
253
+ inputSchema: {
254
+ type: "object",
255
+ properties: {
256
+ id: {
257
+ type: "string",
258
+ description: "Change request sys_id or number (e.g., CHG0012345)",
259
+ },
260
+ },
261
+ required: ["id"],
262
+ },
263
+ },
264
+ {
265
+ name: "changes_create",
266
+ description: "Create a new change request",
267
+ inputSchema: {
268
+ type: "object",
269
+ properties: {
270
+ short_description: { type: "string" },
271
+ description: { type: "string" },
272
+ type: { type: "string", enum: ["standard", "normal", "emergency"] },
273
+ category: { type: "string" },
274
+ assignment_group: { type: "string" },
275
+ cmdb_ci: { type: "string" },
276
+ start_date: {
277
+ type: "string",
278
+ description: "Planned start (ISO format)",
279
+ },
280
+ end_date: { type: "string", description: "Planned end (ISO format)" },
281
+ justification: { type: "string" },
282
+ implementation_plan: { type: "string" },
283
+ backout_plan: { type: "string" },
284
+ test_plan: { type: "string" },
285
+ },
286
+ required: ["short_description", "type"],
287
+ },
288
+ },
289
+ {
290
+ name: "changes_tasks",
291
+ description: "Get tasks associated with a change request",
292
+ inputSchema: {
293
+ type: "object",
294
+ properties: {
295
+ id: { type: "string", description: "Change request sys_id or number" },
296
+ },
297
+ required: ["id"],
298
+ },
299
+ },
300
+ // -------------------------------------------------------------------------
301
+ // SERVICE CATALOG
302
+ // -------------------------------------------------------------------------
303
+ {
304
+ name: "catalog_items",
305
+ description: "List available service catalog items",
306
+ inputSchema: {
307
+ type: "object",
308
+ properties: {
309
+ category: {
310
+ type: "string",
311
+ description: "Filter by category sys_id or name",
312
+ },
313
+ query: { type: "string", description: "Search text" },
314
+ limit: { type: "number", default: 20 },
315
+ },
316
+ },
317
+ },
318
+ {
319
+ name: "catalog_get",
320
+ description: "Get details of a catalog item including variables/questions",
321
+ inputSchema: {
322
+ type: "object",
323
+ properties: {
324
+ id: { type: "string", description: "Catalog item sys_id" },
325
+ },
326
+ required: ["id"],
327
+ },
328
+ },
329
+ {
330
+ name: "catalog_order",
331
+ description: "Order/submit a catalog item request",
332
+ inputSchema: {
333
+ type: "object",
334
+ properties: {
335
+ item_id: { type: "string", description: "Catalog item sys_id" },
336
+ variables: {
337
+ type: "object",
338
+ description: "Variable values for the request",
339
+ additionalProperties: true,
340
+ },
341
+ requested_for: {
342
+ type: "string",
343
+ description: "User sys_id (defaults to current user)",
344
+ },
345
+ quantity: { type: "number", default: 1 },
346
+ },
347
+ required: ["item_id"],
348
+ },
349
+ },
350
+ {
351
+ name: "requests_list",
352
+ description: "List service requests (sc_request) for current user or with filter",
353
+ inputSchema: {
354
+ type: "object",
355
+ properties: {
356
+ query: { type: "string" },
357
+ limit: { type: "number", default: 20 },
358
+ my_requests: {
359
+ type: "boolean",
360
+ description: "Only show my requests",
361
+ default: true,
362
+ },
363
+ },
364
+ },
365
+ },
366
+ {
367
+ name: "requests_items",
368
+ description: "List requested items (sc_req_item) with status",
369
+ inputSchema: {
370
+ type: "object",
371
+ properties: {
372
+ query: { type: "string" },
373
+ limit: { type: "number", default: 20 },
374
+ },
375
+ },
376
+ },
377
+ // -------------------------------------------------------------------------
378
+ // CMDB
379
+ // -------------------------------------------------------------------------
380
+ {
381
+ name: "cmdb_search",
382
+ description: "Search CMDB configuration items",
383
+ inputSchema: {
384
+ type: "object",
385
+ properties: {
386
+ query: { type: "string", description: "Search text or encoded query" },
387
+ class: {
388
+ type: "string",
389
+ description: "CI class (cmdb_ci_server, cmdb_ci_app, etc.)",
390
+ default: "cmdb_ci",
391
+ },
392
+ limit: { type: "number", default: 20 },
393
+ },
394
+ required: ["query"],
395
+ },
396
+ },
397
+ {
398
+ name: "cmdb_get",
399
+ description: "Get configuration item details",
400
+ inputSchema: {
401
+ type: "object",
402
+ properties: {
403
+ id: { type: "string", description: "CI sys_id or name" },
404
+ class: {
405
+ type: "string",
406
+ description: "CI class table",
407
+ default: "cmdb_ci",
408
+ },
409
+ },
410
+ required: ["id"],
411
+ },
412
+ },
413
+ {
414
+ name: "cmdb_relationships",
415
+ description: "Get relationships for a configuration item",
416
+ inputSchema: {
417
+ type: "object",
418
+ properties: {
419
+ id: { type: "string", description: "CI sys_id" },
420
+ direction: {
421
+ type: "string",
422
+ enum: ["parent", "child", "both"],
423
+ default: "both",
424
+ },
425
+ },
426
+ required: ["id"],
427
+ },
428
+ },
429
+ // -------------------------------------------------------------------------
430
+ // PROBLEMS
431
+ // -------------------------------------------------------------------------
432
+ {
433
+ name: "problems_list",
434
+ description: "List problem records",
435
+ inputSchema: {
436
+ type: "object",
437
+ properties: {
438
+ query: { type: "string" },
439
+ limit: { type: "number", default: 20 },
440
+ },
441
+ },
442
+ },
443
+ {
444
+ name: "problems_get",
445
+ description: "Get a problem record",
446
+ inputSchema: {
447
+ type: "object",
448
+ properties: {
449
+ id: { type: "string", description: "Problem sys_id or number" },
450
+ },
451
+ required: ["id"],
452
+ },
453
+ },
454
+ {
455
+ name: "problems_create",
456
+ description: "Create a problem record",
457
+ inputSchema: {
458
+ type: "object",
459
+ properties: {
460
+ short_description: { type: "string" },
461
+ description: { type: "string" },
462
+ category: { type: "string" },
463
+ subcategory: { type: "string" },
464
+ assignment_group: { type: "string" },
465
+ cmdb_ci: { type: "string" },
466
+ },
467
+ required: ["short_description"],
468
+ },
469
+ },
470
+ // -------------------------------------------------------------------------
471
+ // KNOWLEDGE
472
+ // -------------------------------------------------------------------------
473
+ {
474
+ name: "knowledge_search",
475
+ description: "Search knowledge base articles",
476
+ inputSchema: {
477
+ type: "object",
478
+ properties: {
479
+ query: { type: "string", description: "Search text" },
480
+ knowledge_base: {
481
+ type: "string",
482
+ description: "KB sys_id to search in",
483
+ },
484
+ limit: { type: "number", default: 10 },
485
+ },
486
+ required: ["query"],
487
+ },
488
+ },
489
+ {
490
+ name: "knowledge_get",
491
+ description: "Get a knowledge article",
492
+ inputSchema: {
493
+ type: "object",
494
+ properties: {
495
+ id: { type: "string", description: "Article sys_id or number" },
496
+ },
497
+ required: ["id"],
498
+ },
499
+ },
500
+ // -------------------------------------------------------------------------
501
+ // USERS & GROUPS
502
+ // -------------------------------------------------------------------------
503
+ {
504
+ name: "users_search",
505
+ description: "Search for users",
506
+ inputSchema: {
507
+ type: "object",
508
+ properties: {
509
+ query: {
510
+ type: "string",
511
+ description: "Name, email, or user_name to search",
512
+ },
513
+ limit: { type: "number", default: 10 },
514
+ },
515
+ required: ["query"],
516
+ },
517
+ },
518
+ {
519
+ name: "users_get",
520
+ description: "Get user details",
521
+ inputSchema: {
522
+ type: "object",
523
+ properties: {
524
+ id: { type: "string", description: "User sys_id, user_name, or email" },
525
+ },
526
+ required: ["id"],
527
+ },
528
+ },
529
+ {
530
+ name: "groups_list",
531
+ description: "List groups",
532
+ inputSchema: {
533
+ type: "object",
534
+ properties: {
535
+ query: { type: "string", description: "Filter by name" },
536
+ type: { type: "string", description: "Group type" },
537
+ limit: { type: "number", default: 20 },
538
+ },
539
+ },
540
+ },
541
+ {
542
+ name: "groups_members",
543
+ description: "Get members of a group",
544
+ inputSchema: {
545
+ type: "object",
546
+ properties: {
547
+ id: { type: "string", description: "Group sys_id or name" },
548
+ },
549
+ required: ["id"],
550
+ },
551
+ },
552
+ // -------------------------------------------------------------------------
553
+ // TASKS & APPROVALS
554
+ // -------------------------------------------------------------------------
555
+ {
556
+ name: "tasks_my_tasks",
557
+ description: "Get tasks assigned to current user",
558
+ inputSchema: {
559
+ type: "object",
560
+ properties: {
561
+ state: {
562
+ type: "string",
563
+ description: "Filter by state (e.g., 'open', 'pending')",
564
+ },
565
+ limit: { type: "number", default: 20 },
566
+ },
567
+ },
568
+ },
569
+ {
570
+ name: "tasks_update",
571
+ description: "Update a task",
572
+ inputSchema: {
573
+ type: "object",
574
+ properties: {
575
+ id: { type: "string", description: "Task sys_id or number" },
576
+ data: { type: "object", additionalProperties: true },
577
+ },
578
+ required: ["id", "data"],
579
+ },
580
+ },
581
+ {
582
+ name: "approvals_pending",
583
+ description: "Get pending approvals for current user with ENRICHED details from parent records. Returns number, short_description, opened_by, urgency, and stage - not just approval metadata. Supports keyword filtering.",
584
+ inputSchema: {
585
+ type: "object",
586
+ properties: {
587
+ keyword: {
588
+ type: "string",
589
+ description: "Filter by keyword in parent record's short_description (e.g., 'firewall', 'access')",
590
+ },
591
+ type: {
592
+ type: "string",
593
+ enum: ["all", "sc_req_item", "change_request", "sc_task"],
594
+ description: "Filter by parent record type",
595
+ default: "all",
596
+ },
597
+ limit: { type: "number", default: 20 },
598
+ },
599
+ },
600
+ },
601
+ {
602
+ name: "approvals_approve",
603
+ description: "Approve an approval request",
604
+ inputSchema: {
605
+ type: "object",
606
+ properties: {
607
+ id: { type: "string", description: "Approval sys_id" },
608
+ comments: { type: "string", description: "Approval comments" },
609
+ },
610
+ required: ["id"],
611
+ },
612
+ },
613
+ {
614
+ name: "approvals_reject",
615
+ description: "Reject an approval request",
616
+ inputSchema: {
617
+ type: "object",
618
+ properties: {
619
+ id: { type: "string", description: "Approval sys_id" },
620
+ comments: { type: "string", description: "Rejection reason" },
621
+ },
622
+ required: ["id", "comments"],
623
+ },
624
+ },
625
+ // -------------------------------------------------------------------------
626
+ // ATTACHMENTS (Full API)
627
+ // -------------------------------------------------------------------------
628
+ {
629
+ name: "attachment_list",
630
+ description: "List attachments for a record",
631
+ inputSchema: {
632
+ type: "object",
633
+ properties: {
634
+ table: { type: "string", description: "Table name" },
635
+ id: { type: "string", description: "Record sys_id" },
636
+ },
637
+ required: ["table", "id"],
638
+ },
639
+ },
640
+ {
641
+ name: "attachment_get",
642
+ description: "Get attachment metadata by sys_id",
643
+ inputSchema: {
644
+ type: "object",
645
+ properties: {
646
+ id: { type: "string", description: "Attachment sys_id" },
647
+ },
648
+ required: ["id"],
649
+ },
650
+ },
651
+ {
652
+ name: "attachment_download",
653
+ description: "Get attachment content (returns base64 encoded for binary files)",
654
+ inputSchema: {
655
+ type: "object",
656
+ properties: {
657
+ id: { type: "string", description: "Attachment sys_id" },
658
+ },
659
+ required: ["id"],
660
+ },
661
+ },
662
+ {
663
+ name: "attachment_upload",
664
+ description: "Upload an attachment to a record",
665
+ inputSchema: {
666
+ type: "object",
667
+ properties: {
668
+ table: { type: "string", description: "Target table name" },
669
+ id: { type: "string", description: "Target record sys_id" },
670
+ filename: { type: "string", description: "File name" },
671
+ content: {
672
+ type: "string",
673
+ description: "Base64 encoded file content",
674
+ },
675
+ content_type: {
676
+ type: "string",
677
+ description: "MIME type (e.g., application/pdf)",
678
+ default: "application/octet-stream",
679
+ },
680
+ },
681
+ required: ["table", "id", "filename", "content"],
682
+ },
683
+ },
684
+ {
685
+ name: "attachment_delete",
686
+ description: "Delete an attachment",
687
+ inputSchema: {
688
+ type: "object",
689
+ properties: {
690
+ id: { type: "string", description: "Attachment sys_id" },
691
+ },
692
+ required: ["id"],
693
+ },
694
+ },
695
+ // -------------------------------------------------------------------------
696
+ // SERVICE CATALOG (Full sn_sc API)
697
+ // -------------------------------------------------------------------------
698
+ {
699
+ name: "catalog_categories",
700
+ description: "List service catalog categories",
701
+ inputSchema: {
702
+ type: "object",
703
+ properties: {
704
+ catalog_id: {
705
+ type: "string",
706
+ description: "Catalog sys_id (optional)",
707
+ },
708
+ limit: { type: "number", default: 50 },
709
+ },
710
+ },
711
+ },
712
+ {
713
+ name: "catalog_item_variables",
714
+ description: "Get variables/questions for a catalog item (required fields for ordering)",
715
+ inputSchema: {
716
+ type: "object",
717
+ properties: {
718
+ id: { type: "string", description: "Catalog item sys_id" },
719
+ },
720
+ required: ["id"],
721
+ },
722
+ },
723
+ {
724
+ name: "catalog_add_to_cart",
725
+ description: "Add a catalog item to the shopping cart",
726
+ inputSchema: {
727
+ type: "object",
728
+ properties: {
729
+ item_id: { type: "string", description: "Catalog item sys_id" },
730
+ quantity: { type: "number", default: 1 },
731
+ variables: {
732
+ type: "object",
733
+ description: "Variable values for the item",
734
+ additionalProperties: true,
735
+ },
736
+ },
737
+ required: ["item_id"],
738
+ },
739
+ },
740
+ {
741
+ name: "catalog_get_cart",
742
+ description: "Get current shopping cart contents",
743
+ inputSchema: {
744
+ type: "object",
745
+ properties: {},
746
+ },
747
+ },
748
+ {
749
+ name: "catalog_submit_cart",
750
+ description: "Submit/checkout the shopping cart to create requests",
751
+ inputSchema: {
752
+ type: "object",
753
+ properties: {},
754
+ },
755
+ },
756
+ {
757
+ name: "catalog_order_now",
758
+ description: "Order a catalog item immediately (skip cart, single-step order)",
759
+ inputSchema: {
760
+ type: "object",
761
+ properties: {
762
+ item_id: { type: "string", description: "Catalog item sys_id" },
763
+ quantity: { type: "number", default: 1 },
764
+ variables: {
765
+ type: "object",
766
+ description: "Variable values for the item",
767
+ additionalProperties: true,
768
+ },
769
+ requested_for: {
770
+ type: "string",
771
+ description: "User sys_id to request for",
772
+ },
773
+ },
774
+ required: ["item_id"],
775
+ },
776
+ },
777
+ // -------------------------------------------------------------------------
778
+ // IMPORT SET API
779
+ // -------------------------------------------------------------------------
780
+ {
781
+ name: "import_set_load",
782
+ description: "Load data into an import set staging table",
783
+ inputSchema: {
784
+ type: "object",
785
+ properties: {
786
+ table: {
787
+ type: "string",
788
+ description: "Import set table name (e.g., u_my_import_set)",
789
+ },
790
+ data: {
791
+ type: "object",
792
+ description: "Data to import (single record)",
793
+ additionalProperties: true,
794
+ },
795
+ },
796
+ required: ["table", "data"],
797
+ },
798
+ },
799
+ {
800
+ name: "import_set_load_multiple",
801
+ description: "Load multiple records into an import set staging table",
802
+ inputSchema: {
803
+ type: "object",
804
+ properties: {
805
+ table: {
806
+ type: "string",
807
+ description: "Import set table name",
808
+ },
809
+ records: {
810
+ type: "array",
811
+ items: { type: "object" },
812
+ description: "Array of records to import",
813
+ },
814
+ },
815
+ required: ["table", "records"],
816
+ },
817
+ },
818
+ {
819
+ name: "import_set_status",
820
+ description: "Get status of an import set",
821
+ inputSchema: {
822
+ type: "object",
823
+ properties: {
824
+ id: { type: "string", description: "Import set sys_id" },
825
+ },
826
+ required: ["id"],
827
+ },
828
+ },
829
+ // -------------------------------------------------------------------------
830
+ // BATCH API
831
+ // -------------------------------------------------------------------------
832
+ {
833
+ name: "batch_request",
834
+ description: "Execute multiple API requests in a single call",
835
+ inputSchema: {
836
+ type: "object",
837
+ properties: {
838
+ requests: {
839
+ type: "array",
840
+ description: "Array of request objects",
841
+ items: {
842
+ type: "object",
843
+ properties: {
844
+ id: { type: "string", description: "Request identifier" },
845
+ method: {
846
+ type: "string",
847
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
848
+ },
849
+ url: { type: "string", description: "Relative URL path" },
850
+ body: {
851
+ type: "object",
852
+ description: "Request body for POST/PUT",
853
+ },
854
+ headers: { type: "object", description: "Additional headers" },
855
+ },
856
+ required: ["id", "method", "url"],
857
+ },
858
+ },
859
+ },
860
+ required: ["requests"],
861
+ },
862
+ },
863
+ // -------------------------------------------------------------------------
864
+ // CMDB INSTANCE API (Advanced)
865
+ // -------------------------------------------------------------------------
866
+ {
867
+ name: "cmdb_classes",
868
+ description: "List CMDB CI classes/types",
869
+ inputSchema: {
870
+ type: "object",
871
+ properties: {
872
+ parent_class: {
873
+ type: "string",
874
+ description: "Parent class to filter by",
875
+ default: "cmdb_ci",
876
+ },
877
+ limit: { type: "number", default: 50 },
878
+ },
879
+ },
880
+ },
881
+ {
882
+ name: "cmdb_instance_list",
883
+ description: "List CMDB instances by class (uses CMDB Instance API /now/cmdb/instance)",
884
+ inputSchema: {
885
+ type: "object",
886
+ properties: {
887
+ class_name: {
888
+ type: "string",
889
+ description: "CI class name (e.g., cmdb_ci_server, cmdb_ci_linux_server)",
890
+ },
891
+ query: { type: "string", description: "Filter query" },
892
+ limit: { type: "number", default: 20 },
893
+ },
894
+ required: ["class_name"],
895
+ },
896
+ },
897
+ {
898
+ name: "cmdb_create",
899
+ description: "Create a new CMDB configuration item",
900
+ inputSchema: {
901
+ type: "object",
902
+ properties: {
903
+ class_name: {
904
+ type: "string",
905
+ description: "CI class (e.g., cmdb_ci_server)",
906
+ },
907
+ data: {
908
+ type: "object",
909
+ description: "CI attributes",
910
+ additionalProperties: true,
911
+ },
912
+ },
913
+ required: ["class_name", "data"],
914
+ },
915
+ },
916
+ {
917
+ name: "cmdb_update",
918
+ description: "Update a CMDB configuration item",
919
+ inputSchema: {
920
+ type: "object",
921
+ properties: {
922
+ id: { type: "string", description: "CI sys_id" },
923
+ class_name: { type: "string", description: "CI class" },
924
+ data: { type: "object", additionalProperties: true },
925
+ },
926
+ required: ["id", "data"],
927
+ },
928
+ },
929
+ {
930
+ name: "cmdb_relationship_create",
931
+ description: "Create a relationship between two CIs",
932
+ inputSchema: {
933
+ type: "object",
934
+ properties: {
935
+ parent: { type: "string", description: "Parent CI sys_id" },
936
+ child: { type: "string", description: "Child CI sys_id" },
937
+ type: {
938
+ type: "string",
939
+ description: "Relationship type sys_id or name",
940
+ },
941
+ },
942
+ required: ["parent", "child", "type"],
943
+ },
944
+ },
945
+ // -------------------------------------------------------------------------
946
+ // TASK SLA
947
+ // -------------------------------------------------------------------------
948
+ {
949
+ name: "sla_list",
950
+ description: "List SLA definitions",
951
+ inputSchema: {
952
+ type: "object",
953
+ properties: {
954
+ query: { type: "string" },
955
+ limit: { type: "number", default: 20 },
956
+ },
957
+ },
958
+ },
959
+ {
960
+ name: "task_sla_list",
961
+ description: "List task SLAs (SLA records attached to tasks)",
962
+ inputSchema: {
963
+ type: "object",
964
+ properties: {
965
+ task_id: {
966
+ type: "string",
967
+ description: "Task sys_id to get SLAs for",
968
+ },
969
+ query: { type: "string" },
970
+ limit: { type: "number", default: 20 },
971
+ },
972
+ },
973
+ },
974
+ {
975
+ name: "task_sla_get",
976
+ description: "Get task SLA details",
977
+ inputSchema: {
978
+ type: "object",
979
+ properties: {
980
+ id: { type: "string", description: "Task SLA sys_id" },
981
+ },
982
+ required: ["id"],
983
+ },
984
+ },
985
+ // -------------------------------------------------------------------------
986
+ // WORKFLOW
987
+ // -------------------------------------------------------------------------
988
+ {
989
+ name: "workflow_list",
990
+ description: "List workflow definitions",
991
+ inputSchema: {
992
+ type: "object",
993
+ properties: {
994
+ query: { type: "string" },
995
+ active: { type: "boolean", default: true },
996
+ limit: { type: "number", default: 20 },
997
+ },
998
+ },
999
+ },
1000
+ {
1001
+ name: "workflow_context_list",
1002
+ description: "List workflow contexts (running workflow instances)",
1003
+ inputSchema: {
1004
+ type: "object",
1005
+ properties: {
1006
+ table: {
1007
+ type: "string",
1008
+ description: "Filter by table (e.g., incident)",
1009
+ },
1010
+ record_id: {
1011
+ type: "string",
1012
+ description: "Filter by record sys_id",
1013
+ },
1014
+ state: {
1015
+ type: "string",
1016
+ enum: ["executing", "finished", "cancelled"],
1017
+ },
1018
+ limit: { type: "number", default: 20 },
1019
+ },
1020
+ },
1021
+ },
1022
+ {
1023
+ name: "workflow_context_get",
1024
+ description: "Get workflow context details",
1025
+ inputSchema: {
1026
+ type: "object",
1027
+ properties: {
1028
+ id: { type: "string", description: "Workflow context sys_id" },
1029
+ },
1030
+ required: ["id"],
1031
+ },
1032
+ },
1033
+ // -------------------------------------------------------------------------
1034
+ // EMAIL / NOTIFICATIONS
1035
+ // -------------------------------------------------------------------------
1036
+ {
1037
+ name: "email_list",
1038
+ description: "List emails (sys_email table)",
1039
+ inputSchema: {
1040
+ type: "object",
1041
+ properties: {
1042
+ query: { type: "string" },
1043
+ type: {
1044
+ type: "string",
1045
+ enum: ["sent", "received", "send-ready", "draft"],
1046
+ },
1047
+ limit: { type: "number", default: 20 },
1048
+ },
1049
+ },
1050
+ },
1051
+ {
1052
+ name: "email_get",
1053
+ description: "Get email details",
1054
+ inputSchema: {
1055
+ type: "object",
1056
+ properties: {
1057
+ id: { type: "string", description: "Email sys_id" },
1058
+ },
1059
+ required: ["id"],
1060
+ },
1061
+ },
1062
+ {
1063
+ name: "notification_list",
1064
+ description: "List email notification rules",
1065
+ inputSchema: {
1066
+ type: "object",
1067
+ properties: {
1068
+ table: { type: "string", description: "Filter by table" },
1069
+ active: { type: "boolean", default: true },
1070
+ limit: { type: "number", default: 20 },
1071
+ },
1072
+ },
1073
+ },
1074
+ // -------------------------------------------------------------------------
1075
+ // EVENTS
1076
+ // -------------------------------------------------------------------------
1077
+ {
1078
+ name: "event_list",
1079
+ description: "List system events (sysevent)",
1080
+ inputSchema: {
1081
+ type: "object",
1082
+ properties: {
1083
+ query: { type: "string" },
1084
+ name: { type: "string", description: "Event name filter" },
1085
+ limit: { type: "number", default: 20 },
1086
+ },
1087
+ },
1088
+ },
1089
+ {
1090
+ name: "event_create",
1091
+ description: "Create/fire a system event",
1092
+ inputSchema: {
1093
+ type: "object",
1094
+ properties: {
1095
+ name: { type: "string", description: "Event name" },
1096
+ instance: {
1097
+ type: "string",
1098
+ description: "Record sys_id (parm1)",
1099
+ },
1100
+ parm2: { type: "string", description: "Parameter 2" },
1101
+ table: { type: "string", description: "Table name" },
1102
+ },
1103
+ required: ["name"],
1104
+ },
1105
+ },
1106
+ // -------------------------------------------------------------------------
1107
+ // JOURNAL / ACTIVITY STREAM
1108
+ // -------------------------------------------------------------------------
1109
+ {
1110
+ name: "journal_list",
1111
+ description: "List journal entries (work notes, comments) for a record",
1112
+ inputSchema: {
1113
+ type: "object",
1114
+ properties: {
1115
+ table: { type: "string", description: "Table name" },
1116
+ id: { type: "string", description: "Record sys_id" },
1117
+ type: {
1118
+ type: "string",
1119
+ enum: ["work_notes", "comments", "all"],
1120
+ default: "all",
1121
+ },
1122
+ limit: { type: "number", default: 50 },
1123
+ },
1124
+ required: ["table", "id"],
1125
+ },
1126
+ },
1127
+ {
1128
+ name: "activity_stream",
1129
+ description: "Get activity stream for a record (all changes, comments)",
1130
+ inputSchema: {
1131
+ type: "object",
1132
+ properties: {
1133
+ table: { type: "string", description: "Table name" },
1134
+ id: { type: "string", description: "Record sys_id" },
1135
+ limit: { type: "number", default: 50 },
1136
+ },
1137
+ required: ["table", "id"],
1138
+ },
1139
+ },
1140
+ // -------------------------------------------------------------------------
1141
+ // AUDIT / HISTORY
1142
+ // -------------------------------------------------------------------------
1143
+ {
1144
+ name: "audit_list",
1145
+ description: "List audit history for a record",
1146
+ inputSchema: {
1147
+ type: "object",
1148
+ properties: {
1149
+ table: { type: "string", description: "Table name" },
1150
+ id: { type: "string", description: "Record sys_id" },
1151
+ limit: { type: "number", default: 50 },
1152
+ },
1153
+ required: ["table", "id"],
1154
+ },
1155
+ },
1156
+ // -------------------------------------------------------------------------
1157
+ // SCHEDULED JOBS
1158
+ // -------------------------------------------------------------------------
1159
+ {
1160
+ name: "scheduled_job_list",
1161
+ description: "List scheduled jobs",
1162
+ inputSchema: {
1163
+ type: "object",
1164
+ properties: {
1165
+ query: { type: "string" },
1166
+ active: { type: "boolean", default: true },
1167
+ limit: { type: "number", default: 20 },
1168
+ },
1169
+ },
1170
+ },
1171
+ {
1172
+ name: "scheduled_job_run",
1173
+ description: "Execute a scheduled job immediately",
1174
+ inputSchema: {
1175
+ type: "object",
1176
+ properties: {
1177
+ id: { type: "string", description: "Scheduled job sys_id" },
1178
+ },
1179
+ required: ["id"],
1180
+ },
1181
+ },
1182
+ // -------------------------------------------------------------------------
1183
+ // METRICS / PERFORMANCE ANALYTICS
1184
+ // -------------------------------------------------------------------------
1185
+ {
1186
+ name: "metric_list",
1187
+ description: "List defined metrics",
1188
+ inputSchema: {
1189
+ type: "object",
1190
+ properties: {
1191
+ query: { type: "string" },
1192
+ limit: { type: "number", default: 20 },
1193
+ },
1194
+ },
1195
+ },
1196
+ {
1197
+ name: "metric_data",
1198
+ description: "Get metric data/values",
1199
+ inputSchema: {
1200
+ type: "object",
1201
+ properties: {
1202
+ metric_id: { type: "string", description: "Metric sys_id" },
1203
+ start_date: { type: "string", description: "Start date (ISO)" },
1204
+ end_date: { type: "string", description: "End date (ISO)" },
1205
+ },
1206
+ required: ["metric_id"],
1207
+ },
1208
+ },
1209
+ // -------------------------------------------------------------------------
1210
+ // UPDATE SETS
1211
+ // -------------------------------------------------------------------------
1212
+ {
1213
+ name: "update_set_list",
1214
+ description: "List update sets",
1215
+ inputSchema: {
1216
+ type: "object",
1217
+ properties: {
1218
+ query: { type: "string" },
1219
+ state: {
1220
+ type: "string",
1221
+ enum: ["in progress", "complete", "ignore"],
1222
+ },
1223
+ limit: { type: "number", default: 20 },
1224
+ },
1225
+ },
1226
+ },
1227
+ {
1228
+ name: "update_set_get",
1229
+ description: "Get update set details with customer updates",
1230
+ inputSchema: {
1231
+ type: "object",
1232
+ properties: {
1233
+ id: { type: "string", description: "Update set sys_id" },
1234
+ },
1235
+ required: ["id"],
1236
+ },
1237
+ },
1238
+ // -------------------------------------------------------------------------
1239
+ // ASSET MANAGEMENT (ITAM)
1240
+ // -------------------------------------------------------------------------
1241
+ {
1242
+ name: "asset_list",
1243
+ description: "List assets (alm_asset)",
1244
+ inputSchema: {
1245
+ type: "object",
1246
+ properties: {
1247
+ query: { type: "string" },
1248
+ asset_tag: { type: "string", description: "Filter by asset tag" },
1249
+ state: {
1250
+ type: "string",
1251
+ enum: ["in_stock", "in_use", "on_order", "retired", "disposed"],
1252
+ },
1253
+ limit: { type: "number", default: 20 },
1254
+ },
1255
+ },
1256
+ },
1257
+ {
1258
+ name: "asset_get",
1259
+ description: "Get asset details",
1260
+ inputSchema: {
1261
+ type: "object",
1262
+ properties: {
1263
+ id: { type: "string", description: "Asset sys_id or asset_tag" },
1264
+ },
1265
+ required: ["id"],
1266
+ },
1267
+ },
1268
+ {
1269
+ name: "asset_create",
1270
+ description: "Create an asset",
1271
+ inputSchema: {
1272
+ type: "object",
1273
+ properties: {
1274
+ asset_tag: { type: "string" },
1275
+ display_name: { type: "string" },
1276
+ model_category: { type: "string" },
1277
+ model: { type: "string", description: "Model sys_id" },
1278
+ serial_number: { type: "string" },
1279
+ assigned_to: { type: "string" },
1280
+ cost: { type: "number" },
1281
+ purchase_date: { type: "string" },
1282
+ },
1283
+ required: ["display_name"],
1284
+ },
1285
+ },
1286
+ {
1287
+ name: "asset_update",
1288
+ description: "Update an asset",
1289
+ inputSchema: {
1290
+ type: "object",
1291
+ properties: {
1292
+ id: { type: "string", description: "Asset sys_id" },
1293
+ data: { type: "object", additionalProperties: true },
1294
+ },
1295
+ required: ["id", "data"],
1296
+ },
1297
+ },
1298
+ // -------------------------------------------------------------------------
1299
+ // SOFTWARE LICENSE MANAGEMENT
1300
+ // -------------------------------------------------------------------------
1301
+ {
1302
+ name: "license_list",
1303
+ description: "List software licenses (alm_license)",
1304
+ inputSchema: {
1305
+ type: "object",
1306
+ properties: {
1307
+ query: { type: "string" },
1308
+ product: { type: "string", description: "Software product name" },
1309
+ limit: { type: "number", default: 20 },
1310
+ },
1311
+ },
1312
+ },
1313
+ {
1314
+ name: "license_get",
1315
+ description: "Get software license details",
1316
+ inputSchema: {
1317
+ type: "object",
1318
+ properties: {
1319
+ id: { type: "string", description: "License sys_id" },
1320
+ },
1321
+ required: ["id"],
1322
+ },
1323
+ },
1324
+ {
1325
+ name: "license_entitlements",
1326
+ description: "List license entitlements/allocations",
1327
+ inputSchema: {
1328
+ type: "object",
1329
+ properties: {
1330
+ license_id: { type: "string", description: "License sys_id" },
1331
+ limit: { type: "number", default: 50 },
1332
+ },
1333
+ },
1334
+ },
1335
+ // -------------------------------------------------------------------------
1336
+ // SOFTWARE ASSET MANAGEMENT
1337
+ // -------------------------------------------------------------------------
1338
+ {
1339
+ name: "software_list",
1340
+ description: "List software installations",
1341
+ inputSchema: {
1342
+ type: "object",
1343
+ properties: {
1344
+ query: { type: "string" },
1345
+ publisher: { type: "string" },
1346
+ limit: { type: "number", default: 20 },
1347
+ },
1348
+ },
1349
+ },
1350
+ {
1351
+ name: "software_product_list",
1352
+ description: "List software products (software catalog)",
1353
+ inputSchema: {
1354
+ type: "object",
1355
+ properties: {
1356
+ query: { type: "string" },
1357
+ vendor: { type: "string" },
1358
+ limit: { type: "number", default: 20 },
1359
+ },
1360
+ },
1361
+ },
1362
+ // -------------------------------------------------------------------------
1363
+ // CONTRACTS
1364
+ // -------------------------------------------------------------------------
1365
+ {
1366
+ name: "contract_list",
1367
+ description: "List contracts",
1368
+ inputSchema: {
1369
+ type: "object",
1370
+ properties: {
1371
+ query: { type: "string" },
1372
+ type: { type: "string", description: "Contract type" },
1373
+ state: { type: "string" },
1374
+ limit: { type: "number", default: 20 },
1375
+ },
1376
+ },
1377
+ },
1378
+ {
1379
+ name: "contract_get",
1380
+ description: "Get contract details",
1381
+ inputSchema: {
1382
+ type: "object",
1383
+ properties: {
1384
+ id: { type: "string", description: "Contract sys_id" },
1385
+ },
1386
+ required: ["id"],
1387
+ },
1388
+ },
1389
+ // -------------------------------------------------------------------------
1390
+ // LOCATION
1391
+ // -------------------------------------------------------------------------
1392
+ {
1393
+ name: "location_list",
1394
+ description: "List locations",
1395
+ inputSchema: {
1396
+ type: "object",
1397
+ properties: {
1398
+ query: { type: "string" },
1399
+ type: { type: "string", description: "Location type" },
1400
+ limit: { type: "number", default: 50 },
1401
+ },
1402
+ },
1403
+ },
1404
+ {
1405
+ name: "location_get",
1406
+ description: "Get location details",
1407
+ inputSchema: {
1408
+ type: "object",
1409
+ properties: {
1410
+ id: { type: "string", description: "Location sys_id or name" },
1411
+ },
1412
+ required: ["id"],
1413
+ },
1414
+ },
1415
+ // -------------------------------------------------------------------------
1416
+ // DEPARTMENT / COST CENTER
1417
+ // -------------------------------------------------------------------------
1418
+ {
1419
+ name: "department_list",
1420
+ description: "List departments",
1421
+ inputSchema: {
1422
+ type: "object",
1423
+ properties: {
1424
+ query: { type: "string" },
1425
+ limit: { type: "number", default: 50 },
1426
+ },
1427
+ },
1428
+ },
1429
+ {
1430
+ name: "cost_center_list",
1431
+ description: "List cost centers",
1432
+ inputSchema: {
1433
+ type: "object",
1434
+ properties: {
1435
+ query: { type: "string" },
1436
+ limit: { type: "number", default: 50 },
1437
+ },
1438
+ },
1439
+ },
1440
+ // -------------------------------------------------------------------------
1441
+ // DISCOVERY / ITOM
1442
+ // -------------------------------------------------------------------------
1443
+ {
1444
+ name: "discovery_status_list",
1445
+ description: "List discovery status records",
1446
+ inputSchema: {
1447
+ type: "object",
1448
+ properties: {
1449
+ query: { type: "string" },
1450
+ state: { type: "string", enum: ["active", "completed", "cancelled"] },
1451
+ limit: { type: "number", default: 20 },
1452
+ },
1453
+ },
1454
+ },
1455
+ {
1456
+ name: "discovery_schedule_list",
1457
+ description: "List discovery schedules",
1458
+ inputSchema: {
1459
+ type: "object",
1460
+ properties: {
1461
+ query: { type: "string" },
1462
+ active: { type: "boolean", default: true },
1463
+ limit: { type: "number", default: 20 },
1464
+ },
1465
+ },
1466
+ },
1467
+ // -------------------------------------------------------------------------
1468
+ // SECURITY / ACL
1469
+ // -------------------------------------------------------------------------
1470
+ {
1471
+ name: "acl_list",
1472
+ description: "List access control rules",
1473
+ inputSchema: {
1474
+ type: "object",
1475
+ properties: {
1476
+ table: { type: "string", description: "Filter by table" },
1477
+ operation: {
1478
+ type: "string",
1479
+ enum: ["read", "write", "create", "delete"],
1480
+ },
1481
+ limit: { type: "number", default: 20 },
1482
+ },
1483
+ },
1484
+ },
1485
+ {
1486
+ name: "role_list",
1487
+ description: "List roles",
1488
+ inputSchema: {
1489
+ type: "object",
1490
+ properties: {
1491
+ query: { type: "string" },
1492
+ limit: { type: "number", default: 50 },
1493
+ },
1494
+ },
1495
+ },
1496
+ {
1497
+ name: "user_roles",
1498
+ description: "Get roles for a user",
1499
+ inputSchema: {
1500
+ type: "object",
1501
+ properties: {
1502
+ user_id: { type: "string", description: "User sys_id" },
1503
+ },
1504
+ required: ["user_id"],
1505
+ },
1506
+ },
1507
+ // -------------------------------------------------------------------------
1508
+ // GENERIC / UTILITIES
1509
+ // -------------------------------------------------------------------------
1510
+ {
1511
+ name: "table_query",
1512
+ description: "Generic table query - query any ServiceNow table",
1513
+ inputSchema: {
1514
+ type: "object",
1515
+ properties: {
1516
+ table: {
1517
+ type: "string",
1518
+ description: "Table name (e.g., incident, sys_user, cmdb_ci)",
1519
+ },
1520
+ query: { type: "string", description: "Encoded query string" },
1521
+ fields: {
1522
+ type: "string",
1523
+ description: "Comma-separated fields to return",
1524
+ },
1525
+ limit: { type: "number", default: 20 },
1526
+ offset: { type: "number", default: 0 },
1527
+ order_by: { type: "string", description: "Field to order by" },
1528
+ order_dir: { type: "string", enum: ["asc", "desc"], default: "desc" },
1529
+ },
1530
+ required: ["table"],
1531
+ },
1532
+ },
1533
+ {
1534
+ name: "table_get",
1535
+ description: "Get a single record from any table",
1536
+ inputSchema: {
1537
+ type: "object",
1538
+ properties: {
1539
+ table: { type: "string", description: "Table name" },
1540
+ id: { type: "string", description: "Record sys_id" },
1541
+ fields: { type: "string", description: "Comma-separated fields" },
1542
+ },
1543
+ required: ["table", "id"],
1544
+ },
1545
+ },
1546
+ {
1547
+ name: "table_create",
1548
+ description: "Create a record in any table",
1549
+ inputSchema: {
1550
+ type: "object",
1551
+ properties: {
1552
+ table: { type: "string", description: "Table name" },
1553
+ data: { type: "object", additionalProperties: true },
1554
+ },
1555
+ required: ["table", "data"],
1556
+ },
1557
+ },
1558
+ {
1559
+ name: "table_update",
1560
+ description: "Update a record in any table",
1561
+ inputSchema: {
1562
+ type: "object",
1563
+ properties: {
1564
+ table: { type: "string", description: "Table name" },
1565
+ id: { type: "string", description: "Record sys_id" },
1566
+ data: { type: "object", additionalProperties: true },
1567
+ },
1568
+ required: ["table", "id", "data"],
1569
+ },
1570
+ },
1571
+ {
1572
+ name: "table_delete",
1573
+ description: "Delete a record from any table",
1574
+ inputSchema: {
1575
+ type: "object",
1576
+ properties: {
1577
+ table: { type: "string", description: "Table name" },
1578
+ id: { type: "string", description: "Record sys_id" },
1579
+ },
1580
+ required: ["table", "id"],
1581
+ },
1582
+ },
1583
+ {
1584
+ name: "aggregate",
1585
+ description: "Run aggregate queries (count, sum, avg, etc.)",
1586
+ inputSchema: {
1587
+ type: "object",
1588
+ properties: {
1589
+ table: { type: "string", description: "Table name" },
1590
+ query: { type: "string", description: "Filter query" },
1591
+ group_by: { type: "string", description: "Field to group by" },
1592
+ aggregate: {
1593
+ type: "string",
1594
+ enum: ["COUNT", "SUM", "AVG", "MIN", "MAX"],
1595
+ default: "COUNT",
1596
+ },
1597
+ having: { type: "string", description: "Having clause" },
1598
+ },
1599
+ required: ["table"],
1600
+ },
1601
+ },
1602
+ {
1603
+ name: "table_schema",
1604
+ description: "Get table schema/dictionary information",
1605
+ inputSchema: {
1606
+ type: "object",
1607
+ properties: {
1608
+ table: { type: "string", description: "Table name" },
1609
+ },
1610
+ required: ["table"],
1611
+ },
1612
+ },
1613
+ {
1614
+ name: "choice_list",
1615
+ description: "Get choice list values for a field",
1616
+ inputSchema: {
1617
+ type: "object",
1618
+ properties: {
1619
+ table: { type: "string", description: "Table name" },
1620
+ field: { type: "string", description: "Field name" },
1621
+ },
1622
+ required: ["table", "field"],
1623
+ },
1624
+ },
1625
+ ];
1626
+ // ============================================================================
1627
+ // ServiceNow API Client
1628
+ // ============================================================================
1629
+ class ServiceNowClient {
1630
+ baseUrl;
1631
+ authHeader;
1632
+ sessionCookies;
1633
+ userToken;
1634
+ authMethod;
1635
+ constructor() {
1636
+ // Priority: Browser auth > Basic auth > Session tokens
1637
+ if (browserAuth) {
1638
+ // Use browser-captured cookies
1639
+ this.baseUrl = browserAuth.instanceUrl;
1640
+ this.authHeader = "";
1641
+ this.sessionCookies = browserAuth.cookies;
1642
+ this.userToken = browserAuth.userToken;
1643
+ this.authMethod = "browser";
1644
+ console.error("Using browser-based SSO authentication");
1645
+ }
1646
+ else if (USERNAME && PASSWORD) {
1647
+ // Basic auth for REST Table API
1648
+ this.baseUrl = INSTANCE_URL.replace(/\/$/, "");
1649
+ this.authHeader =
1650
+ "Basic " + Buffer.from(`${USERNAME}:${PASSWORD}`).toString("base64");
1651
+ this.sessionCookies = "";
1652
+ this.userToken = "";
1653
+ this.authMethod = "basic";
1654
+ }
1655
+ else if (SESSION_TOKEN) {
1656
+ // Session-based auth for GraphQL
1657
+ this.baseUrl = INSTANCE_URL.replace(/\/$/, "");
1658
+ this.authHeader = "";
1659
+ this.sessionCookies = SESSION_TOKEN;
1660
+ this.userToken = USER_TOKEN;
1661
+ this.authMethod = "session";
1662
+ }
1663
+ else {
1664
+ // No auth configured - MCP will still start but tools will prompt for auth
1665
+ this.baseUrl = INSTANCE_URL.replace(/\/$/, "");
1666
+ this.authHeader = "";
1667
+ this.sessionCookies = "";
1668
+ this.userToken = "";
1669
+ this.authMethod = "none";
1670
+ }
1671
+ }
1672
+ getAuthStatus() {
1673
+ return {
1674
+ method: this.authMethod,
1675
+ configured: this.authMethod !== "none",
1676
+ instanceUrl: this.baseUrl || "(not set)",
1677
+ details: this.authMethod === "browser"
1678
+ ? "Using SSO cookies from browser authentication"
1679
+ : this.authMethod === "basic"
1680
+ ? "Using username/password basic auth"
1681
+ : this.authMethod === "session"
1682
+ ? "Using session token auth"
1683
+ : "No authentication configured. Use auth_browser tool or set credentials.",
1684
+ };
1685
+ }
1686
+ // Reload browser auth cookies (called when 401 is received)
1687
+ reloadCredentials() {
1688
+ if (this.authMethod !== "browser") {
1689
+ return false;
1690
+ }
1691
+ if (reloadBrowserAuth() && browserAuth) {
1692
+ this.baseUrl = browserAuth.instanceUrl;
1693
+ this.sessionCookies = browserAuth.cookies;
1694
+ this.userToken = browserAuth.userToken;
1695
+ console.error("✅ ServiceNowClient credentials reloaded");
1696
+ return true;
1697
+ }
1698
+ return false;
1699
+ }
1700
+ // Validate session by making a lightweight API call
1701
+ async validateSession() {
1702
+ if (this.authMethod === "none") {
1703
+ return { valid: false, error: "No authentication configured" };
1704
+ }
1705
+ try {
1706
+ // Make a minimal API call to check session validity
1707
+ const url = `${this.baseUrl}/api/now/table/sys_user?sysparm_limit=1&sysparm_fields=sys_id`;
1708
+ const headers = {
1709
+ Accept: "application/json",
1710
+ };
1711
+ if (this.authMethod === "browser" && this.sessionCookies) {
1712
+ headers["Cookie"] = this.sessionCookies;
1713
+ }
1714
+ else if (this.authHeader) {
1715
+ headers["Authorization"] = this.authHeader;
1716
+ }
1717
+ const response = await fetch(url, { method: "GET", headers });
1718
+ if (response.status === 401) {
1719
+ return { valid: false, error: "Session expired (401 Unauthorized)" };
1720
+ }
1721
+ return { valid: response.ok };
1722
+ }
1723
+ catch (err) {
1724
+ return {
1725
+ valid: false,
1726
+ error: err instanceof Error ? err.message : "Validation failed",
1727
+ };
1728
+ }
1729
+ }
1730
+ checkConfig() {
1731
+ if (!this.baseUrl) {
1732
+ throw new Error("ServiceNow not configured. Set SERVICENOW_INSTANCE_URL environment variable.\n" +
1733
+ "Example: https://yourinstance.service-now.com");
1734
+ }
1735
+ }
1736
+ async request(method, endpoint, body, useGraphQL = false) {
1737
+ this.checkConfig();
1738
+ const url = `${this.baseUrl}${endpoint}`;
1739
+ const headers = {
1740
+ "Content-Type": "application/json",
1741
+ Accept: "application/json",
1742
+ };
1743
+ // Apply authentication based on method
1744
+ if (this.authMethod === "browser" && this.sessionCookies) {
1745
+ // Browser auth - use cookies for all requests
1746
+ headers["Cookie"] = this.sessionCookies;
1747
+ if (this.userToken) {
1748
+ headers["x-usertoken"] = this.userToken;
1749
+ }
1750
+ }
1751
+ else if (useGraphQL && this.userToken) {
1752
+ headers["x-usertoken"] = this.userToken;
1753
+ if (this.sessionCookies) {
1754
+ headers["Cookie"] = this.sessionCookies;
1755
+ }
1756
+ }
1757
+ else if (this.authHeader) {
1758
+ headers["Authorization"] = this.authHeader;
1759
+ }
1760
+ else {
1761
+ throw new Error("No authentication configured. Options:\n" +
1762
+ "1. Use auth_browser tool to authenticate via SSO in browser\n" +
1763
+ "2. Set SERVICENOW_USERNAME + SERVICENOW_PASSWORD (for REST API)\n" +
1764
+ "3. Set SERVICENOW_SESSION_TOKEN + SERVICENOW_USER_TOKEN (for session auth)");
1765
+ }
1766
+ let response = await fetch(url, {
1767
+ method,
1768
+ headers,
1769
+ body: body ? JSON.stringify(body) : undefined,
1770
+ });
1771
+ // Handle 401 with automatic cookie reload and retry (browser auth only)
1772
+ if (response.status === 401 && this.authMethod === "browser") {
1773
+ console.error("⚠️ Received 401 - attempting to reload cookies...");
1774
+ if (this.reloadCredentials()) {
1775
+ // Update headers with new cookies and retry
1776
+ headers["Cookie"] = this.sessionCookies;
1777
+ if (this.userToken) {
1778
+ headers["x-usertoken"] = this.userToken;
1779
+ }
1780
+ console.error("🔄 Retrying request with refreshed cookies...");
1781
+ response = await fetch(url, {
1782
+ method,
1783
+ headers,
1784
+ body: body ? JSON.stringify(body) : undefined,
1785
+ });
1786
+ }
1787
+ }
1788
+ if (!response.ok) {
1789
+ const errorText = await response.text();
1790
+ throw new Error(`ServiceNow API error ${response.status}: ${errorText}`);
1791
+ }
1792
+ return response.json();
1793
+ }
1794
+ // REST Table API methods
1795
+ async tableQuery(table, params) {
1796
+ const searchParams = new URLSearchParams();
1797
+ if (params.query)
1798
+ searchParams.set("sysparm_query", params.query);
1799
+ if (params.fields)
1800
+ searchParams.set("sysparm_fields", params.fields);
1801
+ if (params.limit)
1802
+ searchParams.set("sysparm_limit", String(params.limit));
1803
+ if (params.offset)
1804
+ searchParams.set("sysparm_offset", String(params.offset));
1805
+ if (params.orderBy) {
1806
+ const dir = params.orderDir === "asc" ? "" : "DESC";
1807
+ searchParams.set("sysparm_query", `${params.query || ""}^ORDERBY${dir}${params.orderBy}`);
1808
+ }
1809
+ searchParams.set("sysparm_display_value", "true");
1810
+ return this.request("GET", `/api/now/table/${table}?${searchParams.toString()}`);
1811
+ }
1812
+ async tableGet(table, id, fields) {
1813
+ const searchParams = new URLSearchParams();
1814
+ if (fields)
1815
+ searchParams.set("sysparm_fields", fields);
1816
+ searchParams.set("sysparm_display_value", "true");
1817
+ return this.request("GET", `/api/now/table/${table}/${id}?${searchParams.toString()}`);
1818
+ }
1819
+ async tableCreate(table, data) {
1820
+ return this.request("POST", `/api/now/table/${table}`, data);
1821
+ }
1822
+ async tableUpdate(table, id, data) {
1823
+ return this.request("PATCH", `/api/now/table/${table}/${id}`, data);
1824
+ }
1825
+ async aggregate(table, params) {
1826
+ const searchParams = new URLSearchParams();
1827
+ if (params.query)
1828
+ searchParams.set("sysparm_query", params.query);
1829
+ if (params.groupBy)
1830
+ searchParams.set("sysparm_group_by", params.groupBy);
1831
+ searchParams.set("sysparm_count", "true");
1832
+ return this.request("GET", `/api/now/stats/${table}?${searchParams.toString()}`);
1833
+ }
1834
+ // GraphQL API for session-based queries
1835
+ async graphqlQuery(operationName, query, variables) {
1836
+ return this.request("POST", "/api/now/graphql", {
1837
+ operationName,
1838
+ query,
1839
+ variables,
1840
+ cacheable: false,
1841
+ }, true);
1842
+ }
1843
+ // Resolve number to sys_id for incidents, changes, etc.
1844
+ async resolveId(table, id) {
1845
+ if (id.length === 32 && /^[a-f0-9]+$/.test(id)) {
1846
+ return id; // Already a sys_id
1847
+ }
1848
+ // Look up by number
1849
+ const numberField = this.getNumberField(table);
1850
+ const result = (await this.tableQuery(table, {
1851
+ query: `${numberField}=${id}`,
1852
+ fields: "sys_id",
1853
+ limit: 1,
1854
+ }));
1855
+ if (result.result && result.result.length > 0) {
1856
+ return result.result[0].sys_id;
1857
+ }
1858
+ throw new Error(`Could not find ${table} with ${numberField}=${id}`);
1859
+ }
1860
+ getNumberField(table) {
1861
+ const numberFields = {
1862
+ incident: "number",
1863
+ change_request: "number",
1864
+ problem: "number",
1865
+ sc_request: "number",
1866
+ sc_req_item: "number",
1867
+ kb_knowledge: "number",
1868
+ task: "number",
1869
+ };
1870
+ return numberFields[table] || "number";
1871
+ }
1872
+ // Delete a record
1873
+ async tableDelete(table, id) {
1874
+ return this.request("DELETE", `/api/now/table/${table}/${id}`);
1875
+ }
1876
+ // Attachment API
1877
+ async attachmentGet(id) {
1878
+ return this.request("GET", `/api/now/attachment/${id}`);
1879
+ }
1880
+ async attachmentDownload(id) {
1881
+ // Returns file content
1882
+ return this.request("GET", `/api/now/attachment/${id}/file`);
1883
+ }
1884
+ async attachmentUpload(table, recordId, filename, content, contentType) {
1885
+ this.checkConfig();
1886
+ const url = `${this.baseUrl}/api/now/attachment/file?table_name=${table}&table_sys_id=${recordId}&file_name=${encodeURIComponent(filename)}`;
1887
+ const headers = {
1888
+ "Content-Type": contentType,
1889
+ Accept: "application/json",
1890
+ };
1891
+ if (this.authHeader) {
1892
+ headers["Authorization"] = this.authHeader;
1893
+ }
1894
+ // Decode base64 content
1895
+ const binaryContent = Buffer.from(content, "base64");
1896
+ const response = await fetch(url, {
1897
+ method: "POST",
1898
+ headers,
1899
+ body: binaryContent,
1900
+ });
1901
+ if (!response.ok) {
1902
+ const errorText = await response.text();
1903
+ throw new Error(`Attachment upload error ${response.status}: ${errorText}`);
1904
+ }
1905
+ return response.json();
1906
+ }
1907
+ async attachmentDelete(id) {
1908
+ return this.request("DELETE", `/api/now/attachment/${id}`);
1909
+ }
1910
+ // Service Catalog API (sn_sc)
1911
+ async catalogAddToCart(itemId, quantity, variables) {
1912
+ return this.request("POST", `/api/sn_sc/servicecatalog/items/${itemId}/add_to_cart`, { sysparm_quantity: quantity, variables });
1913
+ }
1914
+ async catalogGetCart() {
1915
+ return this.request("GET", "/api/sn_sc/servicecatalog/cart");
1916
+ }
1917
+ async catalogSubmitCart() {
1918
+ return this.request("POST", "/api/sn_sc/servicecatalog/cart/submit_order");
1919
+ }
1920
+ async catalogOrderNow(itemId, quantity, variables, requestedFor) {
1921
+ const body = {
1922
+ sysparm_quantity: quantity,
1923
+ variables,
1924
+ };
1925
+ if (requestedFor)
1926
+ body.sysparm_requested_for = requestedFor;
1927
+ return this.request("POST", `/api/sn_sc/servicecatalog/items/${itemId}/order_now`, body);
1928
+ }
1929
+ async catalogGetVariables(itemId) {
1930
+ return this.request("GET", `/api/sn_sc/servicecatalog/items/${itemId}/variables`);
1931
+ }
1932
+ // Import Set API
1933
+ async importSetLoad(table, data) {
1934
+ return this.request("POST", `/api/now/import/${table}`, data);
1935
+ }
1936
+ async importSetLoadMultiple(table, records) {
1937
+ return this.request("POST", `/api/now/import/${table}/insertMultiple`, {
1938
+ records,
1939
+ });
1940
+ }
1941
+ // Batch API
1942
+ async batchRequest(requests) {
1943
+ return this.request("POST", "/api/now/v1/batch", {
1944
+ batch_request_id: `batch_${Date.now()}`,
1945
+ rest_requests: requests.map((r) => ({
1946
+ id: r.id,
1947
+ method: r.method,
1948
+ url: r.url,
1949
+ body: r.body ? JSON.stringify(r.body) : undefined,
1950
+ headers: r.headers
1951
+ ? Object.entries(r.headers).map(([name, value]) => ({ name, value }))
1952
+ : [],
1953
+ })),
1954
+ });
1955
+ }
1956
+ // CMDB Instance API
1957
+ async cmdbInstanceList(className, query, limit) {
1958
+ const params = new URLSearchParams();
1959
+ if (query)
1960
+ params.set("sysparm_query", query);
1961
+ if (limit)
1962
+ params.set("sysparm_limit", String(limit));
1963
+ return this.request("GET", `/api/now/cmdb/instance/${className}?${params.toString()}`);
1964
+ }
1965
+ // =========================================================================
1966
+ // UNIFIED WORK QUEUE METHODS (P1-P4 optimizations)
1967
+ // =========================================================================
1968
+ // Cache for user context (valid for session duration)
1969
+ userContextCache = null;
1970
+ async getMyContext() {
1971
+ // Return cached if available
1972
+ if (this.userContextCache) {
1973
+ return this.userContextCache;
1974
+ }
1975
+ // Get current user info
1976
+ const userResult = (await this.request("GET", "/api/now/table/sys_user?sysparm_query=user_name=javascript:gs.getUserName()&sysparm_limit=1&sysparm_fields=sys_id,user_name,name,email,title,department,manager"));
1977
+ const user = userResult.result?.[0] || {};
1978
+ const userId = user.sys_id;
1979
+ // Get user's group memberships (need both sys_id and display name)
1980
+ const groupsResult = (await this.request("GET", `/api/now/table/sys_user_grmember?sysparm_query=user=${userId}&sysparm_fields=group&sysparm_display_value=all`));
1981
+ const groups = (groupsResult.result || []).map((g) => ({
1982
+ sys_id: g.group?.value || "",
1983
+ name: g.group?.display_value || "",
1984
+ }));
1985
+ // Get user's roles
1986
+ const rolesResult = (await this.request("GET", `/api/now/table/sys_user_has_role?sysparm_query=user=${userId}&sysparm_fields=role&sysparm_display_value=true`));
1987
+ const roles = (rolesResult.result || []).map((r) => r.role?.display_value || "");
1988
+ // Cache and return
1989
+ this.userContextCache = { user, groups, roles };
1990
+ return this.userContextCache;
1991
+ }
1992
+ async getMyWorkQueue(params) {
1993
+ const include = params.include || ["assigned", "approvals", "group_queue"];
1994
+ const limit = params.limit || 20;
1995
+ const keyword = params.keyword;
1996
+ const results = {
1997
+ assigned: [],
1998
+ approvals: [],
1999
+ group_queue: [],
2000
+ };
2001
+ // Build keyword filter
2002
+ const keywordFilter = keyword ? `^short_descriptionLIKE${keyword}` : "";
2003
+ // 1. Get assigned tasks (if requested)
2004
+ if (include.includes("assigned")) {
2005
+ const assignedResult = (await this.tableQuery("task", {
2006
+ query: `assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe^active=true${keywordFilter}`,
2007
+ fields: "number,short_description,state,priority,sys_class_name,sys_updated_on,assignment_group",
2008
+ limit,
2009
+ }));
2010
+ results.assigned = assignedResult.result || [];
2011
+ }
2012
+ // 2. Get pending approvals with enriched parent data (if requested)
2013
+ if (include.includes("approvals")) {
2014
+ results.approvals = await this.getEnrichedApprovals({ keyword, limit });
2015
+ }
2016
+ // 3. Get unassigned group queue items (if requested)
2017
+ if (include.includes("group_queue")) {
2018
+ const context = await this.getMyContext();
2019
+ const groupIds = context.groups.map((g) => g.sys_id).join(",");
2020
+ if (groupIds) {
2021
+ const groupQueueResult = (await this.tableQuery("task", {
2022
+ query: `assignment_groupIN${groupIds}^assigned_toISEMPTY^active=true${keywordFilter}`,
2023
+ fields: "number,short_description,state,priority,sys_class_name,assignment_group,sys_created_on",
2024
+ limit,
2025
+ }));
2026
+ results.group_queue = groupQueueResult.result || [];
2027
+ }
2028
+ }
2029
+ return {
2030
+ ...results,
2031
+ summary: {
2032
+ total: results.assigned.length +
2033
+ results.approvals.length +
2034
+ results.group_queue.length,
2035
+ assigned: results.assigned.length,
2036
+ approvals: results.approvals.length,
2037
+ group_queue: results.group_queue.length,
2038
+ },
2039
+ };
2040
+ }
2041
+ async getEnrichedApprovals(params) {
2042
+ const limit = params.limit || 20;
2043
+ // Get pending approvals
2044
+ const approvalsResult = (await this.tableQuery("sysapproval_approver", {
2045
+ query: "approverDYNAMIC90d1921e5f510100a9ad2572f2b477fe^state=requested",
2046
+ fields: "sys_id,sysapproval,state,sys_updated_on,document_id",
2047
+ limit: limit * 2, // Fetch more in case some get filtered out
2048
+ }));
2049
+ const approvals = approvalsResult.result || [];
2050
+ const enrichedApprovals = [];
2051
+ // Enrich each approval with parent record details
2052
+ for (const approval of approvals) {
2053
+ const docId = approval.document_id;
2054
+ const sysapproval = approval.sysapproval;
2055
+ if (!sysapproval?.link)
2056
+ continue;
2057
+ try {
2058
+ // Extract table and sys_id from the link
2059
+ const linkMatch = sysapproval.link.match(/\/table\/([^/]+)\/([^?]+)/);
2060
+ if (!linkMatch)
2061
+ continue;
2062
+ const [, table, parentSysId] = linkMatch;
2063
+ // Filter by type if specified
2064
+ if (params.type && params.type !== "all" && table !== params.type) {
2065
+ continue;
2066
+ }
2067
+ // Fetch parent record details
2068
+ const parentResult = (await this.tableGet(table, parentSysId, "number,short_description,opened_by,opened_at,urgency,stage,state"));
2069
+ const parent = parentResult.result || {};
2070
+ // Filter by keyword if specified
2071
+ const shortDesc = parent.short_description || "";
2072
+ if (params.keyword &&
2073
+ !shortDesc.toLowerCase().includes(params.keyword.toLowerCase())) {
2074
+ continue;
2075
+ }
2076
+ enrichedApprovals.push({
2077
+ approval_sys_id: approval.sys_id,
2078
+ number: parent.number,
2079
+ short_description: parent.short_description,
2080
+ type: table,
2081
+ state: approval.state,
2082
+ opened_by: parent.opened_by?.display_value ||
2083
+ parent.opened_by,
2084
+ opened_at: parent.opened_at,
2085
+ urgency: parent.urgency,
2086
+ stage: parent.stage,
2087
+ waiting_since: approval.sys_updated_on,
2088
+ action: "approve",
2089
+ });
2090
+ if (enrichedApprovals.length >= limit)
2091
+ break;
2092
+ }
2093
+ catch {
2094
+ // Skip approvals we can't enrich
2095
+ continue;
2096
+ }
2097
+ }
2098
+ return enrichedApprovals;
2099
+ }
2100
+ }
2101
+ // ============================================================================
2102
+ // MCP Server
2103
+ // ============================================================================
2104
+ class ServiceNowMcpServer {
2105
+ server;
2106
+ client;
2107
+ constructor() {
2108
+ this.server = new Server({ name: "servicenow", version: "1.0.0" }, { capabilities: { tools: {} } });
2109
+ this.client = new ServiceNowClient();
2110
+ this.setupHandlers();
2111
+ }
2112
+ setupHandlers() {
2113
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
2114
+ tools: TOOLS,
2115
+ }));
2116
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
2117
+ const { name, arguments: args } = request.params;
2118
+ try {
2119
+ const result = await this.handleTool(name, args);
2120
+ return {
2121
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
2122
+ };
2123
+ }
2124
+ catch (error) {
2125
+ const message = error instanceof Error ? error.message : String(error);
2126
+ return {
2127
+ content: [{ type: "text", text: `Error: ${message}` }],
2128
+ isError: true,
2129
+ };
2130
+ }
2131
+ });
2132
+ }
2133
+ async handleTool(name, args) {
2134
+ // Route to appropriate handler based on tool name
2135
+ const [category, action] = name.split("_");
2136
+ switch (name) {
2137
+ // Authentication
2138
+ case "auth_browser": {
2139
+ const instanceUrl = args.instance_url ||
2140
+ INSTANCE_URL ||
2141
+ "https://qvcprod.service-now.com";
2142
+ const result = await authenticateViaBrowser(instanceUrl);
2143
+ if (result.success) {
2144
+ // Hot-reload credentials without requiring restart
2145
+ const reloaded = this.client.reloadCredentials();
2146
+ return {
2147
+ status: "success",
2148
+ message: reloaded
2149
+ ? "Browser authentication completed. Credentials reloaded - ready to use immediately."
2150
+ : "Browser authentication completed. Cookies saved. Note: If this is first auth, you may need to restart Claude.",
2151
+ instanceUrl: result.instanceUrl,
2152
+ cookieCount: result.cookies.length,
2153
+ hasUserToken: !!result.userToken,
2154
+ credentialsReloaded: reloaded,
2155
+ };
2156
+ }
2157
+ else {
2158
+ throw new Error(result.error || "Browser authentication failed");
2159
+ }
2160
+ }
2161
+ case "auth_status": {
2162
+ const status = this.client.getAuthStatus();
2163
+ const validation = await this.client.validateSession();
2164
+ return {
2165
+ ...status,
2166
+ sessionValid: validation.valid,
2167
+ validationError: validation.error,
2168
+ };
2169
+ }
2170
+ // Unified Work Queue
2171
+ case "my_context":
2172
+ return this.client.getMyContext();
2173
+ case "my_work_queue":
2174
+ return this.client.getMyWorkQueue({
2175
+ include: args.include,
2176
+ keyword: args.keyword,
2177
+ limit: args.limit,
2178
+ });
2179
+ // Incidents
2180
+ case "incidents_list":
2181
+ return this.client.tableQuery("incident", {
2182
+ query: args.query,
2183
+ fields: args.fields ||
2184
+ "number,short_description,state,priority,assigned_to,sys_updated_on",
2185
+ limit: args.limit || 20,
2186
+ offset: args.offset || 0,
2187
+ });
2188
+ case "incidents_get": {
2189
+ const id = await this.client.resolveId("incident", args.id);
2190
+ return this.client.tableGet("incident", id, args.fields);
2191
+ }
2192
+ case "incidents_create":
2193
+ return this.client.tableCreate("incident", args);
2194
+ case "incidents_update": {
2195
+ const id = await this.client.resolveId("incident", args.id);
2196
+ return this.client.tableUpdate("incident", id, args.data);
2197
+ }
2198
+ case "incidents_add_comment": {
2199
+ const id = await this.client.resolveId("incident", args.id);
2200
+ const field = args.type === "comments" ? "comments" : "work_notes";
2201
+ return this.client.tableUpdate("incident", id, {
2202
+ [field]: args.comment,
2203
+ });
2204
+ }
2205
+ case "incidents_resolve": {
2206
+ const id = await this.client.resolveId("incident", args.id);
2207
+ return this.client.tableUpdate("incident", id, {
2208
+ state: 6, // Resolved
2209
+ close_code: args.resolution_code,
2210
+ close_notes: args.resolution_notes,
2211
+ });
2212
+ }
2213
+ // Changes
2214
+ case "changes_list":
2215
+ return this.client.tableQuery("change_request", {
2216
+ query: args.query,
2217
+ fields: "number,short_description,state,type,start_date,end_date,assigned_to",
2218
+ limit: args.limit || 20,
2219
+ });
2220
+ case "changes_get": {
2221
+ const id = await this.client.resolveId("change_request", args.id);
2222
+ return this.client.tableGet("change_request", id);
2223
+ }
2224
+ case "changes_create":
2225
+ return this.client.tableCreate("change_request", args);
2226
+ case "changes_tasks": {
2227
+ const id = await this.client.resolveId("change_request", args.id);
2228
+ return this.client.tableQuery("change_task", {
2229
+ query: `change_request=${id}`,
2230
+ fields: "number,short_description,state,assigned_to,order",
2231
+ });
2232
+ }
2233
+ // Service Catalog
2234
+ case "catalog_items":
2235
+ return this.client.tableQuery("sc_cat_item", {
2236
+ query: args.query
2237
+ ? `nameLIKE${args.query}`
2238
+ : args.category
2239
+ ? `category=${args.category}`
2240
+ : "active=true",
2241
+ fields: "name,short_description,category,price,sys_id",
2242
+ limit: args.limit || 20,
2243
+ });
2244
+ case "catalog_get":
2245
+ return this.client.tableGet("sc_cat_item", args.id);
2246
+ case "catalog_order":
2247
+ // Use the Service Catalog API for ordering
2248
+ return this.client.tableCreate("sc_request", {
2249
+ requested_for: args.requested_for,
2250
+ // Note: Full catalog ordering requires the SC API, not table API
2251
+ });
2252
+ case "requests_list":
2253
+ return this.client.tableQuery("sc_request", {
2254
+ query: args.my_requests
2255
+ ? "requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe"
2256
+ : args.query,
2257
+ fields: "number,short_description,request_state,opened_at,requested_for",
2258
+ limit: args.limit || 20,
2259
+ });
2260
+ case "requests_items":
2261
+ return this.client.tableQuery("sc_req_item", {
2262
+ query: args.query,
2263
+ fields: "number,short_description,stage,state,request,cat_item",
2264
+ limit: args.limit || 20,
2265
+ });
2266
+ // CMDB
2267
+ case "cmdb_search":
2268
+ return this.client.tableQuery(args.class || "cmdb_ci", {
2269
+ query: `nameLIKE${args.query}^ORasset_tagLIKE${args.query}`,
2270
+ fields: "name,sys_class_name,operational_status,install_status,location",
2271
+ limit: args.limit || 20,
2272
+ });
2273
+ case "cmdb_get":
2274
+ return this.client.tableGet(args.class || "cmdb_ci", args.id);
2275
+ case "cmdb_relationships":
2276
+ return this.client.tableQuery("cmdb_rel_ci", {
2277
+ query: `parent=${args.id}^ORchild=${args.id}`,
2278
+ fields: "parent,child,type",
2279
+ });
2280
+ // Problems
2281
+ case "problems_list":
2282
+ return this.client.tableQuery("problem", {
2283
+ query: args.query,
2284
+ fields: "number,short_description,state,priority,assigned_to",
2285
+ limit: args.limit || 20,
2286
+ });
2287
+ case "problems_get": {
2288
+ const id = await this.client.resolveId("problem", args.id);
2289
+ return this.client.tableGet("problem", id);
2290
+ }
2291
+ case "problems_create":
2292
+ return this.client.tableCreate("problem", args);
2293
+ // Knowledge
2294
+ case "knowledge_search":
2295
+ return this.client.tableQuery("kb_knowledge", {
2296
+ query: `textLIKE${args.query}^workflow_state=published`,
2297
+ fields: "number,short_description,text,sys_view_count",
2298
+ limit: args.limit || 10,
2299
+ });
2300
+ case "knowledge_get": {
2301
+ const id = await this.client.resolveId("kb_knowledge", args.id);
2302
+ return this.client.tableGet("kb_knowledge", id);
2303
+ }
2304
+ // Users & Groups
2305
+ case "users_search":
2306
+ return this.client.tableQuery("sys_user", {
2307
+ query: `nameLIKE${args.query}^ORuser_nameLIKE${args.query}^ORemailLIKE${args.query}`,
2308
+ fields: "user_name,name,email,department,title,manager,active",
2309
+ limit: args.limit || 10,
2310
+ });
2311
+ case "users_get":
2312
+ return this.client.tableGet("sys_user", args.id);
2313
+ case "groups_list":
2314
+ return this.client.tableQuery("sys_user_group", {
2315
+ query: args.query ? `nameLIKE${args.query}` : "",
2316
+ fields: "name,description,manager,type,active",
2317
+ limit: args.limit || 20,
2318
+ });
2319
+ case "groups_members": {
2320
+ // First resolve group if needed
2321
+ let groupId = args.id;
2322
+ if (!/^[a-f0-9]{32}$/.test(groupId)) {
2323
+ const groups = (await this.client.tableQuery("sys_user_group", {
2324
+ query: `name=${groupId}`,
2325
+ fields: "sys_id",
2326
+ limit: 1,
2327
+ }));
2328
+ if (groups.result?.length)
2329
+ groupId = groups.result[0].sys_id;
2330
+ }
2331
+ return this.client.tableQuery("sys_user_grmember", {
2332
+ query: `group=${groupId}`,
2333
+ fields: "user,group",
2334
+ });
2335
+ }
2336
+ // Tasks & Approvals
2337
+ case "tasks_my_tasks":
2338
+ return this.client.tableQuery("task", {
2339
+ query: "assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe^active=true",
2340
+ fields: "number,short_description,state,priority,sys_class_name",
2341
+ limit: args.limit || 20,
2342
+ });
2343
+ case "tasks_update": {
2344
+ const id = await this.client.resolveId("task", args.id);
2345
+ return this.client.tableUpdate("task", id, args.data);
2346
+ }
2347
+ case "approvals_pending":
2348
+ return this.client.getEnrichedApprovals({
2349
+ keyword: args.keyword,
2350
+ type: args.type,
2351
+ limit: args.limit || 20,
2352
+ });
2353
+ case "approvals_approve":
2354
+ return this.client.tableUpdate("sysapproval_approver", args.id, {
2355
+ state: "approved",
2356
+ comments: args.comments,
2357
+ });
2358
+ case "approvals_reject":
2359
+ return this.client.tableUpdate("sysapproval_approver", args.id, {
2360
+ state: "rejected",
2361
+ comments: args.comments,
2362
+ });
2363
+ // Generic table operations
2364
+ case "table_query":
2365
+ return this.client.tableQuery(args.table, {
2366
+ query: args.query,
2367
+ fields: args.fields,
2368
+ limit: args.limit || 20,
2369
+ offset: args.offset || 0,
2370
+ orderBy: args.order_by,
2371
+ orderDir: args.order_dir,
2372
+ });
2373
+ case "table_get":
2374
+ return this.client.tableGet(args.table, args.id, args.fields);
2375
+ case "table_create":
2376
+ return this.client.tableCreate(args.table, args.data);
2377
+ case "table_update":
2378
+ return this.client.tableUpdate(args.table, args.id, args.data);
2379
+ case "aggregate":
2380
+ return this.client.aggregate(args.table, {
2381
+ query: args.query,
2382
+ groupBy: args.group_by,
2383
+ aggregate: args.aggregate,
2384
+ });
2385
+ // Attachments (Full API)
2386
+ case "attachment_list":
2387
+ return this.client.tableQuery("sys_attachment", {
2388
+ query: `table_name=${args.table}^table_sys_id=${args.id}`,
2389
+ fields: "file_name,size_bytes,content_type,sys_created_on,sys_id",
2390
+ });
2391
+ case "attachment_get":
2392
+ return this.client.attachmentGet(args.id);
2393
+ case "attachment_download":
2394
+ return this.client.attachmentDownload(args.id);
2395
+ case "attachment_upload":
2396
+ return this.client.attachmentUpload(args.table, args.id, args.filename, args.content, args.content_type || "application/octet-stream");
2397
+ case "attachment_delete":
2398
+ return this.client.attachmentDelete(args.id);
2399
+ // Service Catalog (Full sn_sc API)
2400
+ case "catalog_categories":
2401
+ return this.client.tableQuery("sc_category", {
2402
+ query: args.catalog_id
2403
+ ? `sc_catalog=${args.catalog_id}`
2404
+ : "active=true",
2405
+ fields: "title,description,sc_catalog,parent,sys_id",
2406
+ limit: args.limit || 50,
2407
+ });
2408
+ case "catalog_item_variables":
2409
+ return this.client.catalogGetVariables(args.id);
2410
+ case "catalog_add_to_cart":
2411
+ return this.client.catalogAddToCart(args.item_id, args.quantity || 1, args.variables || {});
2412
+ case "catalog_get_cart":
2413
+ return this.client.catalogGetCart();
2414
+ case "catalog_submit_cart":
2415
+ return this.client.catalogSubmitCart();
2416
+ case "catalog_order_now":
2417
+ return this.client.catalogOrderNow(args.item_id, args.quantity || 1, args.variables || {}, args.requested_for);
2418
+ // Import Set API
2419
+ case "import_set_load":
2420
+ return this.client.importSetLoad(args.table, args.data);
2421
+ case "import_set_load_multiple":
2422
+ return this.client.importSetLoadMultiple(args.table, args.records);
2423
+ case "import_set_status":
2424
+ return this.client.tableGet("sys_import_set", args.id);
2425
+ // Batch API
2426
+ case "batch_request":
2427
+ return this.client.batchRequest(args.requests);
2428
+ // CMDB Instance API (Advanced)
2429
+ case "cmdb_classes":
2430
+ return this.client.tableQuery("sys_db_object", {
2431
+ query: `super_class.name=${args.parent_class || "cmdb_ci"}`,
2432
+ fields: "name,label,super_class",
2433
+ limit: args.limit || 50,
2434
+ });
2435
+ case "cmdb_instance_list":
2436
+ return this.client.cmdbInstanceList(args.class_name, args.query, args.limit);
2437
+ case "cmdb_create":
2438
+ return this.client.tableCreate(args.class_name, args.data);
2439
+ case "cmdb_update":
2440
+ return this.client.tableUpdate(args.class_name || "cmdb_ci", args.id, args.data);
2441
+ case "cmdb_relationship_create":
2442
+ return this.client.tableCreate("cmdb_rel_ci", {
2443
+ parent: args.parent,
2444
+ child: args.child,
2445
+ type: args.type,
2446
+ });
2447
+ // Task SLA
2448
+ case "sla_list":
2449
+ return this.client.tableQuery("contract_sla", {
2450
+ query: args.query,
2451
+ fields: "name,type,table,duration,sys_id",
2452
+ limit: args.limit || 20,
2453
+ });
2454
+ case "task_sla_list":
2455
+ return this.client.tableQuery("task_sla", {
2456
+ query: args.task_id
2457
+ ? `task=${args.task_id}`
2458
+ : args.query || "",
2459
+ fields: "task,sla,stage,has_breached,planned_end_time,end_time,percentage",
2460
+ limit: args.limit || 20,
2461
+ });
2462
+ case "task_sla_get":
2463
+ return this.client.tableGet("task_sla", args.id);
2464
+ // Workflow
2465
+ case "workflow_list":
2466
+ return this.client.tableQuery("wf_workflow", {
2467
+ query: args.active !== false ? "active=true" : args.query,
2468
+ fields: "name,table,description,active,sys_id",
2469
+ limit: args.limit || 20,
2470
+ });
2471
+ case "workflow_context_list": {
2472
+ let query = args.state ? `state=${args.state}` : "";
2473
+ if (args.table)
2474
+ query += `${query ? "^" : ""}table=${args.table}`;
2475
+ if (args.record_id)
2476
+ query += `${query ? "^" : ""}id=${args.record_id}`;
2477
+ return this.client.tableQuery("wf_context", {
2478
+ query,
2479
+ fields: "name,table,id,state,started,ended,workflow_version",
2480
+ limit: args.limit || 20,
2481
+ });
2482
+ }
2483
+ case "workflow_context_get":
2484
+ return this.client.tableGet("wf_context", args.id);
2485
+ // Email / Notifications
2486
+ case "email_list": {
2487
+ let query = args.type ? `type=${args.type}` : "";
2488
+ if (args.query)
2489
+ query = args.query;
2490
+ return this.client.tableQuery("sys_email", {
2491
+ query,
2492
+ fields: "subject,recipients,type,state,sys_created_on,target_table,instance",
2493
+ limit: args.limit || 20,
2494
+ });
2495
+ }
2496
+ case "email_get":
2497
+ return this.client.tableGet("sys_email", args.id);
2498
+ case "notification_list":
2499
+ return this.client.tableQuery("sysevent_email_action", {
2500
+ query: args.active !== false
2501
+ ? `active=true${args.table ? `^collection=${args.table}` : ""}`
2502
+ : args.query,
2503
+ fields: "name,collection,event_name,recipient_fields,active",
2504
+ limit: args.limit || 20,
2505
+ });
2506
+ // Events
2507
+ case "event_list":
2508
+ return this.client.tableQuery("sysevent", {
2509
+ query: args.name ? `name=${args.name}` : args.query || "",
2510
+ fields: "name,parm1,parm2,table,sys_created_on,state",
2511
+ limit: args.limit || 20,
2512
+ });
2513
+ case "event_create":
2514
+ return this.client.tableCreate("sysevent", {
2515
+ name: args.name,
2516
+ parm1: args.instance,
2517
+ parm2: args.parm2,
2518
+ table: args.table,
2519
+ });
2520
+ // Journal / Activity Stream
2521
+ case "journal_list": {
2522
+ const journalTable = `sys_journal_field`;
2523
+ let query = `element_id=${args.id}^name=${args.table}`;
2524
+ if (args.type && args.type !== "all") {
2525
+ query += `^element=${args.type}`;
2526
+ }
2527
+ return this.client.tableQuery(journalTable, {
2528
+ query,
2529
+ fields: "element,value,sys_created_on,sys_created_by",
2530
+ limit: args.limit || 50,
2531
+ });
2532
+ }
2533
+ case "activity_stream":
2534
+ return this.client.tableQuery("sys_history_line", {
2535
+ query: `set.id=${args.id}`,
2536
+ fields: "field,old,new,label,update_time,user",
2537
+ limit: args.limit || 50,
2538
+ });
2539
+ // Audit / History
2540
+ case "audit_list":
2541
+ return this.client.tableQuery("sys_audit", {
2542
+ query: `documentkey=${args.id}^tablename=${args.table}`,
2543
+ fields: "fieldname,oldvalue,newvalue,sys_created_on,user",
2544
+ limit: args.limit || 50,
2545
+ });
2546
+ // Scheduled Jobs
2547
+ case "scheduled_job_list":
2548
+ return this.client.tableQuery("sysauto", {
2549
+ query: args.active !== false ? "active=true" : args.query,
2550
+ fields: "name,run_type,run_dayofweek,run_time,active,sys_class_name",
2551
+ limit: args.limit || 20,
2552
+ });
2553
+ case "scheduled_job_run":
2554
+ // Trigger a scheduled job by updating its run field
2555
+ return this.client.tableUpdate("sysauto", args.id, {
2556
+ run_as_scheduled: true,
2557
+ });
2558
+ // Metrics / Performance Analytics
2559
+ case "metric_list":
2560
+ return this.client.tableQuery("pa_indicators", {
2561
+ query: args.query,
2562
+ fields: "name,description,unit,aggregate,frequency",
2563
+ limit: args.limit || 20,
2564
+ });
2565
+ case "metric_data": {
2566
+ let query = `indicator=${args.metric_id}`;
2567
+ if (args.start_date)
2568
+ query += `^sys_created_on>=${args.start_date}`;
2569
+ if (args.end_date)
2570
+ query += `^sys_created_on<=${args.end_date}`;
2571
+ return this.client.tableQuery("pa_scores", {
2572
+ query,
2573
+ fields: "indicator,value,date,breakdown,breakdown_value",
2574
+ limit: 100,
2575
+ });
2576
+ }
2577
+ // Update Sets
2578
+ case "update_set_list":
2579
+ return this.client.tableQuery("sys_update_set", {
2580
+ query: args.state
2581
+ ? `state=${args.state}`
2582
+ : args.query || "",
2583
+ fields: "name,state,application,description,sys_created_on",
2584
+ limit: args.limit || 20,
2585
+ });
2586
+ case "update_set_get":
2587
+ return this.client.tableGet("sys_update_set", args.id);
2588
+ // Security / ACL
2589
+ case "acl_list": {
2590
+ let query = "";
2591
+ if (args.table)
2592
+ query += `name STARTSWITH ${args.table}`;
2593
+ if (args.operation)
2594
+ query += `${query ? "^" : ""}operation=${args.operation}`;
2595
+ return this.client.tableQuery("sys_security_acl", {
2596
+ query,
2597
+ fields: "name,operation,type,condition,script,active",
2598
+ limit: args.limit || 20,
2599
+ });
2600
+ }
2601
+ case "role_list":
2602
+ return this.client.tableQuery("sys_user_role", {
2603
+ query: args.query,
2604
+ fields: "name,description,grantable,assignable_by",
2605
+ limit: args.limit || 50,
2606
+ });
2607
+ case "user_roles":
2608
+ return this.client.tableQuery("sys_user_has_role", {
2609
+ query: `user=${args.user_id}`,
2610
+ fields: "role,granted_by,inherited",
2611
+ });
2612
+ // Asset Management (ITAM)
2613
+ case "asset_list": {
2614
+ let query = args.query || "";
2615
+ if (args.asset_tag)
2616
+ query = `asset_tag=${args.asset_tag}`;
2617
+ if (args.state)
2618
+ query += `${query ? "^" : ""}install_status=${args.state}`;
2619
+ return this.client.tableQuery("alm_asset", {
2620
+ query,
2621
+ fields: "asset_tag,display_name,model,serial_number,install_status,assigned_to,cost",
2622
+ limit: args.limit || 20,
2623
+ });
2624
+ }
2625
+ case "asset_get": {
2626
+ const id = args.id;
2627
+ // Try by sys_id first, then by asset_tag
2628
+ if (/^[a-f0-9]{32}$/.test(id)) {
2629
+ return this.client.tableGet("alm_asset", id);
2630
+ }
2631
+ return this.client.tableQuery("alm_asset", {
2632
+ query: `asset_tag=${id}`,
2633
+ limit: 1,
2634
+ });
2635
+ }
2636
+ case "asset_create":
2637
+ return this.client.tableCreate("alm_asset", args);
2638
+ case "asset_update":
2639
+ return this.client.tableUpdate("alm_asset", args.id, args.data);
2640
+ // Software License Management
2641
+ case "license_list":
2642
+ return this.client.tableQuery("alm_license", {
2643
+ query: args.product
2644
+ ? `software_product.nameLIKE${args.product}`
2645
+ : args.query || "",
2646
+ fields: "software_product,start_date,end_date,quantity,license_type,cost",
2647
+ limit: args.limit || 20,
2648
+ });
2649
+ case "license_get":
2650
+ return this.client.tableGet("alm_license", args.id);
2651
+ case "license_entitlements":
2652
+ return this.client.tableQuery("alm_entitlement", {
2653
+ query: args.license_id ? `license=${args.license_id}` : "",
2654
+ fields: "license,user,allocated,start_date,end_date",
2655
+ limit: args.limit || 50,
2656
+ });
2657
+ // Software Asset Management
2658
+ case "software_list":
2659
+ return this.client.tableQuery("cmdb_sam_sw_install", {
2660
+ query: args.publisher
2661
+ ? `publisherLIKE${args.publisher}`
2662
+ : args.query || "",
2663
+ fields: "display_name,publisher,version,installed_on,install_date",
2664
+ limit: args.limit || 20,
2665
+ });
2666
+ case "software_product_list":
2667
+ return this.client.tableQuery("cmdb_software_product_model", {
2668
+ query: args.vendor
2669
+ ? `manufacturerLIKE${args.vendor}`
2670
+ : args.query || "",
2671
+ fields: "name,manufacturer,version,category",
2672
+ limit: args.limit || 20,
2673
+ });
2674
+ // Contracts
2675
+ case "contract_list":
2676
+ return this.client.tableQuery("ast_contract", {
2677
+ query: args.state
2678
+ ? `state=${args.state}`
2679
+ : args.query || "",
2680
+ fields: "number,short_description,vendor,start_date,end_date,state,contract_value",
2681
+ limit: args.limit || 20,
2682
+ });
2683
+ case "contract_get":
2684
+ return this.client.tableGet("ast_contract", args.id);
2685
+ // Location
2686
+ case "location_list":
2687
+ return this.client.tableQuery("cmn_location", {
2688
+ query: args.query || "",
2689
+ fields: "name,street,city,state,country,zip,latitude,longitude,parent",
2690
+ limit: args.limit || 50,
2691
+ });
2692
+ case "location_get": {
2693
+ const id = args.id;
2694
+ if (/^[a-f0-9]{32}$/.test(id)) {
2695
+ return this.client.tableGet("cmn_location", id);
2696
+ }
2697
+ return this.client.tableQuery("cmn_location", {
2698
+ query: `name=${id}`,
2699
+ limit: 1,
2700
+ });
2701
+ }
2702
+ // Department / Cost Center
2703
+ case "department_list":
2704
+ return this.client.tableQuery("cmn_department", {
2705
+ query: args.query || "",
2706
+ fields: "name,head,cost_center,description,parent",
2707
+ limit: args.limit || 50,
2708
+ });
2709
+ case "cost_center_list":
2710
+ return this.client.tableQuery("cmn_cost_center", {
2711
+ query: args.query || "",
2712
+ fields: "name,account_number,manager,valid_from,valid_to",
2713
+ limit: args.limit || 50,
2714
+ });
2715
+ // Discovery / ITOM
2716
+ case "discovery_status_list":
2717
+ return this.client.tableQuery("discovery_status", {
2718
+ query: args.state
2719
+ ? `state=${args.state}`
2720
+ : args.query || "",
2721
+ fields: "name,state,type,schedule,started,completed,scanned_devices",
2722
+ limit: args.limit || 20,
2723
+ });
2724
+ case "discovery_schedule_list":
2725
+ return this.client.tableQuery("discovery_schedule", {
2726
+ query: args.active !== false
2727
+ ? "active=true"
2728
+ : args.query || "",
2729
+ fields: "name,discover,run_type,run_time,max_run_time,active",
2730
+ limit: args.limit || 20,
2731
+ });
2732
+ // Table delete
2733
+ case "table_delete":
2734
+ return this.client.tableDelete(args.table, args.id);
2735
+ // Table schema
2736
+ case "table_schema":
2737
+ return this.client.tableQuery("sys_dictionary", {
2738
+ query: `name=${args.table}`,
2739
+ fields: "element,column_label,internal_type,max_length,mandatory,reference",
2740
+ limit: 200,
2741
+ });
2742
+ // Choice list
2743
+ case "choice_list":
2744
+ return this.client.tableQuery("sys_choice", {
2745
+ query: `name=${args.table}^element=${args.field}`,
2746
+ fields: "value,label,sequence,inactive",
2747
+ });
2748
+ default:
2749
+ throw new Error(`Unknown tool: ${name}`);
2750
+ }
2751
+ }
2752
+ async run() {
2753
+ const transport = new StdioServerTransport();
2754
+ await this.server.connect(transport);
2755
+ console.error("ServiceNow MCP server running on stdio");
2756
+ }
2757
+ }
2758
+ const server = new ServiceNowMcpServer();
2759
+ server.run().catch(console.error);