@tedorigawa001/servicenow-mcp 1.0.2

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.
Files changed (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +844 -0
  3. package/dist/cli/auth.d.ts +15 -0
  4. package/dist/cli/auth.d.ts.map +1 -0
  5. package/dist/cli/auth.js +200 -0
  6. package/dist/cli/auth.js.map +1 -0
  7. package/dist/cli/config-store.d.ts +28 -0
  8. package/dist/cli/config-store.d.ts.map +1 -0
  9. package/dist/cli/config-store.js +66 -0
  10. package/dist/cli/config-store.js.map +1 -0
  11. package/dist/cli/detect-clients.d.ts +16 -0
  12. package/dist/cli/detect-clients.d.ts.map +1 -0
  13. package/dist/cli/detect-clients.js +151 -0
  14. package/dist/cli/detect-clients.js.map +1 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +135 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/setup.d.ts +4 -0
  20. package/dist/cli/setup.d.ts.map +1 -0
  21. package/dist/cli/setup.js +564 -0
  22. package/dist/cli/setup.js.map +1 -0
  23. package/dist/cli/writers/index.d.ts +9 -0
  24. package/dist/cli/writers/index.d.ts.map +1 -0
  25. package/dist/cli/writers/index.js +135 -0
  26. package/dist/cli/writers/index.js.map +1 -0
  27. package/dist/prompts/index.d.ts +25 -0
  28. package/dist/prompts/index.d.ts.map +1 -0
  29. package/dist/prompts/index.js +38 -0
  30. package/dist/prompts/index.js.map +1 -0
  31. package/dist/prompts/itsm.d.ts +20 -0
  32. package/dist/prompts/itsm.d.ts.map +1 -0
  33. package/dist/prompts/itsm.js +110 -0
  34. package/dist/prompts/itsm.js.map +1 -0
  35. package/dist/prompts/user-prompts.d.ts +3 -0
  36. package/dist/prompts/user-prompts.d.ts.map +1 -0
  37. package/dist/prompts/user-prompts.js +35 -0
  38. package/dist/prompts/user-prompts.js.map +1 -0
  39. package/dist/resources/index.d.ts +26 -0
  40. package/dist/resources/index.d.ts.map +1 -0
  41. package/dist/resources/index.js +99 -0
  42. package/dist/resources/index.js.map +1 -0
  43. package/dist/server-http.d.ts +41 -0
  44. package/dist/server-http.d.ts.map +1 -0
  45. package/dist/server-http.js +187 -0
  46. package/dist/server-http.js.map +1 -0
  47. package/dist/server.d.ts +54 -0
  48. package/dist/server.d.ts.map +1 -0
  49. package/dist/server.js +150 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/servicenow/client.d.ts +137 -0
  52. package/dist/servicenow/client.d.ts.map +1 -0
  53. package/dist/servicenow/client.js +855 -0
  54. package/dist/servicenow/client.js.map +1 -0
  55. package/dist/servicenow/instances.d.ts +28 -0
  56. package/dist/servicenow/instances.d.ts.map +1 -0
  57. package/dist/servicenow/instances.js +190 -0
  58. package/dist/servicenow/instances.js.map +1 -0
  59. package/dist/servicenow/types.d.ts +578 -0
  60. package/dist/servicenow/types.d.ts.map +1 -0
  61. package/dist/servicenow/types.js +3 -0
  62. package/dist/servicenow/types.js.map +1 -0
  63. package/dist/tools/agile.d.ts +225 -0
  64. package/dist/tools/agile.d.ts.map +1 -0
  65. package/dist/tools/agile.js +205 -0
  66. package/dist/tools/agile.js.map +1 -0
  67. package/dist/tools/app-studio.d.ts +139 -0
  68. package/dist/tools/app-studio.d.ts.map +1 -0
  69. package/dist/tools/app-studio.js +139 -0
  70. package/dist/tools/app-studio.js.map +1 -0
  71. package/dist/tools/atf.d.ts +144 -0
  72. package/dist/tools/atf.d.ts.map +1 -0
  73. package/dist/tools/atf.js +186 -0
  74. package/dist/tools/atf.js.map +1 -0
  75. package/dist/tools/catalog.d.ts +966 -0
  76. package/dist/tools/catalog.d.ts.map +1 -0
  77. package/dist/tools/catalog.js +640 -0
  78. package/dist/tools/catalog.js.map +1 -0
  79. package/dist/tools/change.d.ts +351 -0
  80. package/dist/tools/change.d.ts.map +1 -0
  81. package/dist/tools/change.js +214 -0
  82. package/dist/tools/change.js.map +1 -0
  83. package/dist/tools/core.d.ts +768 -0
  84. package/dist/tools/core.d.ts.map +1 -0
  85. package/dist/tools/core.js +695 -0
  86. package/dist/tools/core.js.map +1 -0
  87. package/dist/tools/csm.d.ts +403 -0
  88. package/dist/tools/csm.d.ts.map +1 -0
  89. package/dist/tools/csm.js +256 -0
  90. package/dist/tools/csm.js.map +1 -0
  91. package/dist/tools/deployment.d.ts +366 -0
  92. package/dist/tools/deployment.d.ts.map +1 -0
  93. package/dist/tools/deployment.js +181 -0
  94. package/dist/tools/deployment.js.map +1 -0
  95. package/dist/tools/devops.d.ts +236 -0
  96. package/dist/tools/devops.d.ts.map +1 -0
  97. package/dist/tools/devops.js +207 -0
  98. package/dist/tools/devops.js.map +1 -0
  99. package/dist/tools/discovery.d.ts +306 -0
  100. package/dist/tools/discovery.d.ts.map +1 -0
  101. package/dist/tools/discovery.js +357 -0
  102. package/dist/tools/discovery.js.map +1 -0
  103. package/dist/tools/flow.d.ts +496 -0
  104. package/dist/tools/flow.d.ts.map +1 -0
  105. package/dist/tools/flow.js +348 -0
  106. package/dist/tools/flow.js.map +1 -0
  107. package/dist/tools/hrsd.d.ts +790 -0
  108. package/dist/tools/hrsd.d.ts.map +1 -0
  109. package/dist/tools/hrsd.js +378 -0
  110. package/dist/tools/hrsd.js.map +1 -0
  111. package/dist/tools/incident.d.ts +259 -0
  112. package/dist/tools/incident.d.ts.map +1 -0
  113. package/dist/tools/incident.js +170 -0
  114. package/dist/tools/incident.js.map +1 -0
  115. package/dist/tools/index.d.ts +13 -0
  116. package/dist/tools/index.d.ts.map +1 -0
  117. package/dist/tools/index.js +385 -0
  118. package/dist/tools/index.js.map +1 -0
  119. package/dist/tools/integration.d.ts +940 -0
  120. package/dist/tools/integration.d.ts.map +1 -0
  121. package/dist/tools/integration.js +685 -0
  122. package/dist/tools/integration.js.map +1 -0
  123. package/dist/tools/itam.d.ts +462 -0
  124. package/dist/tools/itam.d.ts.map +1 -0
  125. package/dist/tools/itam.js +306 -0
  126. package/dist/tools/itam.js.map +1 -0
  127. package/dist/tools/knowledge.d.ts +187 -0
  128. package/dist/tools/knowledge.d.ts.map +1 -0
  129. package/dist/tools/knowledge.js +161 -0
  130. package/dist/tools/knowledge.js.map +1 -0
  131. package/dist/tools/ml.d.ts +263 -0
  132. package/dist/tools/ml.d.ts.map +1 -0
  133. package/dist/tools/ml.js +251 -0
  134. package/dist/tools/ml.js.map +1 -0
  135. package/dist/tools/mobile.d.ts +352 -0
  136. package/dist/tools/mobile.d.ts.map +1 -0
  137. package/dist/tools/mobile.js +122 -0
  138. package/dist/tools/mobile.js.map +1 -0
  139. package/dist/tools/notification.d.ts +590 -0
  140. package/dist/tools/notification.d.ts.map +1 -0
  141. package/dist/tools/notification.js +382 -0
  142. package/dist/tools/notification.js.map +1 -0
  143. package/dist/tools/now-assist.d.ts +300 -0
  144. package/dist/tools/now-assist.d.ts.map +1 -0
  145. package/dist/tools/now-assist.js +227 -0
  146. package/dist/tools/now-assist.js.map +1 -0
  147. package/dist/tools/performance.d.ts +447 -0
  148. package/dist/tools/performance.d.ts.map +1 -0
  149. package/dist/tools/performance.js +473 -0
  150. package/dist/tools/performance.js.map +1 -0
  151. package/dist/tools/portal.d.ts +530 -0
  152. package/dist/tools/portal.d.ts.map +1 -0
  153. package/dist/tools/portal.js +425 -0
  154. package/dist/tools/portal.js.map +1 -0
  155. package/dist/tools/problem.d.ts +111 -0
  156. package/dist/tools/problem.d.ts.map +1 -0
  157. package/dist/tools/problem.js +101 -0
  158. package/dist/tools/problem.js.map +1 -0
  159. package/dist/tools/reporting.d.ts +825 -0
  160. package/dist/tools/reporting.d.ts.map +1 -0
  161. package/dist/tools/reporting.js +453 -0
  162. package/dist/tools/reporting.js.map +1 -0
  163. package/dist/tools/sam.d.ts +312 -0
  164. package/dist/tools/sam.d.ts.map +1 -0
  165. package/dist/tools/sam.js +360 -0
  166. package/dist/tools/sam.js.map +1 -0
  167. package/dist/tools/schema-helpers.d.ts +119 -0
  168. package/dist/tools/schema-helpers.d.ts.map +1 -0
  169. package/dist/tools/schema-helpers.js +121 -0
  170. package/dist/tools/schema-helpers.js.map +1 -0
  171. package/dist/tools/script.d.ts +714 -0
  172. package/dist/tools/script.d.ts.map +1 -0
  173. package/dist/tools/script.js +629 -0
  174. package/dist/tools/script.js.map +1 -0
  175. package/dist/tools/security.d.ts +802 -0
  176. package/dist/tools/security.d.ts.map +1 -0
  177. package/dist/tools/security.js +426 -0
  178. package/dist/tools/security.js.map +1 -0
  179. package/dist/tools/smart-query.d.ts +84 -0
  180. package/dist/tools/smart-query.d.ts.map +1 -0
  181. package/dist/tools/smart-query.js +320 -0
  182. package/dist/tools/smart-query.js.map +1 -0
  183. package/dist/tools/sys-properties.d.ts +315 -0
  184. package/dist/tools/sys-properties.d.ts.map +1 -0
  185. package/dist/tools/sys-properties.js +416 -0
  186. package/dist/tools/sys-properties.js.map +1 -0
  187. package/dist/tools/task.d.ts +82 -0
  188. package/dist/tools/task.d.ts.map +1 -0
  189. package/dist/tools/task.js +96 -0
  190. package/dist/tools/task.js.map +1 -0
  191. package/dist/tools/updateset.d.ts +159 -0
  192. package/dist/tools/updateset.d.ts.map +1 -0
  193. package/dist/tools/updateset.js +266 -0
  194. package/dist/tools/updateset.js.map +1 -0
  195. package/dist/tools/usem-approval.d.ts +130 -0
  196. package/dist/tools/usem-approval.d.ts.map +1 -0
  197. package/dist/tools/usem-approval.js +194 -0
  198. package/dist/tools/usem-approval.js.map +1 -0
  199. package/dist/tools/usem-config.d.ts +171 -0
  200. package/dist/tools/usem-config.d.ts.map +1 -0
  201. package/dist/tools/usem-config.js +254 -0
  202. package/dist/tools/usem-config.js.map +1 -0
  203. package/dist/tools/usem-integration.d.ts +228 -0
  204. package/dist/tools/usem-integration.d.ts.map +1 -0
  205. package/dist/tools/usem-integration.js +223 -0
  206. package/dist/tools/usem-integration.js.map +1 -0
  207. package/dist/tools/usem-sla.d.ts +201 -0
  208. package/dist/tools/usem-sla.d.ts.map +1 -0
  209. package/dist/tools/usem-sla.js +322 -0
  210. package/dist/tools/usem-sla.js.map +1 -0
  211. package/dist/tools/usem.d.ts +489 -0
  212. package/dist/tools/usem.d.ts.map +1 -0
  213. package/dist/tools/usem.js +514 -0
  214. package/dist/tools/usem.js.map +1 -0
  215. package/dist/tools/user.d.ts +206 -0
  216. package/dist/tools/user.d.ts.map +1 -0
  217. package/dist/tools/user.js +163 -0
  218. package/dist/tools/user.js.map +1 -0
  219. package/dist/tools/va.d.ts +217 -0
  220. package/dist/tools/va.d.ts.map +1 -0
  221. package/dist/tools/va.js +178 -0
  222. package/dist/tools/va.js.map +1 -0
  223. package/dist/tools/workspace.d.ts +565 -0
  224. package/dist/tools/workspace.d.ts.map +1 -0
  225. package/dist/tools/workspace.js +201 -0
  226. package/dist/tools/workspace.js.map +1 -0
  227. package/dist/utils/errors.d.ts +6 -0
  228. package/dist/utils/errors.d.ts.map +1 -0
  229. package/dist/utils/errors.js +11 -0
  230. package/dist/utils/errors.js.map +1 -0
  231. package/dist/utils/logging.d.ts +7 -0
  232. package/dist/utils/logging.d.ts.map +1 -0
  233. package/dist/utils/logging.js +15 -0
  234. package/dist/utils/logging.js.map +1 -0
  235. package/dist/utils/permissions.d.ts +21 -0
  236. package/dist/utils/permissions.d.ts.map +1 -0
  237. package/dist/utils/permissions.js +54 -0
  238. package/dist/utils/permissions.js.map +1 -0
  239. package/instances.example.json +69 -0
  240. package/package.json +110 -0
@@ -0,0 +1,695 @@
1
+ import { ServiceNowError } from '../utils/errors.js';
2
+ import { requireWrite } from '../utils/permissions.js';
3
+ import { instanceManager } from '../servicenow/instances.js';
4
+ export function getCoreToolDefinitions() {
5
+ return [
6
+ {
7
+ name: 'query_records',
8
+ description: 'Query ServiceNow records with filtering, field selection, pagination, and sorting',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ table: { type: 'string', description: 'Table name (e.g., "incident", "change_request")' },
13
+ query: { type: 'string', description: 'Encoded query string (e.g., "active=true^priority=1")' },
14
+ fields: { type: 'string', description: 'Comma-separated fields to return' },
15
+ limit: { type: 'number', description: 'Max records (default: 10, max: 1000)' },
16
+ orderBy: { type: 'string', description: 'Field to sort by. Prefix with "-" for descending' },
17
+ display_value: {
18
+ description: 'Return human-readable values for reference/choice fields. true = display values only ({display_value, link}); "all" = both raw and display ({value, display_value, link}). Omit for raw sys_id/values.',
19
+ oneOf: [{ type: 'boolean' }, { type: 'string', enum: ['all'] }],
20
+ },
21
+ },
22
+ required: ['table'],
23
+ },
24
+ },
25
+ {
26
+ name: 'get_table_schema',
27
+ description: 'Get the structure and field information for a ServiceNow table',
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ table: { type: 'string', description: 'Table name to inspect' },
32
+ },
33
+ required: ['table'],
34
+ },
35
+ },
36
+ {
37
+ name: 'get_record',
38
+ description: 'Retrieve complete details of a specific record by sys_id',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {
42
+ table: { type: 'string', description: 'Table name' },
43
+ sys_id: { type: 'string', description: '32-character system ID' },
44
+ fields: { type: 'string', description: 'Optional comma-separated fields' },
45
+ },
46
+ required: ['table', 'sys_id'],
47
+ },
48
+ },
49
+ {
50
+ name: 'get_user',
51
+ description: 'Look up user details by email or username',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ user_identifier: { type: 'string', description: 'Email address or username' },
56
+ },
57
+ required: ['user_identifier'],
58
+ },
59
+ },
60
+ {
61
+ name: 'get_group',
62
+ description: 'Find assignment group details by name or sys_id',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ group_identifier: { type: 'string', description: 'Group name or sys_id' },
67
+ },
68
+ required: ['group_identifier'],
69
+ },
70
+ },
71
+ {
72
+ name: 'search_cmdb_ci',
73
+ description: 'Search for configuration items (CIs) in the CMDB',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ query: { type: 'string', description: 'Encoded query (e.g., "sys_class_name=cmdb_ci_server")' },
78
+ limit: { type: 'number', description: 'Max CIs (default: 10, max: 100)' },
79
+ },
80
+ required: [],
81
+ },
82
+ },
83
+ {
84
+ name: 'get_cmdb_ci',
85
+ description: 'Get complete information about a specific configuration item',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {
89
+ ci_sys_id: { type: 'string', description: 'System ID of the CI' },
90
+ fields: { type: 'string', description: 'Optional comma-separated fields' },
91
+ },
92
+ required: ['ci_sys_id'],
93
+ },
94
+ },
95
+ {
96
+ name: 'list_relationships',
97
+ description: 'Show parent and child relationships for a CI',
98
+ inputSchema: {
99
+ type: 'object',
100
+ properties: {
101
+ ci_sys_id: { type: 'string', description: 'System ID of the CI' },
102
+ },
103
+ required: ['ci_sys_id'],
104
+ },
105
+ },
106
+ {
107
+ name: 'list_discovery_schedules',
108
+ description: 'List discovery schedules and their run status',
109
+ inputSchema: {
110
+ type: 'object',
111
+ properties: {
112
+ active_only: { type: 'boolean', description: 'Only show active schedules' },
113
+ },
114
+ required: [],
115
+ },
116
+ },
117
+ {
118
+ name: 'list_mid_servers',
119
+ description: 'List MID servers and verify they are healthy',
120
+ inputSchema: {
121
+ type: 'object',
122
+ properties: {
123
+ active_only: { type: 'boolean', description: 'Only show servers with status "Up"' },
124
+ },
125
+ required: [],
126
+ },
127
+ },
128
+ {
129
+ name: 'list_active_events',
130
+ description: 'Monitor critical infrastructure events',
131
+ inputSchema: {
132
+ type: 'object',
133
+ properties: {
134
+ query: { type: 'string', description: 'Filter events (e.g., "severity=1")' },
135
+ limit: { type: 'number', description: 'Max events (default: 10)' },
136
+ },
137
+ required: [],
138
+ },
139
+ },
140
+ {
141
+ name: 'cmdb_health_dashboard',
142
+ description: 'Get CMDB data quality metrics (completeness of server and network CI data)',
143
+ inputSchema: { type: 'object', properties: {}, required: [] },
144
+ },
145
+ {
146
+ name: 'service_mapping_summary',
147
+ description: 'View service dependencies and related CIs for impact analysis',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ service_sys_id: { type: 'string', description: 'System ID of the business service' },
152
+ },
153
+ required: ['service_sys_id'],
154
+ },
155
+ },
156
+ {
157
+ name: 'natural_language_search',
158
+ description: 'Search ServiceNow using plain English (experimental)',
159
+ inputSchema: {
160
+ type: 'object',
161
+ properties: {
162
+ query: { type: 'string', description: 'Plain English query' },
163
+ limit: { type: 'number', description: 'Max results (default: 10)' },
164
+ },
165
+ required: ['query'],
166
+ },
167
+ },
168
+ {
169
+ name: 'natural_language_update',
170
+ description: 'Update a record using natural language (experimental, requires WRITE_ENABLED=true)',
171
+ inputSchema: {
172
+ type: 'object',
173
+ properties: {
174
+ instruction: { type: 'string', description: 'Natural language update instruction' },
175
+ table: { type: 'string', description: 'Table name' },
176
+ },
177
+ required: ['instruction', 'table'],
178
+ },
179
+ },
180
+ {
181
+ name: 'list_instances',
182
+ description: 'List all configured ServiceNow instances (multi-instance / multi-customer support)',
183
+ inputSchema: { type: 'object', properties: {}, required: [] },
184
+ },
185
+ {
186
+ name: 'switch_instance',
187
+ description: 'Switch the active ServiceNow instance for this session',
188
+ inputSchema: {
189
+ type: 'object',
190
+ properties: {
191
+ name: { type: 'string', description: 'Instance name as configured (e.g. "prod", "dev", "customer_a")' },
192
+ },
193
+ required: ['name'],
194
+ },
195
+ },
196
+ {
197
+ name: 'get_current_instance',
198
+ description: 'Get the currently active ServiceNow instance name and URL',
199
+ inputSchema: { type: 'object', properties: {}, required: [] },
200
+ },
201
+ {
202
+ name: 'create_ci_relationship',
203
+ description: '[Write] Create a relationship between two CMDB Configuration Items',
204
+ inputSchema: {
205
+ type: 'object',
206
+ properties: {
207
+ parent: { type: 'string', description: 'Parent CI sys_id' },
208
+ child: { type: 'string', description: 'Child CI sys_id' },
209
+ type: { type: 'string', description: 'Relationship type (e.g. "Runs on::Runs")' },
210
+ },
211
+ required: ['parent', 'child', 'type'],
212
+ },
213
+ },
214
+ {
215
+ name: 'cmdb_impact_analysis',
216
+ description: 'Analyze the downstream impact of a Configuration Item change or outage',
217
+ inputSchema: {
218
+ type: 'object',
219
+ properties: {
220
+ ci_sys_id: { type: 'string', description: 'CI sys_id to analyze' },
221
+ depth: { type: 'number', description: 'Relationship depth to traverse (default: 2)' },
222
+ },
223
+ required: ['ci_sys_id'],
224
+ },
225
+ },
226
+ {
227
+ name: 'run_discovery_scan',
228
+ description: '[Write] Trigger a ServiceNow Discovery scan for network/infrastructure',
229
+ inputSchema: {
230
+ type: 'object',
231
+ properties: {
232
+ schedule_id: { type: 'string', description: 'Discovery schedule sys_id to run' },
233
+ mid_server: { type: 'string', description: 'Optional MID server sys_id — pins the schedule to this specific MID' },
234
+ },
235
+ required: ['schedule_id'],
236
+ },
237
+ },
238
+ {
239
+ name: 'describe_table',
240
+ description: 'Return full field schema for a ServiceNow table using sys_dictionary — includes field types, reference targets, mandatory/unique flags, and parent table. More accurate than get_table_schema for empty tables or custom tables.',
241
+ inputSchema: {
242
+ type: 'object',
243
+ properties: {
244
+ table: { type: 'string', description: 'Table name to inspect (e.g. "incident", "sn_vul_vulnerable_item")' },
245
+ include_inherited: { type: 'boolean', description: 'Include fields inherited from parent tables (default: false)' },
246
+ },
247
+ required: ['table'],
248
+ },
249
+ },
250
+ {
251
+ name: 'check_table_access',
252
+ description: 'Diagnose the connected service account\'s effective access to one or more tables BEFORE attempting operations. Returns per-table readable/writable flags plus the account\'s current user and assigned roles. Read is probed with a sysparm_limit=1 GET (200=readable, 403=denied); write is probed non-destructively with an empty PATCH to a reserved all-zero sys_id (404=writable, 403=denied) — no record is ever created or modified. Useful to avoid "User Not Authorized" retries.',
253
+ inputSchema: {
254
+ type: 'object',
255
+ properties: {
256
+ tables: {
257
+ type: 'array',
258
+ items: { type: 'string' },
259
+ description: 'Table names to check (max 20), e.g. ["incident", "sn_vul_vulnerable_item"]',
260
+ },
261
+ check_write: {
262
+ type: 'boolean',
263
+ description: 'Also probe write access via a non-destructive empty PATCH (default: true)',
264
+ },
265
+ },
266
+ required: ['tables'],
267
+ },
268
+ },
269
+ {
270
+ name: 'get_integration_health',
271
+ description: 'Report the health of Vulnerability Response data integration runs (sn_vul_integration_run) over a recent window. Surfaces failed runs and — critically — silent stalls where no runs occurred at all (e.g. an NVD/Qualys feed quietly failing with 503/429). Returns success/failure counts, the most recent success and failure timestamps, recent run details, and actionable alerts.',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ days: { type: 'number', description: 'Look back this many days (default: 7, max: 365)' },
276
+ source: { type: 'string', description: 'Filter to a single integration source, e.g. "NVD", "Qualys", "Tenable" (default: all)' },
277
+ },
278
+ },
279
+ },
280
+ ];
281
+ }
282
+ export async function executeCoreToolCall(client, name, args) {
283
+ switch (name) {
284
+ case 'query_records': {
285
+ const params = args;
286
+ if (!params.table)
287
+ throw new ServiceNowError('Table name is required', 'INVALID_REQUEST');
288
+ const response = await client.queryRecords(params);
289
+ return { count: response.count, records: response.records, summary: `Found ${response.count} record(s) in "${params.table}"` };
290
+ }
291
+ case 'get_table_schema':
292
+ if (!args.table)
293
+ throw new ServiceNowError('Table name is required', 'INVALID_REQUEST');
294
+ return await client.getTableSchema(args.table);
295
+ case 'get_record': {
296
+ const p = args;
297
+ if (!p.table || !p.sys_id)
298
+ throw new ServiceNowError('table and sys_id are required', 'INVALID_REQUEST');
299
+ return await client.getRecord(p.table, p.sys_id, p.fields);
300
+ }
301
+ case 'get_user':
302
+ if (!args.user_identifier)
303
+ throw new ServiceNowError('user_identifier is required', 'INVALID_REQUEST');
304
+ return await client.getUser(args.user_identifier);
305
+ case 'get_group':
306
+ if (!args.group_identifier)
307
+ throw new ServiceNowError('group_identifier is required', 'INVALID_REQUEST');
308
+ return await client.getGroup(args.group_identifier);
309
+ case 'search_cmdb_ci':
310
+ return await client.searchCmdbCi(args.query, args.limit);
311
+ case 'get_cmdb_ci': {
312
+ const p = args;
313
+ if (!p.ci_sys_id)
314
+ throw new ServiceNowError('ci_sys_id is required', 'INVALID_REQUEST');
315
+ return await client.getCmdbCi(p.ci_sys_id, p.fields);
316
+ }
317
+ case 'list_relationships': {
318
+ const p = args;
319
+ if (!p.ci_sys_id)
320
+ throw new ServiceNowError('ci_sys_id is required', 'INVALID_REQUEST');
321
+ return await client.listRelationships(p.ci_sys_id);
322
+ }
323
+ case 'list_discovery_schedules':
324
+ return await client.listDiscoverySchedules(args.active_only);
325
+ case 'list_mid_servers':
326
+ return await client.listMidServers(args.active_only);
327
+ case 'list_active_events':
328
+ return await client.listActiveEvents(args.query, args.limit);
329
+ case 'cmdb_health_dashboard':
330
+ return await client.cmdbHealthDashboard();
331
+ case 'service_mapping_summary': {
332
+ const p = args;
333
+ if (!p.service_sys_id)
334
+ throw new ServiceNowError('service_sys_id is required', 'INVALID_REQUEST');
335
+ return await client.serviceMappingSummary(p.service_sys_id);
336
+ }
337
+ case 'natural_language_search':
338
+ return await client.naturalLanguageSearch(args.query, args.limit);
339
+ case 'natural_language_update':
340
+ requireWrite();
341
+ return await client.naturalLanguageUpdate(args.instruction, args.table);
342
+ case 'list_instances':
343
+ return {
344
+ current: instanceManager.getCurrentName(),
345
+ instances: instanceManager.listAll(),
346
+ total: instanceManager.listNames().length,
347
+ };
348
+ case 'switch_instance':
349
+ if (!args.name)
350
+ throw new ServiceNowError('name is required', 'INVALID_REQUEST');
351
+ instanceManager.switch(args.name);
352
+ return {
353
+ action: 'switched',
354
+ active_instance: instanceManager.getCurrentName(),
355
+ url: instanceManager.getCurrentUrl(),
356
+ };
357
+ case 'get_current_instance':
358
+ return {
359
+ name: instanceManager.getCurrentName(),
360
+ url: instanceManager.getCurrentUrl(),
361
+ all_instances: instanceManager.listNames(),
362
+ };
363
+ case 'create_ci_relationship': {
364
+ requireWrite();
365
+ if (!args.parent || !args.child || !args.type)
366
+ throw new ServiceNowError('parent, child, and type are required', 'INVALID_REQUEST');
367
+ const result = await client.createRecord('cmdb_rel_ci', {
368
+ parent: args.parent,
369
+ child: args.child,
370
+ type: args.type,
371
+ });
372
+ return { ...result, summary: `Created CI relationship: ${args.parent} -> ${args.child} (${args.type})` };
373
+ }
374
+ case 'cmdb_impact_analysis': {
375
+ if (!args.ci_sys_id)
376
+ throw new ServiceNowError('ci_sys_id is required', 'INVALID_REQUEST');
377
+ if (!/^[0-9a-f]{32}$/i.test(args.ci_sys_id)) {
378
+ throw new ServiceNowError('ci_sys_id must be a 32-character hex string', 'VALIDATION_ERROR');
379
+ }
380
+ const maxDepth = args.depth || 2;
381
+ const visited = new Set();
382
+ const impactTree = [];
383
+ async function traverse(ciSysId, currentDepth) {
384
+ if (currentDepth > maxDepth || visited.has(ciSysId))
385
+ return [];
386
+ visited.add(ciSysId);
387
+ const resp = await client.queryRecords({
388
+ table: 'cmdb_rel_ci',
389
+ query: `parent=${ciSysId}`,
390
+ fields: 'sys_id,child,type,parent',
391
+ limit: 100,
392
+ });
393
+ const children = [];
394
+ for (const rel of resp.records) {
395
+ const childId = typeof rel.child === 'object' ? rel.child.value : rel.child;
396
+ const downstream = await traverse(childId, currentDepth + 1);
397
+ children.push({ relationship: rel, downstream });
398
+ }
399
+ return children;
400
+ }
401
+ const downstream = await traverse(args.ci_sys_id, 1);
402
+ impactTree.push({ ci_sys_id: args.ci_sys_id, depth: maxDepth, downstream });
403
+ return { impact_analysis: impactTree, total_impacted: visited.size - 1 };
404
+ }
405
+ case 'run_discovery_scan': {
406
+ requireWrite();
407
+ if (!args.schedule_id)
408
+ throw new ServiceNowError('schedule_id is required', 'INVALID_REQUEST');
409
+ // Inserting a discovery_status record directly does NOT launch any probes
410
+ // (verified live: the record sits at state=Active with zero probes and the
411
+ // MID never receives a Shazzam job). The Discovery engine only starts runs
412
+ // created by the scheduler or the Discover Now UI action, so the REST-safe
413
+ // trigger is to flip the schedule itself to run once, a few seconds from now.
414
+ const runStart = new Date(Date.now() + 10_000).toISOString().replace('T', ' ').slice(0, 19);
415
+ const payload = { run_type: 'once', run_start: runStart };
416
+ if (args.mid_server) {
417
+ payload.mid_select_method = 'specific_mid';
418
+ payload.mid_server = args.mid_server;
419
+ }
420
+ await client.updateRecord('discovery_schedule', args.schedule_id, payload);
421
+ // Discovery.isValidDiscoverySchedule aborts SILENTLY (no status record, no error)
422
+ // when an IP-based schedule has no active range linked via the `schedule` field
423
+ // on discovery_range_item (NOT `parent`, which links range sets). Surface that
424
+ // trap here instead of leaving the caller waiting for a run that never starts.
425
+ const ranges = await client.queryRecords({
426
+ table: 'discovery_range_item',
427
+ query: `schedule=${args.schedule_id}^active=true`,
428
+ limit: 1,
429
+ fields: 'sys_id',
430
+ });
431
+ const rangeWarning = ranges.count === 0
432
+ ? 'Warning: no active discovery_range_item is linked to this schedule via the "schedule" field. ' +
433
+ 'IP-based schedules (discover=CIs/IPs) abort silently without one.'
434
+ : undefined;
435
+ return {
436
+ action: 'triggered',
437
+ schedule_id: args.schedule_id,
438
+ run_start_utc: runStart,
439
+ active_range_count: ranges.count,
440
+ ...(rangeWarning ? { warning: rangeWarning } : {}),
441
+ summary: `Discovery schedule ${args.schedule_id} set to run once at ${runStart} UTC. ` +
442
+ 'The run appears in discovery_status shortly after; note the schedule run_type is now "once".',
443
+ };
444
+ }
445
+ case 'describe_table': {
446
+ if (!args.table)
447
+ throw new ServiceNowError('table is required', 'INVALID_REQUEST');
448
+ const tableName = args.table;
449
+ const includeInherited = args.include_inherited === true;
450
+ // Fetch table metadata from sys_db_object
451
+ const dbObjResp = await client.queryRecords({
452
+ table: 'sys_db_object',
453
+ query: `name=${tableName}`,
454
+ fields: 'name,label,super_class',
455
+ limit: 1,
456
+ });
457
+ if (dbObjResp.count === 0) {
458
+ throw new ServiceNowError(`Table "${tableName}" not found in sys_db_object`, 'NOT_FOUND');
459
+ }
460
+ const dbObj = dbObjResp.records[0];
461
+ // super_class returns {value: sys_id, link: ...} — resolve to table name via a second query
462
+ let parentTable;
463
+ const superClassSysId = dbObj.super_class && typeof dbObj.super_class === 'object'
464
+ ? dbObj.super_class.value || undefined
465
+ : undefined;
466
+ if (includeInherited && superClassSysId) {
467
+ const parentResp = await client.queryRecords({
468
+ table: 'sys_db_object',
469
+ query: `sys_id=${superClassSysId}`,
470
+ fields: 'name',
471
+ limit: 1,
472
+ });
473
+ parentTable = parentResp.records[0]?.name;
474
+ }
475
+ // Determine which tables to fetch fields for
476
+ const tables = [tableName];
477
+ if (includeInherited && parentTable) {
478
+ tables.push(parentTable);
479
+ }
480
+ const allFields = [];
481
+ for (const t of tables) {
482
+ const dictResp = await client.queryRecords({
483
+ table: 'sys_dictionary',
484
+ query: `name=${t}^internal_type!=collection^element!=NULL`,
485
+ fields: 'element,column_label,internal_type,reference,mandatory,unique,name',
486
+ limit: 500,
487
+ });
488
+ for (const row of dictResp.records) {
489
+ // ServiceNow Table API returns reference/choice fields as {value, link} objects
490
+ const strVal = (v) => v && typeof v === 'object' ? (v.value ?? '') : v ?? '';
491
+ const refValue = strVal(row.reference) || undefined;
492
+ const internalType = strVal(row.internal_type);
493
+ const element = strVal(row.element);
494
+ const columnLabel = strVal(row.column_label) || element;
495
+ const definedIn = strVal(row.name) || t;
496
+ allFields.push({
497
+ element,
498
+ column_label: columnLabel,
499
+ type: internalType,
500
+ ...(refValue ? { reference: refValue } : {}),
501
+ mandatory: row.mandatory === 'true' || row.mandatory === true,
502
+ unique: row.unique === 'true' || row.unique === true,
503
+ ...(includeInherited ? { defined_in: definedIn } : {}),
504
+ });
505
+ }
506
+ }
507
+ allFields.sort((a, b) => a.element.localeCompare(b.element));
508
+ return {
509
+ table: tableName,
510
+ label: dbObj.label || tableName,
511
+ ...(parentTable ? { parent_table: parentTable } : {}),
512
+ field_count: allFields.length,
513
+ fields: allFields,
514
+ summary: `Table "${tableName}" has ${allFields.length} field(s)${includeInherited && parentTable ? ` (includes inherited from "${parentTable}")` : ''}`,
515
+ };
516
+ }
517
+ case 'check_table_access': {
518
+ const tables = args.tables;
519
+ if (!Array.isArray(tables) || tables.length === 0) {
520
+ throw new ServiceNowError('tables must be a non-empty array of table names', 'INVALID_REQUEST');
521
+ }
522
+ if (tables.length > 20) {
523
+ throw new ServiceNowError('tables is limited to 20 entries per call', 'INVALID_REQUEST');
524
+ }
525
+ const checkWrite = args.check_write !== false;
526
+ const ZERO_SYS_ID = '0'.repeat(32);
527
+ const strVal = (v) => v && typeof v === 'object' ? (v.value ?? '') : (v ?? '');
528
+ const codeOf = (e) => e instanceof ServiceNowError ? e.code : 'UNKNOWN';
529
+ // ── Current user + assigned roles (best-effort) ──
530
+ let currentUser;
531
+ let currentRoles = [];
532
+ let rolesError;
533
+ try {
534
+ const me = await client.queryRecords({
535
+ table: 'sys_user',
536
+ query: 'sys_id=javascript:gs.getUserID()',
537
+ fields: 'user_name,name',
538
+ limit: 1,
539
+ });
540
+ currentUser = strVal(me.records[0]?.user_name) || strVal(me.records[0]?.name) || undefined;
541
+ const roleResp = await client.queryRecords({
542
+ table: 'sys_user_has_role',
543
+ query: 'user=javascript:gs.getUserID()',
544
+ fields: 'role.name',
545
+ limit: 500,
546
+ });
547
+ currentRoles = [
548
+ ...new Set(roleResp.records.map(r => strVal(r['role.name'])).filter(Boolean)),
549
+ ].sort();
550
+ }
551
+ catch (e) {
552
+ rolesError = e instanceof Error ? e.message : 'Failed to resolve current roles';
553
+ }
554
+ // ── Per-table read/write probes ──
555
+ const results = [];
556
+ for (const raw of tables) {
557
+ const table = String(raw);
558
+ const entry = { table, readable: false, writable: checkWrite ? false : null };
559
+ // Read probe
560
+ try {
561
+ await client.queryRecords({ table, fields: 'sys_id', limit: 1 });
562
+ entry.readable = true;
563
+ }
564
+ catch (e) {
565
+ const code = codeOf(e);
566
+ if (code === 'INSUFFICIENT_PRIVILEGES') {
567
+ entry.readable = false;
568
+ }
569
+ else {
570
+ // Invalid/unknown table or validation failure — no point probing write
571
+ entry.error = e instanceof Error ? e.message : 'Read probe failed';
572
+ entry.writable = null;
573
+ results.push(entry);
574
+ continue;
575
+ }
576
+ }
577
+ // Write probe (non-destructive empty PATCH to a reserved sys_id)
578
+ if (checkWrite) {
579
+ try {
580
+ await client.updateRecord(table, ZERO_SYS_ID, {});
581
+ entry.writable = true; // 2xx (not expected for a missing record, but counts as access)
582
+ }
583
+ catch (e) {
584
+ const code = codeOf(e);
585
+ if (code === 'NOT_FOUND') {
586
+ entry.writable = true; // passed the write gate; record simply does not exist
587
+ }
588
+ else if (code === 'INSUFFICIENT_PRIVILEGES') {
589
+ entry.writable = false;
590
+ }
591
+ else {
592
+ entry.writable = null;
593
+ entry.error = (entry.error ? entry.error + '; ' : '') +
594
+ `write probe inconclusive (${code})`;
595
+ }
596
+ }
597
+ }
598
+ results.push(entry);
599
+ }
600
+ const readableCount = results.filter(r => r.readable).length;
601
+ const writableCount = results.filter(r => r.writable === true).length;
602
+ return {
603
+ current_user: currentUser,
604
+ current_roles: currentRoles,
605
+ ...(rolesError ? { roles_error: rolesError } : {}),
606
+ results,
607
+ summary: `${results.length} table(s) checked: ${readableCount} readable` +
608
+ (checkWrite ? `, ${writableCount} writable` : '') +
609
+ (currentUser ? ` (as ${currentUser}, ${currentRoles.length} role(s))` : ''),
610
+ };
611
+ }
612
+ case 'get_integration_health': {
613
+ // Clamp the window to a sane integer; the value is interpolated into a
614
+ // gs.daysAgo() expression so it must be a plain number.
615
+ const rawDays = Number(args.days);
616
+ const days = Number.isFinite(rawDays) ? Math.min(Math.max(Math.trunc(rawDays), 1), 365) : 7;
617
+ // Sanitize the optional source filter (exact match, no encoded-query operators).
618
+ const source = typeof args.source === 'string' && args.source.trim()
619
+ ? args.source.replace(/[^a-zA-Z0-9 _.-]/g, '').trim()
620
+ : undefined;
621
+ let query = `start_datetime>=javascript:gs.daysAgo(${days})`;
622
+ if (source)
623
+ query += `^source=${source}`;
624
+ let records;
625
+ try {
626
+ const resp = await client.queryRecords({
627
+ table: 'sn_vul_integration_run',
628
+ query,
629
+ fields: 'number,source,substate,state,start_datetime,end_datetime,vi_created,vi_updated,vi_new_findings,notes,fatal_error_message',
630
+ orderBy: '-start_datetime',
631
+ limit: 200,
632
+ });
633
+ records = resp.records;
634
+ }
635
+ catch (e) {
636
+ if (e instanceof ServiceNowError && (e.code === 'INVALID_REQUEST' || e.code === 'NOT_FOUND')) {
637
+ throw new ServiceNowError('Table sn_vul_integration_run is not available — Vulnerability Response may not be installed on this instance.', 'NOT_FOUND');
638
+ }
639
+ throw e;
640
+ }
641
+ const num = (v) => {
642
+ const n = parseInt(String(v ?? ''), 10);
643
+ return Number.isFinite(n) ? n : 0;
644
+ };
645
+ const isFailed = (r) => String(r.substate).toLowerCase() === 'failed' || !!String(r.fatal_error_message ?? '').trim();
646
+ const isSuccess = (r) => String(r.substate).toLowerCase() === 'success';
647
+ const failedRuns = records.filter(isFailed);
648
+ const successRuns = records.filter(isSuccess);
649
+ // records are ordered most-recent-first
650
+ const lastSuccess = successRuns[0]?.start_datetime;
651
+ const lastFailure = failedRuns[0]?.start_datetime;
652
+ const recent_runs = records.slice(0, 25).map(r => ({
653
+ source: r.source || '',
654
+ substate: r.substate || '',
655
+ start_datetime: r.start_datetime || '',
656
+ end_datetime: r.end_datetime || '',
657
+ vi_created: num(r.vi_created),
658
+ vi_updated: num(r.vi_updated),
659
+ notes: String(r.fatal_error_message || r.notes || '').trim(),
660
+ }));
661
+ // ── Alerts ──
662
+ const alerts = [];
663
+ const scope = source ? `source "${source}"` : 'any integration';
664
+ if (records.length === 0) {
665
+ alerts.push(`No integration runs for ${scope} in the last ${days} day(s) — the feed may be stalled (e.g. silent 503/429 failures).`);
666
+ }
667
+ if (failedRuns.length > 0) {
668
+ alerts.push(`${failedRuns.length} failed run(s) in the last ${days} day(s).`);
669
+ }
670
+ if (records.length > 0 && isFailed(records[0])) {
671
+ const top = records[0];
672
+ alerts.push(`Most recent run failed: ${top.source || 'unknown'} at ${top.start_datetime || 'unknown'} — ${String(top.fatal_error_message || top.notes || 'no detail').trim()}`);
673
+ }
674
+ if (successRuns.length > 0 && !lastSuccess) {
675
+ alerts.push('Recent successes found but without timestamps.');
676
+ }
677
+ return {
678
+ window_days: days,
679
+ ...(source ? { source } : {}),
680
+ summary: {
681
+ total_runs: records.length,
682
+ success: successRuns.length,
683
+ failed: failedRuns.length,
684
+ ...(lastSuccess ? { last_success: lastSuccess } : {}),
685
+ ...(lastFailure ? { last_failure: lastFailure } : {}),
686
+ },
687
+ recent_runs,
688
+ alerts,
689
+ };
690
+ }
691
+ default:
692
+ return null; // not handled here
693
+ }
694
+ }
695
+ //# sourceMappingURL=core.js.map