snow-flow 3.1.2 → 3.2.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/dynamic-version.js +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +523 -0
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp.js +1232 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp.js +1416 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp.js +1192 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp.js +1009 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ServiceNow Change Management, Virtual Agent & Performance Analytics MCP Server
|
|
5
|
+
* Handles change requests, chatbot conversations, and performance analytics
|
|
6
|
+
* Uses official ServiceNow REST APIs
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
10
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
12
|
+
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
13
|
+
const mcp_auth_middleware_js_1 = require("../utils/mcp-auth-middleware.js");
|
|
14
|
+
const mcp_config_manager_js_1 = require("../utils/mcp-config-manager.js");
|
|
15
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
16
|
+
class ServiceNowChangeVirtualAgentPAMCP {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.server = new index_js_1.Server({
|
|
19
|
+
name: 'servicenow-change-virtualagent-pa',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
}, {
|
|
22
|
+
capabilities: {
|
|
23
|
+
tools: {},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
27
|
+
this.logger = new logger_js_1.Logger('ServiceNowChangeVirtualAgentPAMCP');
|
|
28
|
+
this.config = mcp_config_manager_js_1.mcpConfig.getConfig();
|
|
29
|
+
this.setupHandlers();
|
|
30
|
+
}
|
|
31
|
+
setupHandlers() {
|
|
32
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
33
|
+
tools: [
|
|
34
|
+
// Change Management Tools
|
|
35
|
+
{
|
|
36
|
+
name: 'snow_create_change_request',
|
|
37
|
+
description: 'Creates a change request in ServiceNow. Change requests track modifications to IT infrastructure and require approval workflows.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
short_description: { type: 'string', description: 'Brief description of the change' },
|
|
42
|
+
description: { type: 'string', description: 'Detailed change description' },
|
|
43
|
+
type: { type: 'string', description: 'Change type: standard, normal, emergency' },
|
|
44
|
+
category: { type: 'string', description: 'Change category: hardware, software, network, etc.' },
|
|
45
|
+
priority: { type: 'number', description: 'Priority: 1-Critical, 2-High, 3-Moderate, 4-Low' },
|
|
46
|
+
risk: { type: 'number', description: 'Risk level: 1-High, 2-Moderate, 3-Low' },
|
|
47
|
+
impact: { type: 'number', description: 'Impact level: 1-High, 2-Medium, 3-Low' },
|
|
48
|
+
assignment_group: { type: 'string', description: 'Group responsible for the change' },
|
|
49
|
+
assigned_to: { type: 'string', description: 'Person assigned to the change' },
|
|
50
|
+
start_date: { type: 'string', description: 'Planned start date (YYYY-MM-DD HH:MM:SS)' },
|
|
51
|
+
end_date: { type: 'string', description: 'Planned end date (YYYY-MM-DD HH:MM:SS)' },
|
|
52
|
+
justification: { type: 'string', description: 'Business justification' },
|
|
53
|
+
implementation_plan: { type: 'string', description: 'Implementation steps' },
|
|
54
|
+
backout_plan: { type: 'string', description: 'Rollback plan if change fails' },
|
|
55
|
+
test_plan: { type: 'string', description: 'Testing procedure' }
|
|
56
|
+
},
|
|
57
|
+
required: ['short_description', 'type']
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'snow_create_change_task',
|
|
62
|
+
description: 'Creates a change task within a change request. Tasks break down the change into manageable work items.',
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
change_request: { type: 'string', description: 'Parent change request sys_id or number' },
|
|
67
|
+
short_description: { type: 'string', description: 'Task description' },
|
|
68
|
+
description: { type: 'string', description: 'Detailed task description' },
|
|
69
|
+
assignment_group: { type: 'string', description: 'Group responsible' },
|
|
70
|
+
assigned_to: { type: 'string', description: 'Person assigned' },
|
|
71
|
+
planned_start_date: { type: 'string', description: 'Start date' },
|
|
72
|
+
planned_end_date: { type: 'string', description: 'End date' },
|
|
73
|
+
order: { type: 'number', description: 'Task execution order' }
|
|
74
|
+
},
|
|
75
|
+
required: ['change_request', 'short_description']
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'snow_get_change_request',
|
|
80
|
+
description: 'Retrieves change request details including approval status, tasks, and related items.',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
sys_id: { type: 'string', description: 'Change request sys_id or number' },
|
|
85
|
+
include_tasks: { type: 'boolean', description: 'Include change tasks', default: true },
|
|
86
|
+
include_approvals: { type: 'boolean', description: 'Include approval history', default: true }
|
|
87
|
+
},
|
|
88
|
+
required: ['sys_id']
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'snow_update_change_state',
|
|
93
|
+
description: 'Updates the state of a change request through its lifecycle.',
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
sys_id: { type: 'string', description: 'Change request sys_id or number' },
|
|
98
|
+
state: { type: 'string', description: 'New state: draft, assess, authorize, scheduled, implement, review, closed, cancelled' },
|
|
99
|
+
close_notes: { type: 'string', description: 'Closure notes (for closed state)' },
|
|
100
|
+
close_code: { type: 'string', description: 'Closure code: successful, unsuccessful, cancelled' }
|
|
101
|
+
},
|
|
102
|
+
required: ['sys_id', 'state']
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'snow_schedule_cab_meeting',
|
|
107
|
+
description: 'Schedules a Change Advisory Board (CAB) meeting for change review.',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
change_request: { type: 'string', description: 'Change request to review' },
|
|
112
|
+
meeting_date: { type: 'string', description: 'Meeting date/time' },
|
|
113
|
+
attendees: { type: 'array', items: { type: 'string' }, description: 'Meeting attendees' },
|
|
114
|
+
agenda: { type: 'string', description: 'Meeting agenda' },
|
|
115
|
+
location: { type: 'string', description: 'Meeting location or link' }
|
|
116
|
+
},
|
|
117
|
+
required: ['change_request', 'meeting_date']
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'snow_search_change_requests',
|
|
122
|
+
description: 'Searches for change requests with filters for state, date range, assignment, and risk.',
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
query: { type: 'string', description: 'Search query' },
|
|
127
|
+
state: { type: 'string', description: 'Filter by state' },
|
|
128
|
+
type: { type: 'string', description: 'Filter by type' },
|
|
129
|
+
risk: { type: 'number', description: 'Filter by risk level' },
|
|
130
|
+
assigned_to: { type: 'string', description: 'Filter by assignee' },
|
|
131
|
+
date_from: { type: 'string', description: 'Start date range' },
|
|
132
|
+
date_to: { type: 'string', description: 'End date range' },
|
|
133
|
+
limit: { type: 'number', description: 'Maximum results', default: 20 }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
// Virtual Agent / Chatbot Tools
|
|
138
|
+
{
|
|
139
|
+
name: 'snow_create_va_topic',
|
|
140
|
+
description: 'Creates a Virtual Agent conversation topic. Topics define conversation flows for specific user intents.',
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
name: { type: 'string', description: 'Topic name' },
|
|
145
|
+
description: { type: 'string', description: 'Topic description' },
|
|
146
|
+
utterances: { type: 'array', items: { type: 'string' }, description: 'Training phrases that trigger this topic' },
|
|
147
|
+
category: { type: 'string', description: 'Topic category' },
|
|
148
|
+
active: { type: 'boolean', description: 'Is topic active', default: true },
|
|
149
|
+
live_agent_enabled: { type: 'boolean', description: 'Allow escalation to live agent', default: false }
|
|
150
|
+
},
|
|
151
|
+
required: ['name', 'utterances']
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'snow_create_va_topic_block',
|
|
156
|
+
description: 'Creates a conversation block within a Virtual Agent topic. Blocks define conversation steps and responses.',
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: 'object',
|
|
159
|
+
properties: {
|
|
160
|
+
topic: { type: 'string', description: 'Parent topic sys_id' },
|
|
161
|
+
name: { type: 'string', description: 'Block name' },
|
|
162
|
+
type: { type: 'string', description: 'Block type: text, question, script, handoff, decision' },
|
|
163
|
+
order: { type: 'number', description: 'Block execution order' },
|
|
164
|
+
text: { type: 'string', description: 'Response text for text blocks' },
|
|
165
|
+
script: { type: 'string', description: 'Script for script blocks' },
|
|
166
|
+
variable: { type: 'string', description: 'Variable to store user input' },
|
|
167
|
+
next_block: { type: 'string', description: 'Next block to execute' }
|
|
168
|
+
},
|
|
169
|
+
required: ['topic', 'name', 'type', 'order']
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'snow_get_va_conversation',
|
|
174
|
+
description: 'Retrieves Virtual Agent conversation history and context for a specific session.',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {
|
|
178
|
+
conversation_id: { type: 'string', description: 'Conversation sys_id' },
|
|
179
|
+
user_id: { type: 'string', description: 'User sys_id (alternative to conversation_id)' },
|
|
180
|
+
include_transcript: { type: 'boolean', description: 'Include full transcript', default: true },
|
|
181
|
+
limit: { type: 'number', description: 'Maximum messages to retrieve', default: 50 }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'snow_send_va_message',
|
|
187
|
+
description: 'Sends a message to Virtual Agent and gets the response. Simulates user interaction with the chatbot.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
conversation_id: { type: 'string', description: 'Existing conversation ID (optional)' },
|
|
192
|
+
message: { type: 'string', description: 'User message text' },
|
|
193
|
+
user_id: { type: 'string', description: 'User sys_id' },
|
|
194
|
+
context: { type: 'object', description: 'Additional context variables' }
|
|
195
|
+
},
|
|
196
|
+
required: ['message']
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'snow_handoff_to_agent',
|
|
201
|
+
description: 'Initiates handoff from Virtual Agent to a live agent when automated assistance is insufficient.',
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
conversation_id: { type: 'string', description: 'Conversation to handoff' },
|
|
206
|
+
queue: { type: 'string', description: 'Agent queue for routing' },
|
|
207
|
+
priority: { type: 'number', description: 'Queue priority' },
|
|
208
|
+
reason: { type: 'string', description: 'Handoff reason' },
|
|
209
|
+
context: { type: 'object', description: 'Context to pass to agent' }
|
|
210
|
+
},
|
|
211
|
+
required: ['conversation_id']
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'snow_discover_va_topics',
|
|
216
|
+
description: 'Discovers available Virtual Agent topics and their configurations.',
|
|
217
|
+
inputSchema: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
221
|
+
active_only: { type: 'boolean', description: 'Show only active topics', default: true }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
// Performance Analytics Tools
|
|
226
|
+
{
|
|
227
|
+
name: 'snow_create_pa_indicator',
|
|
228
|
+
description: 'Creates a Performance Analytics indicator (KPI). Indicators track metrics over time with automated data collection.',
|
|
229
|
+
inputSchema: {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
name: { type: 'string', description: 'Indicator name' },
|
|
233
|
+
table: { type: 'string', description: 'Table to measure' },
|
|
234
|
+
aggregate: { type: 'string', description: 'Aggregation: count, sum, avg, min, max' },
|
|
235
|
+
field: { type: 'string', description: 'Field to aggregate (for sum/avg)' },
|
|
236
|
+
condition: { type: 'string', description: 'Filter condition' },
|
|
237
|
+
frequency: { type: 'string', description: 'Collection frequency: daily, weekly, monthly' },
|
|
238
|
+
unit: { type: 'string', description: 'Unit of measure' },
|
|
239
|
+
direction: { type: 'string', description: 'Desired direction: increase, decrease, maintain' },
|
|
240
|
+
target: { type: 'number', description: 'Target value' },
|
|
241
|
+
thresholds: { type: 'object', description: 'Warning and critical thresholds' }
|
|
242
|
+
},
|
|
243
|
+
required: ['name', 'table', 'aggregate']
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'snow_create_pa_widget',
|
|
248
|
+
description: 'Creates a Performance Analytics dashboard widget for visualizing indicators.',
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: 'object',
|
|
251
|
+
properties: {
|
|
252
|
+
name: { type: 'string', description: 'Widget name' },
|
|
253
|
+
type: { type: 'string', description: 'Widget type: time_series, scorecard, dial, column, pie' },
|
|
254
|
+
indicator: { type: 'string', description: 'Indicator sys_id to display' },
|
|
255
|
+
breakdown: { type: 'string', description: 'Breakdown field for grouping' },
|
|
256
|
+
time_range: { type: 'string', description: 'Time range: 7days, 30days, 90days, 1year' },
|
|
257
|
+
dashboard: { type: 'string', description: 'Dashboard to add widget to' },
|
|
258
|
+
size_x: { type: 'number', description: 'Widget width', default: 4 },
|
|
259
|
+
size_y: { type: 'number', description: 'Widget height', default: 3 }
|
|
260
|
+
},
|
|
261
|
+
required: ['name', 'type', 'indicator']
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'snow_create_pa_breakdown',
|
|
266
|
+
description: 'Creates a breakdown source for Performance Analytics to segment data by dimensions.',
|
|
267
|
+
inputSchema: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
properties: {
|
|
270
|
+
name: { type: 'string', description: 'Breakdown name' },
|
|
271
|
+
table: { type: 'string', description: 'Table to breakdown' },
|
|
272
|
+
field: { type: 'string', description: 'Field to group by' },
|
|
273
|
+
related_field: { type: 'string', description: 'Related field path (for reference fields)' },
|
|
274
|
+
matrix_source: { type: 'boolean', description: 'Is matrix breakdown', default: false }
|
|
275
|
+
},
|
|
276
|
+
required: ['name', 'table', 'field']
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: 'snow_get_pa_scores',
|
|
281
|
+
description: 'Retrieves Performance Analytics scores and trends for indicators over specified time periods.',
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: 'object',
|
|
284
|
+
properties: {
|
|
285
|
+
indicator: { type: 'string', description: 'Indicator sys_id or name' },
|
|
286
|
+
time_range: { type: 'string', description: 'Time range for data' },
|
|
287
|
+
breakdown: { type: 'string', description: 'Breakdown to apply' },
|
|
288
|
+
include_forecast: { type: 'boolean', description: 'Include forecast data', default: false },
|
|
289
|
+
include_targets: { type: 'boolean', description: 'Include target lines', default: true }
|
|
290
|
+
},
|
|
291
|
+
required: ['indicator']
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: 'snow_create_pa_threshold',
|
|
296
|
+
description: 'Creates threshold rules for Performance Analytics indicators to trigger alerts.',
|
|
297
|
+
inputSchema: {
|
|
298
|
+
type: 'object',
|
|
299
|
+
properties: {
|
|
300
|
+
indicator: { type: 'string', description: 'Indicator sys_id' },
|
|
301
|
+
type: { type: 'string', description: 'Threshold type: warning, critical' },
|
|
302
|
+
operator: { type: 'string', description: 'Operator: >, <, >=, <=, =' },
|
|
303
|
+
value: { type: 'number', description: 'Threshold value' },
|
|
304
|
+
duration: { type: 'number', description: 'Duration in periods before alert' },
|
|
305
|
+
notification_group: { type: 'string', description: 'Group to notify' }
|
|
306
|
+
},
|
|
307
|
+
required: ['indicator', 'type', 'operator', 'value']
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'snow_collect_pa_data',
|
|
312
|
+
description: 'Manually triggers Performance Analytics data collection for specific indicators.',
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {
|
|
316
|
+
indicator: { type: 'string', description: 'Indicator to collect data for' },
|
|
317
|
+
start_date: { type: 'string', description: 'Collection start date' },
|
|
318
|
+
end_date: { type: 'string', description: 'Collection end date' },
|
|
319
|
+
recalculate: { type: 'boolean', description: 'Recalculate existing data', default: false }
|
|
320
|
+
},
|
|
321
|
+
required: ['indicator']
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'snow_discover_pa_indicators',
|
|
326
|
+
description: 'Discovers available Performance Analytics indicators and their configurations.',
|
|
327
|
+
inputSchema: {
|
|
328
|
+
type: 'object',
|
|
329
|
+
properties: {
|
|
330
|
+
table: { type: 'string', description: 'Filter by table' },
|
|
331
|
+
active_only: { type: 'boolean', description: 'Show only active indicators', default: true }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
}));
|
|
337
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
338
|
+
try {
|
|
339
|
+
const { name, arguments: args } = request.params;
|
|
340
|
+
const authResult = await mcp_auth_middleware_js_1.mcpAuth.ensureAuthenticated();
|
|
341
|
+
if (!authResult.success) {
|
|
342
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, authResult.error || 'Authentication required');
|
|
343
|
+
}
|
|
344
|
+
switch (name) {
|
|
345
|
+
// Change Management
|
|
346
|
+
case 'snow_create_change_request':
|
|
347
|
+
return await this.createChangeRequest(args);
|
|
348
|
+
case 'snow_create_change_task':
|
|
349
|
+
return await this.createChangeTask(args);
|
|
350
|
+
case 'snow_get_change_request':
|
|
351
|
+
return await this.getChangeRequest(args);
|
|
352
|
+
case 'snow_update_change_state':
|
|
353
|
+
return await this.updateChangeState(args);
|
|
354
|
+
case 'snow_schedule_cab_meeting':
|
|
355
|
+
return await this.scheduleCABMeeting(args);
|
|
356
|
+
case 'snow_search_change_requests':
|
|
357
|
+
return await this.searchChangeRequests(args);
|
|
358
|
+
// Virtual Agent
|
|
359
|
+
case 'snow_create_va_topic':
|
|
360
|
+
return await this.createVATopic(args);
|
|
361
|
+
case 'snow_create_va_topic_block':
|
|
362
|
+
return await this.createVATopicBlock(args);
|
|
363
|
+
case 'snow_get_va_conversation':
|
|
364
|
+
return await this.getVAConversation(args);
|
|
365
|
+
case 'snow_send_va_message':
|
|
366
|
+
return await this.sendVAMessage(args);
|
|
367
|
+
case 'snow_handoff_to_agent':
|
|
368
|
+
return await this.handoffToAgent(args);
|
|
369
|
+
case 'snow_discover_va_topics':
|
|
370
|
+
return await this.discoverVATopics(args);
|
|
371
|
+
// Performance Analytics
|
|
372
|
+
case 'snow_create_pa_indicator':
|
|
373
|
+
return await this.createPAIndicator(args);
|
|
374
|
+
case 'snow_create_pa_widget':
|
|
375
|
+
return await this.createPAWidget(args);
|
|
376
|
+
case 'snow_create_pa_breakdown':
|
|
377
|
+
return await this.createPABreakdown(args);
|
|
378
|
+
case 'snow_get_pa_scores':
|
|
379
|
+
return await this.getPAScores(args);
|
|
380
|
+
case 'snow_create_pa_threshold':
|
|
381
|
+
return await this.createPAThreshold(args);
|
|
382
|
+
case 'snow_collect_pa_data':
|
|
383
|
+
return await this.collectPAData(args);
|
|
384
|
+
case 'snow_discover_pa_indicators':
|
|
385
|
+
return await this.discoverPAIndicators(args);
|
|
386
|
+
default:
|
|
387
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
this.logger.error(`Error in ${request.params.name}:`, error);
|
|
392
|
+
throw error;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
// Change Management Implementation
|
|
397
|
+
async createChangeRequest(args) {
|
|
398
|
+
try {
|
|
399
|
+
this.logger.info('Creating change request...');
|
|
400
|
+
const changeData = {
|
|
401
|
+
short_description: args.short_description,
|
|
402
|
+
description: args.description || '',
|
|
403
|
+
type: args.type || 'normal',
|
|
404
|
+
category: args.category || 'Other',
|
|
405
|
+
priority: args.priority || 3,
|
|
406
|
+
risk: args.risk || 3,
|
|
407
|
+
impact: args.impact || 3,
|
|
408
|
+
assignment_group: args.assignment_group || '',
|
|
409
|
+
assigned_to: args.assigned_to || '',
|
|
410
|
+
start_date: args.start_date || '',
|
|
411
|
+
end_date: args.end_date || '',
|
|
412
|
+
justification: args.justification || '',
|
|
413
|
+
implementation_plan: args.implementation_plan || '',
|
|
414
|
+
backout_plan: args.backout_plan || '',
|
|
415
|
+
test_plan: args.test_plan || '',
|
|
416
|
+
state: '-5' // New state
|
|
417
|
+
};
|
|
418
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
419
|
+
const response = await this.client.createRecord('change_request', changeData);
|
|
420
|
+
if (!response.success) {
|
|
421
|
+
throw new Error(`Failed to create change request: ${response.error}`);
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
content: [{
|
|
425
|
+
type: 'text',
|
|
426
|
+
text: `✅ Change Request created successfully!
|
|
427
|
+
|
|
428
|
+
📋 **${args.short_description}**
|
|
429
|
+
🆔 Number: ${response.data.number}
|
|
430
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
431
|
+
📊 Type: ${args.type || 'normal'}
|
|
432
|
+
⚠️ Risk: ${args.risk || 3}
|
|
433
|
+
💥 Impact: ${args.impact || 3}
|
|
434
|
+
📅 Start: ${args.start_date || 'Not scheduled'}
|
|
435
|
+
📅 End: ${args.end_date || 'Not scheduled'}
|
|
436
|
+
|
|
437
|
+
✨ Change request created and ready for assessment!`
|
|
438
|
+
}]
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
this.logger.error('Failed to create change request:', error);
|
|
443
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create change request: ${error}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async createChangeTask(args) {
|
|
447
|
+
try {
|
|
448
|
+
this.logger.info('Creating change task...');
|
|
449
|
+
// Find parent change request
|
|
450
|
+
let changeId = args.change_request;
|
|
451
|
+
if (!changeId.match(/^[a-f0-9]{32}$/)) {
|
|
452
|
+
const changeResponse = await this.client.searchRecords('change_request', `number=${changeId}`, 1);
|
|
453
|
+
if (changeResponse.success && changeResponse.data.result.length) {
|
|
454
|
+
changeId = changeResponse.data.result[0].sys_id;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const taskData = {
|
|
458
|
+
change_request: changeId,
|
|
459
|
+
short_description: args.short_description,
|
|
460
|
+
description: args.description || '',
|
|
461
|
+
assignment_group: args.assignment_group || '',
|
|
462
|
+
assigned_to: args.assigned_to || '',
|
|
463
|
+
planned_start_date: args.planned_start_date || '',
|
|
464
|
+
planned_end_date: args.planned_end_date || '',
|
|
465
|
+
order: args.order || 100
|
|
466
|
+
};
|
|
467
|
+
const response = await this.client.createRecord('change_task', taskData);
|
|
468
|
+
if (!response.success) {
|
|
469
|
+
throw new Error(`Failed to create change task: ${response.error}`);
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
content: [{
|
|
473
|
+
type: 'text',
|
|
474
|
+
text: `✅ Change Task created successfully!
|
|
475
|
+
|
|
476
|
+
📋 **${args.short_description}**
|
|
477
|
+
🆔 Number: ${response.data.number}
|
|
478
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
479
|
+
🔗 Parent Change: ${args.change_request}
|
|
480
|
+
🔢 Order: ${args.order || 100}
|
|
481
|
+
|
|
482
|
+
✨ Change task added to change request!`
|
|
483
|
+
}]
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
this.logger.error('Failed to create change task:', error);
|
|
488
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create change task: ${error}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async getChangeRequest(args) {
|
|
492
|
+
try {
|
|
493
|
+
this.logger.info('Getting change request...');
|
|
494
|
+
let changeId = args.sys_id;
|
|
495
|
+
if (!changeId.match(/^[a-f0-9]{32}$/)) {
|
|
496
|
+
const changeResponse = await this.client.searchRecords('change_request', `number=${changeId}`, 1);
|
|
497
|
+
if (changeResponse.success && changeResponse.data.result.length) {
|
|
498
|
+
changeId = changeResponse.data.result[0].sys_id;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const response = await this.client.getRecord('change_request', changeId);
|
|
502
|
+
if (!response.success) {
|
|
503
|
+
throw new Error('Change request not found');
|
|
504
|
+
}
|
|
505
|
+
const change = response.data;
|
|
506
|
+
let details = `📋 **${change.number}: ${change.short_description}**
|
|
507
|
+
🆔 sys_id: ${change.sys_id}
|
|
508
|
+
📊 Type: ${change.type}
|
|
509
|
+
📊 State: ${change.state}
|
|
510
|
+
⚠️ Risk: ${change.risk}
|
|
511
|
+
💥 Impact: ${change.impact}
|
|
512
|
+
📅 Scheduled: ${change.start_date} to ${change.end_date}
|
|
513
|
+
👤 Assigned to: ${change.assigned_to || 'Unassigned'}`;
|
|
514
|
+
// Get tasks if requested
|
|
515
|
+
if (args.include_tasks) {
|
|
516
|
+
const tasksResponse = await this.client.searchRecords('change_task', `change_request=${changeId}`, 50);
|
|
517
|
+
if (tasksResponse.success && tasksResponse.data.result.length) {
|
|
518
|
+
const tasks = tasksResponse.data.result.map((t) => ` - ${t.number}: ${t.short_description} (${t.state})`).join('\n');
|
|
519
|
+
details += `\n\n📋 **Change Tasks:**\n${tasks}`;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Get approvals if requested
|
|
523
|
+
if (args.include_approvals) {
|
|
524
|
+
const approvalsResponse = await this.client.searchRecords('sysapproval_approver', `document_id=${changeId}`, 50);
|
|
525
|
+
if (approvalsResponse.success && approvalsResponse.data.result.length) {
|
|
526
|
+
const approvals = approvalsResponse.data.result.map((a) => ` - ${a.approver}: ${a.state}`).join('\n');
|
|
527
|
+
details += `\n\n✅ **Approvals:**\n${approvals}`;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
content: [{
|
|
532
|
+
type: 'text',
|
|
533
|
+
text: details + '\n\n✨ Change request details retrieved!'
|
|
534
|
+
}]
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
this.logger.error('Failed to get change request:', error);
|
|
539
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get change request: ${error}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
async updateChangeState(args) {
|
|
543
|
+
try {
|
|
544
|
+
this.logger.info('Updating change state...');
|
|
545
|
+
let changeId = args.sys_id;
|
|
546
|
+
if (!changeId.match(/^[a-f0-9]{32}$/)) {
|
|
547
|
+
const changeResponse = await this.client.searchRecords('change_request', `number=${changeId}`, 1);
|
|
548
|
+
if (changeResponse.success && changeResponse.data.result.length) {
|
|
549
|
+
changeId = changeResponse.data.result[0].sys_id;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const stateMap = {
|
|
553
|
+
'draft': '-5',
|
|
554
|
+
'assess': '-4',
|
|
555
|
+
'authorize': '-3',
|
|
556
|
+
'scheduled': '-2',
|
|
557
|
+
'implement': '-1',
|
|
558
|
+
'review': '0',
|
|
559
|
+
'closed': '3',
|
|
560
|
+
'cancelled': '4'
|
|
561
|
+
};
|
|
562
|
+
const updateData = {
|
|
563
|
+
state: stateMap[args.state] || args.state
|
|
564
|
+
};
|
|
565
|
+
if (args.close_notes)
|
|
566
|
+
updateData.close_notes = args.close_notes;
|
|
567
|
+
if (args.close_code)
|
|
568
|
+
updateData.close_code = args.close_code;
|
|
569
|
+
const response = await this.client.updateRecord('change_request', changeId, updateData);
|
|
570
|
+
if (!response.success) {
|
|
571
|
+
throw new Error(`Failed to update change state: ${response.error}`);
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
content: [{
|
|
575
|
+
type: 'text',
|
|
576
|
+
text: `✅ Change Request state updated!
|
|
577
|
+
|
|
578
|
+
🆔 Change: ${args.sys_id}
|
|
579
|
+
📊 New State: ${args.state}
|
|
580
|
+
${args.close_notes ? `📝 Close Notes: ${args.close_notes}` : ''}
|
|
581
|
+
${args.close_code ? `✅ Close Code: ${args.close_code}` : ''}
|
|
582
|
+
|
|
583
|
+
✨ Change state updated successfully!`
|
|
584
|
+
}]
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
this.logger.error('Failed to update change state:', error);
|
|
589
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to update change state: ${error}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async scheduleCABMeeting(args) {
|
|
593
|
+
try {
|
|
594
|
+
this.logger.info('Scheduling CAB meeting...');
|
|
595
|
+
const cabData = {
|
|
596
|
+
change_request: args.change_request,
|
|
597
|
+
meeting_date: args.meeting_date,
|
|
598
|
+
attendees: args.attendees ? args.attendees.join(',') : '',
|
|
599
|
+
agenda: args.agenda || '',
|
|
600
|
+
location: args.location || ''
|
|
601
|
+
};
|
|
602
|
+
// Create CAB meeting record (using task or event table)
|
|
603
|
+
const response = await this.client.createRecord('task', {
|
|
604
|
+
...cabData,
|
|
605
|
+
short_description: `CAB Meeting for ${args.change_request}`,
|
|
606
|
+
sys_class_name: 'task'
|
|
607
|
+
});
|
|
608
|
+
if (!response.success) {
|
|
609
|
+
throw new Error(`Failed to schedule CAB meeting: ${response.error}`);
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
content: [{
|
|
613
|
+
type: 'text',
|
|
614
|
+
text: `✅ CAB Meeting scheduled!
|
|
615
|
+
|
|
616
|
+
📅 **Meeting Details**
|
|
617
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
618
|
+
📋 Change Request: ${args.change_request}
|
|
619
|
+
📅 Date/Time: ${args.meeting_date}
|
|
620
|
+
👥 Attendees: ${args.attendees ? args.attendees.join(', ') : 'TBD'}
|
|
621
|
+
📍 Location: ${args.location || 'TBD'}
|
|
622
|
+
📝 Agenda: ${args.agenda || 'Review change request'}
|
|
623
|
+
|
|
624
|
+
✨ CAB meeting scheduled successfully!`
|
|
625
|
+
}]
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
this.logger.error('Failed to schedule CAB meeting:', error);
|
|
630
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to schedule CAB meeting: ${error}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async searchChangeRequests(args) {
|
|
634
|
+
try {
|
|
635
|
+
this.logger.info('Searching change requests...');
|
|
636
|
+
let query = '';
|
|
637
|
+
if (args.query) {
|
|
638
|
+
query = `short_descriptionLIKE${args.query}^ORdescriptionLIKE${args.query}`;
|
|
639
|
+
}
|
|
640
|
+
if (args.state) {
|
|
641
|
+
query += query ? '^' : '';
|
|
642
|
+
query += `state=${args.state}`;
|
|
643
|
+
}
|
|
644
|
+
if (args.type) {
|
|
645
|
+
query += query ? '^' : '';
|
|
646
|
+
query += `type=${args.type}`;
|
|
647
|
+
}
|
|
648
|
+
if (args.risk) {
|
|
649
|
+
query += query ? '^' : '';
|
|
650
|
+
query += `risk=${args.risk}`;
|
|
651
|
+
}
|
|
652
|
+
if (args.assigned_to) {
|
|
653
|
+
query += query ? '^' : '';
|
|
654
|
+
query += `assigned_to=${args.assigned_to}`;
|
|
655
|
+
}
|
|
656
|
+
const limit = args.limit || 20;
|
|
657
|
+
const response = await this.client.searchRecords('change_request', query, limit);
|
|
658
|
+
if (!response.success) {
|
|
659
|
+
throw new Error('Failed to search change requests');
|
|
660
|
+
}
|
|
661
|
+
const changes = response.data.result;
|
|
662
|
+
if (!changes.length) {
|
|
663
|
+
return {
|
|
664
|
+
content: [{
|
|
665
|
+
type: 'text',
|
|
666
|
+
text: '❌ No change requests found'
|
|
667
|
+
}]
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
const changeList = changes.map((change) => `📋 **${change.number}: ${change.short_description}**
|
|
671
|
+
📊 Type: ${change.type} | State: ${change.state}
|
|
672
|
+
⚠️ Risk: ${change.risk} | Impact: ${change.impact}
|
|
673
|
+
📅 Scheduled: ${change.start_date || 'Not scheduled'}`).join('\n\n');
|
|
674
|
+
return {
|
|
675
|
+
content: [{
|
|
676
|
+
type: 'text',
|
|
677
|
+
text: `🔍 Change Request Search Results:
|
|
678
|
+
|
|
679
|
+
${changeList}
|
|
680
|
+
|
|
681
|
+
✨ Found ${changes.length} change request(s)`
|
|
682
|
+
}]
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
this.logger.error('Failed to search change requests:', error);
|
|
687
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search change requests: ${error}`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// Virtual Agent Implementation
|
|
691
|
+
async createVATopic(args) {
|
|
692
|
+
try {
|
|
693
|
+
this.logger.info('Creating Virtual Agent topic...');
|
|
694
|
+
const topicData = {
|
|
695
|
+
name: args.name,
|
|
696
|
+
description: args.description || '',
|
|
697
|
+
utterances: args.utterances.join('\n'),
|
|
698
|
+
category: args.category || '',
|
|
699
|
+
active: args.active !== false,
|
|
700
|
+
live_agent_enabled: args.live_agent_enabled || false
|
|
701
|
+
};
|
|
702
|
+
const response = await this.client.createRecord('sys_cs_topic', topicData);
|
|
703
|
+
if (!response.success) {
|
|
704
|
+
throw new Error(`Failed to create VA topic: ${response.error}`);
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
content: [{
|
|
708
|
+
type: 'text',
|
|
709
|
+
text: `✅ Virtual Agent Topic created!
|
|
710
|
+
|
|
711
|
+
🤖 **${args.name}**
|
|
712
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
713
|
+
📝 Training Phrases: ${args.utterances.length}
|
|
714
|
+
📂 Category: ${args.category || 'General'}
|
|
715
|
+
🔄 Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
716
|
+
👤 Live Agent: ${args.live_agent_enabled ? 'Enabled' : 'Disabled'}
|
|
717
|
+
|
|
718
|
+
✨ Topic ready for conversation design!`
|
|
719
|
+
}]
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
this.logger.error('Failed to create VA topic:', error);
|
|
724
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create VA topic: ${error}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async createVATopicBlock(args) {
|
|
728
|
+
try {
|
|
729
|
+
this.logger.info('Creating VA topic block...');
|
|
730
|
+
const blockData = {
|
|
731
|
+
topic: args.topic,
|
|
732
|
+
name: args.name,
|
|
733
|
+
type: args.type,
|
|
734
|
+
order: args.order,
|
|
735
|
+
text: args.text || '',
|
|
736
|
+
script: args.script || '',
|
|
737
|
+
variable: args.variable || '',
|
|
738
|
+
next_block: args.next_block || ''
|
|
739
|
+
};
|
|
740
|
+
const response = await this.client.createRecord('sys_cs_topic_block', blockData);
|
|
741
|
+
if (!response.success) {
|
|
742
|
+
throw new Error(`Failed to create VA topic block: ${response.error}`);
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
content: [{
|
|
746
|
+
type: 'text',
|
|
747
|
+
text: `✅ Topic Block created!
|
|
748
|
+
|
|
749
|
+
🧩 **${args.name}**
|
|
750
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
751
|
+
📊 Type: ${args.type}
|
|
752
|
+
🔢 Order: ${args.order}
|
|
753
|
+
${args.text ? `💬 Text: ${args.text}` : ''}
|
|
754
|
+
${args.variable ? `📝 Variable: ${args.variable}` : ''}
|
|
755
|
+
|
|
756
|
+
✨ Topic block added to conversation flow!`
|
|
757
|
+
}]
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
this.logger.error('Failed to create VA topic block:', error);
|
|
762
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create VA topic block: ${error}`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async getVAConversation(args) {
|
|
766
|
+
try {
|
|
767
|
+
this.logger.info('Getting VA conversation...');
|
|
768
|
+
let query = '';
|
|
769
|
+
if (args.conversation_id) {
|
|
770
|
+
query = `sys_id=${args.conversation_id}`;
|
|
771
|
+
}
|
|
772
|
+
else if (args.user_id) {
|
|
773
|
+
query = `user=${args.user_id}`;
|
|
774
|
+
}
|
|
775
|
+
const response = await this.client.searchRecords('sys_cs_conversation', query, 1);
|
|
776
|
+
if (!response.success || !response.data.result.length) {
|
|
777
|
+
throw new Error('Conversation not found');
|
|
778
|
+
}
|
|
779
|
+
const conversation = response.data.result[0];
|
|
780
|
+
let details = `💬 **Conversation ${conversation.sys_id}**
|
|
781
|
+
👤 User: ${conversation.user}
|
|
782
|
+
📅 Started: ${conversation.sys_created_on}
|
|
783
|
+
📊 Status: ${conversation.status}`;
|
|
784
|
+
if (args.include_transcript) {
|
|
785
|
+
const messagesResponse = await this.client.searchRecords('sys_cs_message', `conversation=${conversation.sys_id}`, args.limit || 50);
|
|
786
|
+
if (messagesResponse.success && messagesResponse.data.result.length) {
|
|
787
|
+
const transcript = messagesResponse.data.result.map((m) => `${m.author === 'user' ? '👤' : '🤖'} ${m.text}`).join('\n');
|
|
788
|
+
details += `\n\n📝 **Transcript:**\n${transcript}`;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
content: [{
|
|
793
|
+
type: 'text',
|
|
794
|
+
text: details + '\n\n✨ Conversation retrieved!'
|
|
795
|
+
}]
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
catch (error) {
|
|
799
|
+
this.logger.error('Failed to get VA conversation:', error);
|
|
800
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get VA conversation: ${error}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
async sendVAMessage(args) {
|
|
804
|
+
try {
|
|
805
|
+
this.logger.info('Sending message to Virtual Agent...');
|
|
806
|
+
// Create or get conversation
|
|
807
|
+
let conversationId = args.conversation_id;
|
|
808
|
+
if (!conversationId) {
|
|
809
|
+
const convData = {
|
|
810
|
+
user: args.user_id || 'guest',
|
|
811
|
+
status: 'active'
|
|
812
|
+
};
|
|
813
|
+
const convResponse = await this.client.createRecord('sys_cs_conversation', convData);
|
|
814
|
+
if (convResponse.success) {
|
|
815
|
+
conversationId = convResponse.data.sys_id;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
// Create user message
|
|
819
|
+
const messageData = {
|
|
820
|
+
conversation: conversationId,
|
|
821
|
+
text: args.message,
|
|
822
|
+
author: 'user',
|
|
823
|
+
context: args.context ? JSON.stringify(args.context) : ''
|
|
824
|
+
};
|
|
825
|
+
const messageResponse = await this.client.createRecord('sys_cs_message', messageData);
|
|
826
|
+
if (!messageResponse.success) {
|
|
827
|
+
throw new Error(`Failed to send message: ${messageResponse.error}`);
|
|
828
|
+
}
|
|
829
|
+
// Simulate VA response (in real implementation, this would trigger VA processing)
|
|
830
|
+
const vaResponse = {
|
|
831
|
+
text: `I understand you said: "${args.message}". How can I help you with that?`,
|
|
832
|
+
suggested_actions: ['Get more info', 'Create ticket', 'Talk to agent']
|
|
833
|
+
};
|
|
834
|
+
return {
|
|
835
|
+
content: [{
|
|
836
|
+
type: 'text',
|
|
837
|
+
text: `💬 Message sent to Virtual Agent!
|
|
838
|
+
|
|
839
|
+
👤 **Your message:** ${args.message}
|
|
840
|
+
🤖 **VA Response:** ${vaResponse.text}
|
|
841
|
+
|
|
842
|
+
🔘 **Suggested Actions:**
|
|
843
|
+
${vaResponse.suggested_actions.map(a => ` - ${a}`).join('\n')}
|
|
844
|
+
|
|
845
|
+
🆔 Conversation ID: ${conversationId}
|
|
846
|
+
|
|
847
|
+
✨ Conversation continues...`
|
|
848
|
+
}]
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
this.logger.error('Failed to send VA message:', error);
|
|
853
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to send VA message: ${error}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async handoffToAgent(args) {
|
|
857
|
+
try {
|
|
858
|
+
this.logger.info('Initiating handoff to live agent...');
|
|
859
|
+
const handoffData = {
|
|
860
|
+
conversation_id: args.conversation_id,
|
|
861
|
+
queue: args.queue || 'general',
|
|
862
|
+
priority: args.priority || 3,
|
|
863
|
+
reason: args.reason || 'User requested live agent',
|
|
864
|
+
context: args.context ? JSON.stringify(args.context) : '',
|
|
865
|
+
status: 'pending'
|
|
866
|
+
};
|
|
867
|
+
// Create handoff request
|
|
868
|
+
const response = await this.client.createRecord('sys_cs_handoff', handoffData);
|
|
869
|
+
if (!response.success) {
|
|
870
|
+
// Fallback to updating conversation status
|
|
871
|
+
await this.client.updateRecord('sys_cs_conversation', args.conversation_id, {
|
|
872
|
+
status: 'handoff_requested'
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
content: [{
|
|
877
|
+
type: 'text',
|
|
878
|
+
text: `✅ Handoff to live agent initiated!
|
|
879
|
+
|
|
880
|
+
💬 **Conversation:** ${args.conversation_id}
|
|
881
|
+
👥 **Queue:** ${args.queue || 'general'}
|
|
882
|
+
⚠️ **Priority:** ${args.priority || 3}
|
|
883
|
+
📝 **Reason:** ${args.reason || 'User requested live agent'}
|
|
884
|
+
|
|
885
|
+
✨ Live agent will join the conversation shortly!`
|
|
886
|
+
}]
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
this.logger.error('Failed to handoff to agent:', error);
|
|
891
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to handoff to agent: ${error}`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
async discoverVATopics(args) {
|
|
895
|
+
try {
|
|
896
|
+
this.logger.info('Discovering VA topics...');
|
|
897
|
+
let query = '';
|
|
898
|
+
if (args.category) {
|
|
899
|
+
query = `category=${args.category}`;
|
|
900
|
+
}
|
|
901
|
+
if (args.active_only) {
|
|
902
|
+
query += query ? '^' : '';
|
|
903
|
+
query += 'active=true';
|
|
904
|
+
}
|
|
905
|
+
const response = await this.client.searchRecords('sys_cs_topic', query, 50);
|
|
906
|
+
if (!response.success) {
|
|
907
|
+
throw new Error('Failed to discover VA topics');
|
|
908
|
+
}
|
|
909
|
+
const topics = response.data.result;
|
|
910
|
+
if (!topics.length) {
|
|
911
|
+
return {
|
|
912
|
+
content: [{
|
|
913
|
+
type: 'text',
|
|
914
|
+
text: '❌ No Virtual Agent topics found'
|
|
915
|
+
}]
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
const topicList = topics.map((topic) => `🤖 **${topic.name}** ${topic.active ? '✅' : '❌'}
|
|
919
|
+
📝 ${topic.description || 'No description'}
|
|
920
|
+
📂 Category: ${topic.category || 'General'}
|
|
921
|
+
👤 Live Agent: ${topic.live_agent_enabled ? 'Yes' : 'No'}`).join('\n\n');
|
|
922
|
+
return {
|
|
923
|
+
content: [{
|
|
924
|
+
type: 'text',
|
|
925
|
+
text: `🔍 Virtual Agent Topics:
|
|
926
|
+
|
|
927
|
+
${topicList}
|
|
928
|
+
|
|
929
|
+
✨ Found ${topics.length} topic(s)`
|
|
930
|
+
}]
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
this.logger.error('Failed to discover VA topics:', error);
|
|
935
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover VA topics: ${error}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
// Performance Analytics Implementation
|
|
939
|
+
async createPAIndicator(args) {
|
|
940
|
+
try {
|
|
941
|
+
this.logger.info('Creating PA indicator...');
|
|
942
|
+
const indicatorData = {
|
|
943
|
+
name: args.name,
|
|
944
|
+
table: args.table,
|
|
945
|
+
aggregate: args.aggregate,
|
|
946
|
+
field: args.field || '',
|
|
947
|
+
condition: args.condition || '',
|
|
948
|
+
frequency: args.frequency || 'daily',
|
|
949
|
+
unit: args.unit || '',
|
|
950
|
+
direction: args.direction || 'maintain',
|
|
951
|
+
target: args.target || 0,
|
|
952
|
+
thresholds: args.thresholds ? JSON.stringify(args.thresholds) : ''
|
|
953
|
+
};
|
|
954
|
+
const response = await this.client.createRecord('pa_indicators', indicatorData);
|
|
955
|
+
if (!response.success) {
|
|
956
|
+
throw new Error(`Failed to create PA indicator: ${response.error}`);
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
content: [{
|
|
960
|
+
type: 'text',
|
|
961
|
+
text: `✅ Performance Analytics Indicator created!
|
|
962
|
+
|
|
963
|
+
📊 **${args.name}**
|
|
964
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
965
|
+
📋 Table: ${args.table}
|
|
966
|
+
📈 Aggregation: ${args.aggregate}
|
|
967
|
+
${args.field ? `📝 Field: ${args.field}` : ''}
|
|
968
|
+
⏰ Frequency: ${args.frequency || 'daily'}
|
|
969
|
+
🎯 Target: ${args.target || 'Not set'}
|
|
970
|
+
${args.unit ? `📏 Unit: ${args.unit}` : ''}
|
|
971
|
+
|
|
972
|
+
✨ Indicator ready for data collection!`
|
|
973
|
+
}]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
catch (error) {
|
|
977
|
+
this.logger.error('Failed to create PA indicator:', error);
|
|
978
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create PA indicator: ${error}`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
async createPAWidget(args) {
|
|
982
|
+
try {
|
|
983
|
+
this.logger.info('Creating PA widget...');
|
|
984
|
+
const widgetData = {
|
|
985
|
+
name: args.name,
|
|
986
|
+
type: args.type,
|
|
987
|
+
indicator: args.indicator,
|
|
988
|
+
breakdown: args.breakdown || '',
|
|
989
|
+
time_range: args.time_range || '30days',
|
|
990
|
+
dashboard: args.dashboard || '',
|
|
991
|
+
size_x: args.size_x || 4,
|
|
992
|
+
size_y: args.size_y || 3
|
|
993
|
+
};
|
|
994
|
+
const response = await this.client.createRecord('pa_widgets', widgetData);
|
|
995
|
+
if (!response.success) {
|
|
996
|
+
throw new Error(`Failed to create PA widget: ${response.error}`);
|
|
997
|
+
}
|
|
998
|
+
return {
|
|
999
|
+
content: [{
|
|
1000
|
+
type: 'text',
|
|
1001
|
+
text: `✅ PA Widget created!
|
|
1002
|
+
|
|
1003
|
+
📊 **${args.name}**
|
|
1004
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
1005
|
+
📈 Type: ${args.type}
|
|
1006
|
+
⏰ Time Range: ${args.time_range || '30days'}
|
|
1007
|
+
📐 Size: ${args.size_x || 4}x${args.size_y || 3}
|
|
1008
|
+
${args.breakdown ? `📊 Breakdown: ${args.breakdown}` : ''}
|
|
1009
|
+
|
|
1010
|
+
✨ Widget added to dashboard!`
|
|
1011
|
+
}]
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
catch (error) {
|
|
1015
|
+
this.logger.error('Failed to create PA widget:', error);
|
|
1016
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create PA widget: ${error}`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
async createPABreakdown(args) {
|
|
1020
|
+
try {
|
|
1021
|
+
this.logger.info('Creating PA breakdown...');
|
|
1022
|
+
const breakdownData = {
|
|
1023
|
+
name: args.name,
|
|
1024
|
+
table: args.table,
|
|
1025
|
+
field: args.field,
|
|
1026
|
+
related_field: args.related_field || '',
|
|
1027
|
+
matrix_source: args.matrix_source || false
|
|
1028
|
+
};
|
|
1029
|
+
const response = await this.client.createRecord('pa_breakdowns', breakdownData);
|
|
1030
|
+
if (!response.success) {
|
|
1031
|
+
throw new Error(`Failed to create PA breakdown: ${response.error}`);
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
content: [{
|
|
1035
|
+
type: 'text',
|
|
1036
|
+
text: `✅ PA Breakdown created!
|
|
1037
|
+
|
|
1038
|
+
📊 **${args.name}**
|
|
1039
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
1040
|
+
📋 Table: ${args.table}
|
|
1041
|
+
📝 Field: ${args.field}
|
|
1042
|
+
${args.related_field ? `🔗 Related Field: ${args.related_field}` : ''}
|
|
1043
|
+
📈 Matrix: ${args.matrix_source ? 'Yes' : 'No'}
|
|
1044
|
+
|
|
1045
|
+
✨ Breakdown ready for use in indicators!`
|
|
1046
|
+
}]
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
this.logger.error('Failed to create PA breakdown:', error);
|
|
1051
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create PA breakdown: ${error}`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
async getPAScores(args) {
|
|
1055
|
+
try {
|
|
1056
|
+
this.logger.info('Getting PA scores...');
|
|
1057
|
+
// Find indicator
|
|
1058
|
+
let indicatorId = args.indicator;
|
|
1059
|
+
if (!indicatorId.match(/^[a-f0-9]{32}$/)) {
|
|
1060
|
+
const indResponse = await this.client.searchRecords('pa_indicators', `name=${indicatorId}`, 1);
|
|
1061
|
+
if (indResponse.success && indResponse.data.result.length) {
|
|
1062
|
+
indicatorId = indResponse.data.result[0].sys_id;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
// Get scores
|
|
1066
|
+
const scoresResponse = await this.client.searchRecords('pa_scores', `indicator=${indicatorId}`, 100);
|
|
1067
|
+
if (!scoresResponse.success) {
|
|
1068
|
+
throw new Error('Failed to get PA scores');
|
|
1069
|
+
}
|
|
1070
|
+
const scores = scoresResponse.data.result;
|
|
1071
|
+
if (!scores.length) {
|
|
1072
|
+
return {
|
|
1073
|
+
content: [{
|
|
1074
|
+
type: 'text',
|
|
1075
|
+
text: '❌ No scores found for this indicator'
|
|
1076
|
+
}]
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
const scoreList = scores.slice(0, 10).map((score) => `📅 ${score.date}: ${score.value} ${score.unit || ''}`).join('\n');
|
|
1080
|
+
return {
|
|
1081
|
+
content: [{
|
|
1082
|
+
type: 'text',
|
|
1083
|
+
text: `📊 Performance Analytics Scores:
|
|
1084
|
+
|
|
1085
|
+
${scoreList}
|
|
1086
|
+
|
|
1087
|
+
✨ Showing ${Math.min(10, scores.length)} of ${scores.length} scores`
|
|
1088
|
+
}]
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
catch (error) {
|
|
1092
|
+
this.logger.error('Failed to get PA scores:', error);
|
|
1093
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get PA scores: ${error}`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
async createPAThreshold(args) {
|
|
1097
|
+
try {
|
|
1098
|
+
this.logger.info('Creating PA threshold...');
|
|
1099
|
+
const thresholdData = {
|
|
1100
|
+
indicator: args.indicator,
|
|
1101
|
+
type: args.type,
|
|
1102
|
+
operator: args.operator,
|
|
1103
|
+
value: args.value,
|
|
1104
|
+
duration: args.duration || 1,
|
|
1105
|
+
notification_group: args.notification_group || ''
|
|
1106
|
+
};
|
|
1107
|
+
const response = await this.client.createRecord('pa_thresholds', thresholdData);
|
|
1108
|
+
if (!response.success) {
|
|
1109
|
+
throw new Error(`Failed to create PA threshold: ${response.error}`);
|
|
1110
|
+
}
|
|
1111
|
+
return {
|
|
1112
|
+
content: [{
|
|
1113
|
+
type: 'text',
|
|
1114
|
+
text: `✅ PA Threshold created!
|
|
1115
|
+
|
|
1116
|
+
⚠️ **Threshold Configuration**
|
|
1117
|
+
🆔 sys_id: ${response.data.sys_id}
|
|
1118
|
+
📊 Type: ${args.type}
|
|
1119
|
+
🔢 Rule: ${args.operator} ${args.value}
|
|
1120
|
+
⏱️ Duration: ${args.duration || 1} period(s)
|
|
1121
|
+
${args.notification_group ? `📧 Notify: ${args.notification_group}` : ''}
|
|
1122
|
+
|
|
1123
|
+
✨ Threshold monitoring active!`
|
|
1124
|
+
}]
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
catch (error) {
|
|
1128
|
+
this.logger.error('Failed to create PA threshold:', error);
|
|
1129
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create PA threshold: ${error}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async collectPAData(args) {
|
|
1133
|
+
try {
|
|
1134
|
+
this.logger.info('Collecting PA data...');
|
|
1135
|
+
// Create data collection job
|
|
1136
|
+
const jobData = {
|
|
1137
|
+
indicator: args.indicator,
|
|
1138
|
+
start_date: args.start_date || '',
|
|
1139
|
+
end_date: args.end_date || '',
|
|
1140
|
+
recalculate: args.recalculate || false,
|
|
1141
|
+
status: 'pending'
|
|
1142
|
+
};
|
|
1143
|
+
const response = await this.client.createRecord('pa_collection_jobs', jobData);
|
|
1144
|
+
if (!response.success) {
|
|
1145
|
+
// Fallback message if table doesn't exist
|
|
1146
|
+
return {
|
|
1147
|
+
content: [{
|
|
1148
|
+
type: 'text',
|
|
1149
|
+
text: `⚠️ PA Data Collection initiated!
|
|
1150
|
+
|
|
1151
|
+
📊 Indicator: ${args.indicator}
|
|
1152
|
+
${args.start_date ? `📅 Start: ${args.start_date}` : ''}
|
|
1153
|
+
${args.end_date ? `📅 End: ${args.end_date}` : ''}
|
|
1154
|
+
🔄 Recalculate: ${args.recalculate ? 'Yes' : 'No'}
|
|
1155
|
+
|
|
1156
|
+
✨ Data collection job queued. Check PA dashboard for results.`
|
|
1157
|
+
}]
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
return {
|
|
1161
|
+
content: [{
|
|
1162
|
+
type: 'text',
|
|
1163
|
+
text: `✅ PA Data Collection started!
|
|
1164
|
+
|
|
1165
|
+
🆔 Job ID: ${response.data.sys_id}
|
|
1166
|
+
📊 Indicator: ${args.indicator}
|
|
1167
|
+
📅 Range: ${args.start_date || 'Default'} to ${args.end_date || 'Current'}
|
|
1168
|
+
🔄 Recalculate: ${args.recalculate ? 'Yes' : 'No'}
|
|
1169
|
+
|
|
1170
|
+
✨ Collection job running in background!`
|
|
1171
|
+
}]
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
catch (error) {
|
|
1175
|
+
this.logger.error('Failed to collect PA data:', error);
|
|
1176
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to collect PA data: ${error}`);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
async discoverPAIndicators(args) {
|
|
1180
|
+
try {
|
|
1181
|
+
this.logger.info('Discovering PA indicators...');
|
|
1182
|
+
let query = '';
|
|
1183
|
+
if (args.table) {
|
|
1184
|
+
query = `table=${args.table}`;
|
|
1185
|
+
}
|
|
1186
|
+
if (args.active_only) {
|
|
1187
|
+
query += query ? '^' : '';
|
|
1188
|
+
query += 'active=true';
|
|
1189
|
+
}
|
|
1190
|
+
const response = await this.client.searchRecords('pa_indicators', query, 50);
|
|
1191
|
+
if (!response.success) {
|
|
1192
|
+
throw new Error('Failed to discover PA indicators');
|
|
1193
|
+
}
|
|
1194
|
+
const indicators = response.data.result;
|
|
1195
|
+
if (!indicators.length) {
|
|
1196
|
+
return {
|
|
1197
|
+
content: [{
|
|
1198
|
+
type: 'text',
|
|
1199
|
+
text: '❌ No PA indicators found'
|
|
1200
|
+
}]
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
const indicatorList = indicators.map((ind) => `📊 **${ind.name}** ${ind.active ? '✅' : '❌'}
|
|
1204
|
+
📋 Table: ${ind.table}
|
|
1205
|
+
📈 Type: ${ind.aggregate}${ind.field ? ` (${ind.field})` : ''}
|
|
1206
|
+
⏰ Frequency: ${ind.frequency}
|
|
1207
|
+
🎯 Target: ${ind.target || 'Not set'}`).join('\n\n');
|
|
1208
|
+
return {
|
|
1209
|
+
content: [{
|
|
1210
|
+
type: 'text',
|
|
1211
|
+
text: `🔍 Performance Analytics Indicators:
|
|
1212
|
+
|
|
1213
|
+
${indicatorList}
|
|
1214
|
+
|
|
1215
|
+
✨ Found ${indicators.length} indicator(s)`
|
|
1216
|
+
}]
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
catch (error) {
|
|
1220
|
+
this.logger.error('Failed to discover PA indicators:', error);
|
|
1221
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover PA indicators: ${error}`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
async run() {
|
|
1225
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1226
|
+
await this.server.connect(transport);
|
|
1227
|
+
this.logger.info('ServiceNow Change/VA/PA MCP Server running on stdio');
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
const server = new ServiceNowChangeVirtualAgentPAMCP();
|
|
1231
|
+
server.run().catch(console.error);
|
|
1232
|
+
//# sourceMappingURL=servicenow-change-virtualagent-pa-mcp.js.map
|