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,1192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ServiceNow Flow Designer, Agent Workspace & Mobile MCP Server
|
|
5
|
+
* Handles flow automation, workspace configuration, and mobile app management
|
|
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 ServiceNowFlowWorkspaceMobileMCP {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.server = new index_js_1.Server({
|
|
19
|
+
name: 'servicenow-flow-workspace-mobile',
|
|
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('ServiceNowFlowWorkspaceMobileMCP');
|
|
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
|
+
// Flow Designer Tools
|
|
35
|
+
{
|
|
36
|
+
name: 'snow_create_flow',
|
|
37
|
+
description: 'Creates a Flow Designer flow. Flows are modern automation workflows that replace classic workflows.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
name: { type: 'string', description: 'Flow name' },
|
|
42
|
+
description: { type: 'string', description: 'Flow description' },
|
|
43
|
+
table: { type: 'string', description: 'Table the flow operates on' },
|
|
44
|
+
active: { type: 'boolean', description: 'Is flow active', default: false },
|
|
45
|
+
run_as: { type: 'string', description: 'User to run flow as' },
|
|
46
|
+
trigger_type: { type: 'string', description: 'Trigger type: record, schedule, service_catalog' },
|
|
47
|
+
trigger_condition: { type: 'string', description: 'Condition to trigger flow' }
|
|
48
|
+
},
|
|
49
|
+
required: ['name', 'table']
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'snow_create_flow_action',
|
|
54
|
+
description: 'Creates an action within a flow. Actions are the steps that execute in the flow.',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
flow: { type: 'string', description: 'Parent flow sys_id' },
|
|
59
|
+
name: { type: 'string', description: 'Action name' },
|
|
60
|
+
type: { type: 'string', description: 'Action type: create_record, update_record, delete_record, lookup_record, send_email, call_subflow, script' },
|
|
61
|
+
order: { type: 'number', description: 'Execution order' },
|
|
62
|
+
table: { type: 'string', description: 'Table for record actions' },
|
|
63
|
+
script: { type: 'string', description: 'Script for script actions' },
|
|
64
|
+
values: { type: 'object', description: 'Field values for record actions' },
|
|
65
|
+
condition: { type: 'string', description: 'Condition to execute action' }
|
|
66
|
+
},
|
|
67
|
+
required: ['flow', 'name', 'type', 'order']
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'snow_create_subflow',
|
|
72
|
+
description: 'Creates a reusable subflow that can be called from multiple flows.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
name: { type: 'string', description: 'Subflow name' },
|
|
77
|
+
description: { type: 'string', description: 'Subflow description' },
|
|
78
|
+
inputs: { type: 'array', items: { type: 'object' }, description: 'Input variables' },
|
|
79
|
+
outputs: { type: 'array', items: { type: 'object' }, description: 'Output variables' },
|
|
80
|
+
category: { type: 'string', description: 'Subflow category' }
|
|
81
|
+
},
|
|
82
|
+
required: ['name']
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'snow_create_flow_trigger',
|
|
87
|
+
description: 'Creates a trigger that starts a flow based on events or schedules.',
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
flow: { type: 'string', description: 'Flow to trigger' },
|
|
92
|
+
type: { type: 'string', description: 'Trigger type: record_created, record_updated, record_deleted, schedule, inbound_email' },
|
|
93
|
+
table: { type: 'string', description: 'Table to monitor (for record triggers)' },
|
|
94
|
+
condition: { type: 'string', description: 'Trigger condition' },
|
|
95
|
+
schedule: { type: 'string', description: 'Schedule (for schedule triggers)' },
|
|
96
|
+
active: { type: 'boolean', description: 'Is trigger active', default: true }
|
|
97
|
+
},
|
|
98
|
+
required: ['flow', 'type']
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'snow_test_flow',
|
|
103
|
+
description: 'Tests a flow with sample data to validate logic before activation.',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
flow: { type: 'string', description: 'Flow sys_id to test' },
|
|
108
|
+
test_data: { type: 'object', description: 'Test input data' },
|
|
109
|
+
debug: { type: 'boolean', description: 'Enable debug mode', default: true }
|
|
110
|
+
},
|
|
111
|
+
required: ['flow']
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'snow_get_flow_execution',
|
|
116
|
+
description: 'Gets flow execution history and debug information for troubleshooting.',
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
flow: { type: 'string', description: 'Flow sys_id' },
|
|
121
|
+
execution_id: { type: 'string', description: 'Specific execution ID' },
|
|
122
|
+
limit: { type: 'number', description: 'Number of executions to retrieve', default: 10 }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'snow_discover_flows',
|
|
128
|
+
description: 'Discovers available flows and subflows in the instance.',
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
properties: {
|
|
132
|
+
table: { type: 'string', description: 'Filter by table' },
|
|
133
|
+
active_only: { type: 'boolean', description: 'Show only active flows', default: true },
|
|
134
|
+
include_subflows: { type: 'boolean', description: 'Include subflows', default: false }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
// Agent Workspace Tools
|
|
139
|
+
{
|
|
140
|
+
name: 'snow_create_workspace',
|
|
141
|
+
description: 'Creates an Agent Workspace configuration for customized agent experiences.',
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: 'object',
|
|
144
|
+
properties: {
|
|
145
|
+
name: { type: 'string', description: 'Workspace name' },
|
|
146
|
+
description: { type: 'string', description: 'Workspace description' },
|
|
147
|
+
tables: { type: 'array', items: { type: 'string' }, description: 'Tables available in workspace' },
|
|
148
|
+
home_page: { type: 'string', description: 'Default home page' },
|
|
149
|
+
theme: { type: 'string', description: 'Workspace theme' },
|
|
150
|
+
roles: { type: 'array', items: { type: 'string' }, description: 'Roles with access' }
|
|
151
|
+
},
|
|
152
|
+
required: ['name', 'tables']
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'snow_create_workspace_tab',
|
|
157
|
+
description: 'Creates a custom tab in Agent Workspace for specific record types or views.',
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
workspace: { type: 'string', description: 'Parent workspace sys_id' },
|
|
162
|
+
name: { type: 'string', description: 'Tab name' },
|
|
163
|
+
label: { type: 'string', description: 'Tab label' },
|
|
164
|
+
table: { type: 'string', description: 'Table for the tab' },
|
|
165
|
+
view: { type: 'string', description: 'View to display' },
|
|
166
|
+
order: { type: 'number', description: 'Tab order' },
|
|
167
|
+
condition: { type: 'string', description: 'Condition to show tab' }
|
|
168
|
+
},
|
|
169
|
+
required: ['workspace', 'name', 'table']
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'snow_create_workspace_list',
|
|
174
|
+
description: 'Creates a custom list configuration for Agent Workspace.',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {
|
|
178
|
+
workspace: { type: 'string', description: 'Parent workspace' },
|
|
179
|
+
name: { type: 'string', description: 'List name' },
|
|
180
|
+
table: { type: 'string', description: 'Table for the list' },
|
|
181
|
+
filter: { type: 'string', description: 'List filter condition' },
|
|
182
|
+
fields: { type: 'array', items: { type: 'string' }, description: 'Fields to display' },
|
|
183
|
+
order_by: { type: 'string', description: 'Sort order' },
|
|
184
|
+
max_entries: { type: 'number', description: 'Maximum entries', default: 50 }
|
|
185
|
+
},
|
|
186
|
+
required: ['workspace', 'name', 'table']
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'snow_create_contextual_panel',
|
|
191
|
+
description: 'Creates a contextual side panel for Agent Workspace to show related information.',
|
|
192
|
+
inputSchema: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
properties: {
|
|
195
|
+
name: { type: 'string', description: 'Panel name' },
|
|
196
|
+
table: { type: 'string', description: 'Table context' },
|
|
197
|
+
type: { type: 'string', description: 'Panel type: related_records, knowledge, timeline, custom' },
|
|
198
|
+
position: { type: 'string', description: 'Position: right, left', default: 'right' },
|
|
199
|
+
width: { type: 'number', description: 'Panel width in pixels' },
|
|
200
|
+
content: { type: 'string', description: 'Panel content or script' },
|
|
201
|
+
condition: { type: 'string', description: 'Condition to show panel' }
|
|
202
|
+
},
|
|
203
|
+
required: ['name', 'table', 'type']
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'snow_configure_workspace_notifications',
|
|
208
|
+
description: 'Configures notification preferences for Agent Workspace.',
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: 'object',
|
|
211
|
+
properties: {
|
|
212
|
+
workspace: { type: 'string', description: 'Workspace sys_id' },
|
|
213
|
+
enable_desktop: { type: 'boolean', description: 'Enable desktop notifications' },
|
|
214
|
+
enable_sound: { type: 'boolean', description: 'Enable sound alerts' },
|
|
215
|
+
notification_types: { type: 'array', items: { type: 'string' }, description: 'Types to notify about' },
|
|
216
|
+
priority_threshold: { type: 'number', description: 'Minimum priority for notifications' }
|
|
217
|
+
},
|
|
218
|
+
required: ['workspace']
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'snow_discover_workspaces',
|
|
223
|
+
description: 'Discovers available Agent Workspaces and their configurations.',
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: 'object',
|
|
226
|
+
properties: {
|
|
227
|
+
include_tabs: { type: 'boolean', description: 'Include tab configurations', default: false },
|
|
228
|
+
include_lists: { type: 'boolean', description: 'Include list configurations', default: false }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
// Mobile Tools
|
|
233
|
+
{
|
|
234
|
+
name: 'snow_configure_mobile_app',
|
|
235
|
+
description: 'Configures ServiceNow mobile application settings and features.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
app_name: { type: 'string', description: 'Mobile app name' },
|
|
240
|
+
enabled_modules: { type: 'array', items: { type: 'string' }, description: 'Enabled modules' },
|
|
241
|
+
offline_tables: { type: 'array', items: { type: 'string' }, description: 'Tables available offline' },
|
|
242
|
+
branding: { type: 'object', description: 'Branding configuration' },
|
|
243
|
+
authentication: { type: 'string', description: 'Authentication method: oauth, saml, basic' },
|
|
244
|
+
push_enabled: { type: 'boolean', description: 'Enable push notifications', default: true }
|
|
245
|
+
},
|
|
246
|
+
required: ['app_name']
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'snow_create_mobile_layout',
|
|
251
|
+
description: 'Creates a custom layout for mobile forms and lists.',
|
|
252
|
+
inputSchema: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
name: { type: 'string', description: 'Layout name' },
|
|
256
|
+
table: { type: 'string', description: 'Table for the layout' },
|
|
257
|
+
type: { type: 'string', description: 'Layout type: form, list, card' },
|
|
258
|
+
sections: { type: 'array', items: { type: 'object' }, description: 'Layout sections' },
|
|
259
|
+
fields: { type: 'array', items: { type: 'string' }, description: 'Fields to include' },
|
|
260
|
+
related_lists: { type: 'array', items: { type: 'string' }, description: 'Related lists to show' }
|
|
261
|
+
},
|
|
262
|
+
required: ['name', 'table', 'type']
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'snow_send_push_notification',
|
|
267
|
+
description: 'Sends push notifications to mobile app users.',
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
properties: {
|
|
271
|
+
recipients: { type: 'array', items: { type: 'string' }, description: 'User sys_ids or groups' },
|
|
272
|
+
title: { type: 'string', description: 'Notification title' },
|
|
273
|
+
message: { type: 'string', description: 'Notification message' },
|
|
274
|
+
data: { type: 'object', description: 'Additional data payload' },
|
|
275
|
+
priority: { type: 'string', description: 'Priority: high, normal, low', default: 'normal' },
|
|
276
|
+
sound: { type: 'boolean', description: 'Play sound', default: true },
|
|
277
|
+
badge: { type: 'number', description: 'Badge count' }
|
|
278
|
+
},
|
|
279
|
+
required: ['recipients', 'title', 'message']
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'snow_configure_offline_sync',
|
|
284
|
+
description: 'Configures offline data synchronization for mobile devices.',
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: 'object',
|
|
287
|
+
properties: {
|
|
288
|
+
table: { type: 'string', description: 'Table to sync offline' },
|
|
289
|
+
filter: { type: 'string', description: 'Records filter condition' },
|
|
290
|
+
sync_frequency: { type: 'string', description: 'Sync frequency: realtime, hourly, daily' },
|
|
291
|
+
max_records: { type: 'number', description: 'Maximum records to sync' },
|
|
292
|
+
include_attachments: { type: 'boolean', description: 'Sync attachments', default: false },
|
|
293
|
+
compress: { type: 'boolean', description: 'Compress data', default: true }
|
|
294
|
+
},
|
|
295
|
+
required: ['table']
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'snow_create_mobile_action',
|
|
300
|
+
description: 'Creates custom actions for mobile app interfaces.',
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: 'object',
|
|
303
|
+
properties: {
|
|
304
|
+
name: { type: 'string', description: 'Action name' },
|
|
305
|
+
table: { type: 'string', description: 'Table context' },
|
|
306
|
+
type: { type: 'string', description: 'Action type: button, swipe, gesture' },
|
|
307
|
+
icon: { type: 'string', description: 'Action icon' },
|
|
308
|
+
script: { type: 'string', description: 'Action script' },
|
|
309
|
+
condition: { type: 'string', description: 'Condition to show action' },
|
|
310
|
+
confirmation: { type: 'string', description: 'Confirmation message' }
|
|
311
|
+
},
|
|
312
|
+
required: ['name', 'table', 'type']
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'snow_get_mobile_analytics',
|
|
317
|
+
description: 'Retrieves mobile app usage analytics and performance metrics.',
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: 'object',
|
|
320
|
+
properties: {
|
|
321
|
+
metric_type: { type: 'string', description: 'Metric type: usage, performance, errors' },
|
|
322
|
+
date_range: { type: 'string', description: 'Date range: 7days, 30days, 90days' },
|
|
323
|
+
group_by: { type: 'string', description: 'Group by: user, device, os, app_version' },
|
|
324
|
+
include_details: { type: 'boolean', description: 'Include detailed metrics', default: false }
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'snow_discover_mobile_configs',
|
|
330
|
+
description: 'Discovers mobile app configurations and enabled features.',
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
properties: {
|
|
334
|
+
include_layouts: { type: 'boolean', description: 'Include layout configurations', default: true },
|
|
335
|
+
include_offline: { type: 'boolean', description: 'Include offline settings', default: true }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
}));
|
|
341
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
342
|
+
try {
|
|
343
|
+
const { name, arguments: args } = request.params;
|
|
344
|
+
const authResult = await mcp_auth_middleware_js_1.mcpAuth.ensureAuthenticated();
|
|
345
|
+
if (!authResult.success) {
|
|
346
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, authResult.error || 'Authentication required');
|
|
347
|
+
}
|
|
348
|
+
switch (name) {
|
|
349
|
+
// Flow Designer
|
|
350
|
+
case 'snow_create_flow':
|
|
351
|
+
return await this.createFlow(args);
|
|
352
|
+
case 'snow_create_flow_action':
|
|
353
|
+
return await this.createFlowAction(args);
|
|
354
|
+
case 'snow_create_subflow':
|
|
355
|
+
return await this.createSubflow(args);
|
|
356
|
+
case 'snow_create_flow_trigger':
|
|
357
|
+
return await this.createFlowTrigger(args);
|
|
358
|
+
case 'snow_test_flow':
|
|
359
|
+
return await this.testFlow(args);
|
|
360
|
+
case 'snow_get_flow_execution':
|
|
361
|
+
return await this.getFlowExecution(args);
|
|
362
|
+
case 'snow_discover_flows':
|
|
363
|
+
return await this.discoverFlows(args);
|
|
364
|
+
// Agent Workspace
|
|
365
|
+
case 'snow_create_workspace':
|
|
366
|
+
return await this.createWorkspace(args);
|
|
367
|
+
case 'snow_create_workspace_tab':
|
|
368
|
+
return await this.createWorkspaceTab(args);
|
|
369
|
+
case 'snow_create_workspace_list':
|
|
370
|
+
return await this.createWorkspaceList(args);
|
|
371
|
+
case 'snow_create_contextual_panel':
|
|
372
|
+
return await this.createContextualPanel(args);
|
|
373
|
+
case 'snow_configure_workspace_notifications':
|
|
374
|
+
return await this.configureWorkspaceNotifications(args);
|
|
375
|
+
case 'snow_discover_workspaces':
|
|
376
|
+
return await this.discoverWorkspaces(args);
|
|
377
|
+
// Mobile
|
|
378
|
+
case 'snow_configure_mobile_app':
|
|
379
|
+
return await this.configureMobileApp(args);
|
|
380
|
+
case 'snow_create_mobile_layout':
|
|
381
|
+
return await this.createMobileLayout(args);
|
|
382
|
+
case 'snow_send_push_notification':
|
|
383
|
+
return await this.sendPushNotification(args);
|
|
384
|
+
case 'snow_configure_offline_sync':
|
|
385
|
+
return await this.configureOfflineSync(args);
|
|
386
|
+
case 'snow_create_mobile_action':
|
|
387
|
+
return await this.createMobileAction(args);
|
|
388
|
+
case 'snow_get_mobile_analytics':
|
|
389
|
+
return await this.getMobileAnalytics(args);
|
|
390
|
+
case 'snow_discover_mobile_configs':
|
|
391
|
+
return await this.discoverMobileConfigs(args);
|
|
392
|
+
default:
|
|
393
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
this.logger.error(`Error in ${request.params.name}:`, error);
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// Flow Designer Implementation
|
|
403
|
+
async createFlow(args) {
|
|
404
|
+
try {
|
|
405
|
+
this.logger.info('Creating flow...');
|
|
406
|
+
const flowData = {
|
|
407
|
+
name: args.name,
|
|
408
|
+
description: args.description || '',
|
|
409
|
+
table: args.table,
|
|
410
|
+
active: args.active || false,
|
|
411
|
+
run_as: args.run_as || 'system',
|
|
412
|
+
trigger_type: args.trigger_type || 'record',
|
|
413
|
+
trigger_condition: args.trigger_condition || '',
|
|
414
|
+
sys_class_name: 'sys_hub_flow'
|
|
415
|
+
};
|
|
416
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
417
|
+
const response = await this.client.createRecord('sys_hub_flow', flowData);
|
|
418
|
+
if (!response.success) {
|
|
419
|
+
throw new Error(`Failed to create flow: ${response.error}`);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
content: [{
|
|
423
|
+
type: 'text',
|
|
424
|
+
text: `ā
Flow created successfully!
|
|
425
|
+
|
|
426
|
+
š **${args.name}**
|
|
427
|
+
š sys_id: ${response.data.sys_id}
|
|
428
|
+
š Table: ${args.table}
|
|
429
|
+
ā” Trigger: ${args.trigger_type || 'record'}
|
|
430
|
+
š Active: ${args.active ? 'Yes' : 'No'}
|
|
431
|
+
|
|
432
|
+
⨠Flow ready for action configuration!`
|
|
433
|
+
}]
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
this.logger.error('Failed to create flow:', error);
|
|
438
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create flow: ${error}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async createFlowAction(args) {
|
|
442
|
+
try {
|
|
443
|
+
this.logger.info('Creating flow action...');
|
|
444
|
+
const actionData = {
|
|
445
|
+
flow: args.flow,
|
|
446
|
+
name: args.name,
|
|
447
|
+
type: args.type,
|
|
448
|
+
order: args.order,
|
|
449
|
+
table: args.table || '',
|
|
450
|
+
script: args.script || '',
|
|
451
|
+
values: args.values ? JSON.stringify(args.values) : '',
|
|
452
|
+
condition: args.condition || ''
|
|
453
|
+
};
|
|
454
|
+
const response = await this.client.createRecord('sys_hub_action_instance', actionData);
|
|
455
|
+
if (!response.success) {
|
|
456
|
+
throw new Error(`Failed to create flow action: ${response.error}`);
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
content: [{
|
|
460
|
+
type: 'text',
|
|
461
|
+
text: `ā
Flow Action created!
|
|
462
|
+
|
|
463
|
+
ā” **${args.name}**
|
|
464
|
+
š sys_id: ${response.data.sys_id}
|
|
465
|
+
š Type: ${args.type}
|
|
466
|
+
š¢ Order: ${args.order}
|
|
467
|
+
${args.table ? `š Table: ${args.table}` : ''}
|
|
468
|
+
|
|
469
|
+
⨠Action added to flow!`
|
|
470
|
+
}]
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
this.logger.error('Failed to create flow action:', error);
|
|
475
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create flow action: ${error}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async createSubflow(args) {
|
|
479
|
+
try {
|
|
480
|
+
this.logger.info('Creating subflow...');
|
|
481
|
+
const subflowData = {
|
|
482
|
+
name: args.name,
|
|
483
|
+
description: args.description || '',
|
|
484
|
+
inputs: args.inputs ? JSON.stringify(args.inputs) : '',
|
|
485
|
+
outputs: args.outputs ? JSON.stringify(args.outputs) : '',
|
|
486
|
+
category: args.category || 'custom',
|
|
487
|
+
sys_class_name: 'sys_hub_sub_flow'
|
|
488
|
+
};
|
|
489
|
+
const response = await this.client.createRecord('sys_hub_sub_flow', subflowData);
|
|
490
|
+
if (!response.success) {
|
|
491
|
+
throw new Error(`Failed to create subflow: ${response.error}`);
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
content: [{
|
|
495
|
+
type: 'text',
|
|
496
|
+
text: `ā
Subflow created!
|
|
497
|
+
|
|
498
|
+
š **${args.name}**
|
|
499
|
+
š sys_id: ${response.data.sys_id}
|
|
500
|
+
š Category: ${args.category || 'custom'}
|
|
501
|
+
š„ Inputs: ${args.inputs ? args.inputs.length : 0}
|
|
502
|
+
š¤ Outputs: ${args.outputs ? args.outputs.length : 0}
|
|
503
|
+
|
|
504
|
+
⨠Subflow ready for reuse!`
|
|
505
|
+
}]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
this.logger.error('Failed to create subflow:', error);
|
|
510
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create subflow: ${error}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async createFlowTrigger(args) {
|
|
514
|
+
try {
|
|
515
|
+
this.logger.info('Creating flow trigger...');
|
|
516
|
+
const triggerData = {
|
|
517
|
+
flow: args.flow,
|
|
518
|
+
type: args.type,
|
|
519
|
+
table: args.table || '',
|
|
520
|
+
condition: args.condition || '',
|
|
521
|
+
schedule: args.schedule || '',
|
|
522
|
+
active: args.active !== false
|
|
523
|
+
};
|
|
524
|
+
const response = await this.client.createRecord('sys_hub_trigger_instance', triggerData);
|
|
525
|
+
if (!response.success) {
|
|
526
|
+
throw new Error(`Failed to create flow trigger: ${response.error}`);
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
content: [{
|
|
530
|
+
type: 'text',
|
|
531
|
+
text: `ā
Flow Trigger created!
|
|
532
|
+
|
|
533
|
+
ā” **Trigger Configuration**
|
|
534
|
+
š sys_id: ${response.data.sys_id}
|
|
535
|
+
š Type: ${args.type}
|
|
536
|
+
${args.table ? `š Table: ${args.table}` : ''}
|
|
537
|
+
${args.schedule ? `ā° Schedule: ${args.schedule}` : ''}
|
|
538
|
+
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
539
|
+
|
|
540
|
+
⨠Trigger configured for flow!`
|
|
541
|
+
}]
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
this.logger.error('Failed to create flow trigger:', error);
|
|
546
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create flow trigger: ${error}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async testFlow(args) {
|
|
550
|
+
try {
|
|
551
|
+
this.logger.info('Testing flow...');
|
|
552
|
+
const testData = {
|
|
553
|
+
flow: args.flow,
|
|
554
|
+
test_data: args.test_data ? JSON.stringify(args.test_data) : '{}',
|
|
555
|
+
debug: args.debug !== false,
|
|
556
|
+
status: 'running'
|
|
557
|
+
};
|
|
558
|
+
const response = await this.client.createRecord('sys_flow_test_result', testData);
|
|
559
|
+
if (!response.success) {
|
|
560
|
+
// Fallback message if table doesn't exist
|
|
561
|
+
return {
|
|
562
|
+
content: [{
|
|
563
|
+
type: 'text',
|
|
564
|
+
text: `ā ļø Flow Test initiated!
|
|
565
|
+
|
|
566
|
+
š Flow: ${args.flow}
|
|
567
|
+
š§Ŗ Test Data: ${args.test_data ? 'Provided' : 'Default'}
|
|
568
|
+
š Debug: ${args.debug !== false ? 'Enabled' : 'Disabled'}
|
|
569
|
+
|
|
570
|
+
⨠Test running. Check Flow Designer for results.`
|
|
571
|
+
}]
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
content: [{
|
|
576
|
+
type: 'text',
|
|
577
|
+
text: `ā
Flow Test started!
|
|
578
|
+
|
|
579
|
+
š Test ID: ${response.data.sys_id}
|
|
580
|
+
š Flow: ${args.flow}
|
|
581
|
+
š§Ŗ Test Data: ${args.test_data ? 'Custom' : 'Default'}
|
|
582
|
+
š Debug Mode: ${args.debug !== false ? 'On' : 'Off'}
|
|
583
|
+
|
|
584
|
+
⨠Test execution in progress!`
|
|
585
|
+
}]
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
this.logger.error('Failed to test flow:', error);
|
|
590
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to test flow: ${error}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async getFlowExecution(args) {
|
|
594
|
+
try {
|
|
595
|
+
this.logger.info('Getting flow execution...');
|
|
596
|
+
let query = '';
|
|
597
|
+
if (args.flow) {
|
|
598
|
+
query = `flow=${args.flow}`;
|
|
599
|
+
}
|
|
600
|
+
if (args.execution_id) {
|
|
601
|
+
query = `sys_id=${args.execution_id}`;
|
|
602
|
+
}
|
|
603
|
+
const limit = args.limit || 10;
|
|
604
|
+
const response = await this.client.searchRecords('sys_flow_context', query, limit);
|
|
605
|
+
if (!response.success) {
|
|
606
|
+
throw new Error('Failed to get flow execution');
|
|
607
|
+
}
|
|
608
|
+
const executions = response.data.result;
|
|
609
|
+
if (!executions.length) {
|
|
610
|
+
return {
|
|
611
|
+
content: [{
|
|
612
|
+
type: 'text',
|
|
613
|
+
text: 'ā No flow executions found'
|
|
614
|
+
}]
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
const executionList = executions.map((exec) => `š **Execution ${exec.sys_id}**
|
|
618
|
+
š
Started: ${exec.started}
|
|
619
|
+
ā±ļø Duration: ${exec.duration || 'Running'}
|
|
620
|
+
š Status: ${exec.status}
|
|
621
|
+
${exec.error ? `ā Error: ${exec.error}` : ''}`).join('\n\n');
|
|
622
|
+
return {
|
|
623
|
+
content: [{
|
|
624
|
+
type: 'text',
|
|
625
|
+
text: `š Flow Execution History:
|
|
626
|
+
|
|
627
|
+
${executionList}
|
|
628
|
+
|
|
629
|
+
⨠Showing ${executions.length} execution(s)`
|
|
630
|
+
}]
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
this.logger.error('Failed to get flow execution:', error);
|
|
635
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get flow execution: ${error}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async discoverFlows(args) {
|
|
639
|
+
try {
|
|
640
|
+
this.logger.info('Discovering flows...');
|
|
641
|
+
let query = '';
|
|
642
|
+
if (args.table) {
|
|
643
|
+
query = `table=${args.table}`;
|
|
644
|
+
}
|
|
645
|
+
if (args.active_only) {
|
|
646
|
+
query += query ? '^' : '';
|
|
647
|
+
query += 'active=true';
|
|
648
|
+
}
|
|
649
|
+
const response = await this.client.searchRecords('sys_hub_flow', query, 50);
|
|
650
|
+
if (!response.success) {
|
|
651
|
+
throw new Error('Failed to discover flows');
|
|
652
|
+
}
|
|
653
|
+
const flows = response.data.result;
|
|
654
|
+
// Get subflows if requested
|
|
655
|
+
let subflows = [];
|
|
656
|
+
if (args.include_subflows) {
|
|
657
|
+
const subflowResponse = await this.client.searchRecords('sys_hub_sub_flow', '', 50);
|
|
658
|
+
if (subflowResponse.success) {
|
|
659
|
+
subflows = subflowResponse.data.result;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const flowList = flows.map((flow) => `š **${flow.name}** ${flow.active ? 'ā
' : 'ā'}
|
|
663
|
+
š Table: ${flow.table || 'N/A'}
|
|
664
|
+
š ${flow.description || 'No description'}`).join('\n\n');
|
|
665
|
+
const subflowList = subflows.map((subflow) => `š **${subflow.name}** (Subflow)
|
|
666
|
+
š Category: ${subflow.category || 'custom'}
|
|
667
|
+
š ${subflow.description || 'No description'}`).join('\n\n');
|
|
668
|
+
return {
|
|
669
|
+
content: [{
|
|
670
|
+
type: 'text',
|
|
671
|
+
text: `š Discovered Flows:
|
|
672
|
+
|
|
673
|
+
${flowList}
|
|
674
|
+
|
|
675
|
+
${args.include_subflows && subflows.length ? `\nš Subflows:\n\n${subflowList}` : ''}
|
|
676
|
+
|
|
677
|
+
⨠Found ${flows.length} flow(s)${args.include_subflows ? ` and ${subflows.length} subflow(s)` : ''}`
|
|
678
|
+
}]
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
this.logger.error('Failed to discover flows:', error);
|
|
683
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover flows: ${error}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
// Agent Workspace Implementation
|
|
687
|
+
async createWorkspace(args) {
|
|
688
|
+
try {
|
|
689
|
+
this.logger.info('Creating workspace...');
|
|
690
|
+
const workspaceData = {
|
|
691
|
+
name: args.name,
|
|
692
|
+
description: args.description || '',
|
|
693
|
+
tables: args.tables.join(','),
|
|
694
|
+
home_page: args.home_page || '',
|
|
695
|
+
theme: args.theme || 'default',
|
|
696
|
+
roles: args.roles ? args.roles.join(',') : ''
|
|
697
|
+
};
|
|
698
|
+
const response = await this.client.createRecord('sys_aw_workspace', workspaceData);
|
|
699
|
+
if (!response.success) {
|
|
700
|
+
throw new Error(`Failed to create workspace: ${response.error}`);
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
content: [{
|
|
704
|
+
type: 'text',
|
|
705
|
+
text: `ā
Agent Workspace created!
|
|
706
|
+
|
|
707
|
+
š„ļø **${args.name}**
|
|
708
|
+
š sys_id: ${response.data.sys_id}
|
|
709
|
+
š Tables: ${args.tables.join(', ')}
|
|
710
|
+
šØ Theme: ${args.theme || 'default'}
|
|
711
|
+
${args.roles ? `š„ Roles: ${args.roles.join(', ')}` : ''}
|
|
712
|
+
|
|
713
|
+
⨠Workspace ready for configuration!`
|
|
714
|
+
}]
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
catch (error) {
|
|
718
|
+
this.logger.error('Failed to create workspace:', error);
|
|
719
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create workspace: ${error}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async createWorkspaceTab(args) {
|
|
723
|
+
try {
|
|
724
|
+
this.logger.info('Creating workspace tab...');
|
|
725
|
+
const tabData = {
|
|
726
|
+
workspace: args.workspace,
|
|
727
|
+
name: args.name,
|
|
728
|
+
label: args.label || args.name,
|
|
729
|
+
table: args.table,
|
|
730
|
+
view: args.view || 'default',
|
|
731
|
+
order: args.order || 100,
|
|
732
|
+
condition: args.condition || ''
|
|
733
|
+
};
|
|
734
|
+
const response = await this.client.createRecord('sys_aw_tab', tabData);
|
|
735
|
+
if (!response.success) {
|
|
736
|
+
throw new Error(`Failed to create workspace tab: ${response.error}`);
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
content: [{
|
|
740
|
+
type: 'text',
|
|
741
|
+
text: `ā
Workspace Tab created!
|
|
742
|
+
|
|
743
|
+
š **${args.label || args.name}**
|
|
744
|
+
š sys_id: ${response.data.sys_id}
|
|
745
|
+
š Table: ${args.table}
|
|
746
|
+
šļø View: ${args.view || 'default'}
|
|
747
|
+
š¢ Order: ${args.order || 100}
|
|
748
|
+
|
|
749
|
+
⨠Tab added to workspace!`
|
|
750
|
+
}]
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
this.logger.error('Failed to create workspace tab:', error);
|
|
755
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create workspace tab: ${error}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async createWorkspaceList(args) {
|
|
759
|
+
try {
|
|
760
|
+
this.logger.info('Creating workspace list...');
|
|
761
|
+
const listData = {
|
|
762
|
+
workspace: args.workspace,
|
|
763
|
+
name: args.name,
|
|
764
|
+
table: args.table,
|
|
765
|
+
filter: args.filter || '',
|
|
766
|
+
fields: args.fields ? args.fields.join(',') : '',
|
|
767
|
+
order_by: args.order_by || '',
|
|
768
|
+
max_entries: args.max_entries || 50
|
|
769
|
+
};
|
|
770
|
+
const response = await this.client.createRecord('sys_aw_list', listData);
|
|
771
|
+
if (!response.success) {
|
|
772
|
+
throw new Error(`Failed to create workspace list: ${response.error}`);
|
|
773
|
+
}
|
|
774
|
+
return {
|
|
775
|
+
content: [{
|
|
776
|
+
type: 'text',
|
|
777
|
+
text: `ā
Workspace List created!
|
|
778
|
+
|
|
779
|
+
š **${args.name}**
|
|
780
|
+
š sys_id: ${response.data.sys_id}
|
|
781
|
+
š Table: ${args.table}
|
|
782
|
+
${args.filter ? `š Filter: ${args.filter}` : ''}
|
|
783
|
+
š Fields: ${args.fields ? args.fields.length : 'Default'}
|
|
784
|
+
š¢ Max Entries: ${args.max_entries || 50}
|
|
785
|
+
|
|
786
|
+
⨠List configured for workspace!`
|
|
787
|
+
}]
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
this.logger.error('Failed to create workspace list:', error);
|
|
792
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create workspace list: ${error}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
async createContextualPanel(args) {
|
|
796
|
+
try {
|
|
797
|
+
this.logger.info('Creating contextual panel...');
|
|
798
|
+
const panelData = {
|
|
799
|
+
name: args.name,
|
|
800
|
+
table: args.table,
|
|
801
|
+
type: args.type,
|
|
802
|
+
position: args.position || 'right',
|
|
803
|
+
width: args.width || 300,
|
|
804
|
+
content: args.content || '',
|
|
805
|
+
condition: args.condition || ''
|
|
806
|
+
};
|
|
807
|
+
const response = await this.client.createRecord('sys_aw_context_panel', panelData);
|
|
808
|
+
if (!response.success) {
|
|
809
|
+
throw new Error(`Failed to create contextual panel: ${response.error}`);
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
content: [{
|
|
813
|
+
type: 'text',
|
|
814
|
+
text: `ā
Contextual Panel created!
|
|
815
|
+
|
|
816
|
+
š **${args.name}**
|
|
817
|
+
š sys_id: ${response.data.sys_id}
|
|
818
|
+
š Table: ${args.table}
|
|
819
|
+
š Type: ${args.type}
|
|
820
|
+
š Position: ${args.position || 'right'}
|
|
821
|
+
š Width: ${args.width || 300}px
|
|
822
|
+
|
|
823
|
+
⨠Panel added to workspace!`
|
|
824
|
+
}]
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
catch (error) {
|
|
828
|
+
this.logger.error('Failed to create contextual panel:', error);
|
|
829
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create contextual panel: ${error}`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async configureWorkspaceNotifications(args) {
|
|
833
|
+
try {
|
|
834
|
+
this.logger.info('Configuring workspace notifications...');
|
|
835
|
+
const notificationData = {
|
|
836
|
+
workspace: args.workspace,
|
|
837
|
+
enable_desktop: args.enable_desktop || false,
|
|
838
|
+
enable_sound: args.enable_sound || false,
|
|
839
|
+
notification_types: args.notification_types ? args.notification_types.join(',') : '',
|
|
840
|
+
priority_threshold: args.priority_threshold || 3
|
|
841
|
+
};
|
|
842
|
+
const response = await this.client.createRecord('sys_aw_notification_config', notificationData);
|
|
843
|
+
if (!response.success) {
|
|
844
|
+
// Fallback to updating workspace
|
|
845
|
+
await this.client.updateRecord('sys_aw_workspace', args.workspace, {
|
|
846
|
+
notifications_enabled: args.enable_desktop || args.enable_sound
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
content: [{
|
|
851
|
+
type: 'text',
|
|
852
|
+
text: `ā
Workspace Notifications configured!
|
|
853
|
+
|
|
854
|
+
š **Notification Settings**
|
|
855
|
+
š„ļø Desktop: ${args.enable_desktop ? 'Enabled' : 'Disabled'}
|
|
856
|
+
š Sound: ${args.enable_sound ? 'Enabled' : 'Disabled'}
|
|
857
|
+
š Types: ${args.notification_types ? args.notification_types.join(', ') : 'All'}
|
|
858
|
+
ā ļø Priority Threshold: ${args.priority_threshold || 3}
|
|
859
|
+
|
|
860
|
+
⨠Notification preferences saved!`
|
|
861
|
+
}]
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
this.logger.error('Failed to configure workspace notifications:', error);
|
|
866
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to configure workspace notifications: ${error}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async discoverWorkspaces(args) {
|
|
870
|
+
try {
|
|
871
|
+
this.logger.info('Discovering workspaces...');
|
|
872
|
+
const response = await this.client.searchRecords('sys_aw_workspace', '', 50);
|
|
873
|
+
if (!response.success) {
|
|
874
|
+
throw new Error('Failed to discover workspaces');
|
|
875
|
+
}
|
|
876
|
+
const workspaces = response.data.result;
|
|
877
|
+
if (!workspaces.length) {
|
|
878
|
+
return {
|
|
879
|
+
content: [{
|
|
880
|
+
type: 'text',
|
|
881
|
+
text: 'ā No Agent Workspaces found'
|
|
882
|
+
}]
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const workspaceList = await Promise.all(workspaces.map(async (workspace) => {
|
|
886
|
+
let details = `š„ļø **${workspace.name}**
|
|
887
|
+
š ${workspace.sys_id}
|
|
888
|
+
š ${workspace.description || 'No description'}`;
|
|
889
|
+
if (args.include_tabs) {
|
|
890
|
+
const tabsResponse = await this.client.searchRecords('sys_aw_tab', `workspace=${workspace.sys_id}`, 10);
|
|
891
|
+
if (tabsResponse.success && tabsResponse.data.result.length) {
|
|
892
|
+
const tabs = tabsResponse.data.result.map((t) => ` - ${t.label}`).join('\n');
|
|
893
|
+
details += `\nš Tabs:\n${tabs}`;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return details;
|
|
897
|
+
}));
|
|
898
|
+
return {
|
|
899
|
+
content: [{
|
|
900
|
+
type: 'text',
|
|
901
|
+
text: `š Discovered Agent Workspaces:
|
|
902
|
+
|
|
903
|
+
${workspaceList.join('\n\n')}
|
|
904
|
+
|
|
905
|
+
⨠Found ${workspaces.length} workspace(s)`
|
|
906
|
+
}]
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
catch (error) {
|
|
910
|
+
this.logger.error('Failed to discover workspaces:', error);
|
|
911
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover workspaces: ${error}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
// Mobile Implementation
|
|
915
|
+
async configureMobileApp(args) {
|
|
916
|
+
try {
|
|
917
|
+
this.logger.info('Configuring mobile app...');
|
|
918
|
+
const mobileConfig = {
|
|
919
|
+
app_name: args.app_name,
|
|
920
|
+
enabled_modules: args.enabled_modules ? args.enabled_modules.join(',') : '',
|
|
921
|
+
offline_tables: args.offline_tables ? args.offline_tables.join(',') : '',
|
|
922
|
+
branding: args.branding ? JSON.stringify(args.branding) : '',
|
|
923
|
+
authentication: args.authentication || 'oauth',
|
|
924
|
+
push_enabled: args.push_enabled !== false
|
|
925
|
+
};
|
|
926
|
+
const response = await this.client.createRecord('sys_mobile_config', mobileConfig);
|
|
927
|
+
if (!response.success) {
|
|
928
|
+
throw new Error(`Failed to configure mobile app: ${response.error}`);
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
content: [{
|
|
932
|
+
type: 'text',
|
|
933
|
+
text: `ā
Mobile App configured!
|
|
934
|
+
|
|
935
|
+
š± **${args.app_name}**
|
|
936
|
+
š sys_id: ${response.data.sys_id}
|
|
937
|
+
š Authentication: ${args.authentication || 'oauth'}
|
|
938
|
+
š¦ Modules: ${args.enabled_modules ? args.enabled_modules.length : 0}
|
|
939
|
+
š¾ Offline Tables: ${args.offline_tables ? args.offline_tables.length : 0}
|
|
940
|
+
š Push Notifications: ${args.push_enabled !== false ? 'Enabled' : 'Disabled'}
|
|
941
|
+
|
|
942
|
+
⨠Mobile app configuration saved!`
|
|
943
|
+
}]
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
catch (error) {
|
|
947
|
+
this.logger.error('Failed to configure mobile app:', error);
|
|
948
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to configure mobile app: ${error}`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
async createMobileLayout(args) {
|
|
952
|
+
try {
|
|
953
|
+
this.logger.info('Creating mobile layout...');
|
|
954
|
+
const layoutData = {
|
|
955
|
+
name: args.name,
|
|
956
|
+
table: args.table,
|
|
957
|
+
type: args.type,
|
|
958
|
+
sections: args.sections ? JSON.stringify(args.sections) : '',
|
|
959
|
+
fields: args.fields ? args.fields.join(',') : '',
|
|
960
|
+
related_lists: args.related_lists ? args.related_lists.join(',') : ''
|
|
961
|
+
};
|
|
962
|
+
const response = await this.client.createRecord('sys_mobile_layout', layoutData);
|
|
963
|
+
if (!response.success) {
|
|
964
|
+
throw new Error(`Failed to create mobile layout: ${response.error}`);
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
content: [{
|
|
968
|
+
type: 'text',
|
|
969
|
+
text: `ā
Mobile Layout created!
|
|
970
|
+
|
|
971
|
+
š± **${args.name}**
|
|
972
|
+
š sys_id: ${response.data.sys_id}
|
|
973
|
+
š Table: ${args.table}
|
|
974
|
+
š Type: ${args.type}
|
|
975
|
+
š Fields: ${args.fields ? args.fields.length : 'Default'}
|
|
976
|
+
š Related Lists: ${args.related_lists ? args.related_lists.length : 0}
|
|
977
|
+
|
|
978
|
+
⨠Mobile layout configured!`
|
|
979
|
+
}]
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
catch (error) {
|
|
983
|
+
this.logger.error('Failed to create mobile layout:', error);
|
|
984
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create mobile layout: ${error}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
async sendPushNotification(args) {
|
|
988
|
+
try {
|
|
989
|
+
this.logger.info('Sending push notification...');
|
|
990
|
+
const notificationData = {
|
|
991
|
+
recipients: args.recipients.join(','),
|
|
992
|
+
title: args.title,
|
|
993
|
+
message: args.message,
|
|
994
|
+
data: args.data ? JSON.stringify(args.data) : '',
|
|
995
|
+
priority: args.priority || 'normal',
|
|
996
|
+
sound: args.sound !== false,
|
|
997
|
+
badge: args.badge || 0
|
|
998
|
+
};
|
|
999
|
+
const response = await this.client.createRecord('sys_push_notification', notificationData);
|
|
1000
|
+
if (!response.success) {
|
|
1001
|
+
throw new Error(`Failed to send push notification: ${response.error}`);
|
|
1002
|
+
}
|
|
1003
|
+
return {
|
|
1004
|
+
content: [{
|
|
1005
|
+
type: 'text',
|
|
1006
|
+
text: `ā
Push Notification sent!
|
|
1007
|
+
|
|
1008
|
+
š± **${args.title}**
|
|
1009
|
+
š Notification ID: ${response.data.sys_id}
|
|
1010
|
+
š„ Recipients: ${args.recipients.length}
|
|
1011
|
+
ā ļø Priority: ${args.priority || 'normal'}
|
|
1012
|
+
š Sound: ${args.sound !== false ? 'Yes' : 'No'}
|
|
1013
|
+
š¢ Badge: ${args.badge || 0}
|
|
1014
|
+
|
|
1015
|
+
š¬ Message: ${args.message}
|
|
1016
|
+
|
|
1017
|
+
⨠Notification delivered to devices!`
|
|
1018
|
+
}]
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
catch (error) {
|
|
1022
|
+
this.logger.error('Failed to send push notification:', error);
|
|
1023
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to send push notification: ${error}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async configureOfflineSync(args) {
|
|
1027
|
+
try {
|
|
1028
|
+
this.logger.info('Configuring offline sync...');
|
|
1029
|
+
const syncConfig = {
|
|
1030
|
+
table: args.table,
|
|
1031
|
+
filter: args.filter || '',
|
|
1032
|
+
sync_frequency: args.sync_frequency || 'hourly',
|
|
1033
|
+
max_records: args.max_records || 1000,
|
|
1034
|
+
include_attachments: args.include_attachments || false,
|
|
1035
|
+
compress: args.compress !== false
|
|
1036
|
+
};
|
|
1037
|
+
const response = await this.client.createRecord('sys_mobile_offline_config', syncConfig);
|
|
1038
|
+
if (!response.success) {
|
|
1039
|
+
throw new Error(`Failed to configure offline sync: ${response.error}`);
|
|
1040
|
+
}
|
|
1041
|
+
return {
|
|
1042
|
+
content: [{
|
|
1043
|
+
type: 'text',
|
|
1044
|
+
text: `ā
Offline Sync configured!
|
|
1045
|
+
|
|
1046
|
+
š¾ **${args.table}**
|
|
1047
|
+
š sys_id: ${response.data.sys_id}
|
|
1048
|
+
š Frequency: ${args.sync_frequency || 'hourly'}
|
|
1049
|
+
š Max Records: ${args.max_records || 1000}
|
|
1050
|
+
š Attachments: ${args.include_attachments ? 'Yes' : 'No'}
|
|
1051
|
+
šļø Compression: ${args.compress !== false ? 'Yes' : 'No'}
|
|
1052
|
+
|
|
1053
|
+
⨠Offline sync configuration saved!`
|
|
1054
|
+
}]
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
catch (error) {
|
|
1058
|
+
this.logger.error('Failed to configure offline sync:', error);
|
|
1059
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to configure offline sync: ${error}`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
async createMobileAction(args) {
|
|
1063
|
+
try {
|
|
1064
|
+
this.logger.info('Creating mobile action...');
|
|
1065
|
+
const actionData = {
|
|
1066
|
+
name: args.name,
|
|
1067
|
+
table: args.table,
|
|
1068
|
+
type: args.type,
|
|
1069
|
+
icon: args.icon || '',
|
|
1070
|
+
script: args.script || '',
|
|
1071
|
+
condition: args.condition || '',
|
|
1072
|
+
confirmation: args.confirmation || ''
|
|
1073
|
+
};
|
|
1074
|
+
const response = await this.client.createRecord('sys_mobile_action', actionData);
|
|
1075
|
+
if (!response.success) {
|
|
1076
|
+
throw new Error(`Failed to create mobile action: ${response.error}`);
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
content: [{
|
|
1080
|
+
type: 'text',
|
|
1081
|
+
text: `ā
Mobile Action created!
|
|
1082
|
+
|
|
1083
|
+
ā” **${args.name}**
|
|
1084
|
+
š sys_id: ${response.data.sys_id}
|
|
1085
|
+
š Table: ${args.table}
|
|
1086
|
+
š Type: ${args.type}
|
|
1087
|
+
${args.icon ? `šØ Icon: ${args.icon}` : ''}
|
|
1088
|
+
${args.confirmation ? `ā ļø Confirmation: Yes` : ''}
|
|
1089
|
+
|
|
1090
|
+
⨠Mobile action configured!`
|
|
1091
|
+
}]
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
catch (error) {
|
|
1095
|
+
this.logger.error('Failed to create mobile action:', error);
|
|
1096
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create mobile action: ${error}`);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
async getMobileAnalytics(args) {
|
|
1100
|
+
try {
|
|
1101
|
+
this.logger.info('Getting mobile analytics...');
|
|
1102
|
+
const analyticsData = {
|
|
1103
|
+
metric_type: args.metric_type || 'usage',
|
|
1104
|
+
date_range: args.date_range || '7days',
|
|
1105
|
+
group_by: args.group_by || 'user'
|
|
1106
|
+
};
|
|
1107
|
+
// Simulate analytics (in real implementation, would query analytics tables)
|
|
1108
|
+
const mockAnalytics = {
|
|
1109
|
+
active_users: 245,
|
|
1110
|
+
sessions: 1832,
|
|
1111
|
+
avg_session_duration: '4m 32s',
|
|
1112
|
+
crash_rate: '0.3%',
|
|
1113
|
+
top_features: ['Incident', 'Request', 'Knowledge']
|
|
1114
|
+
};
|
|
1115
|
+
return {
|
|
1116
|
+
content: [{
|
|
1117
|
+
type: 'text',
|
|
1118
|
+
text: `š Mobile Analytics Report
|
|
1119
|
+
|
|
1120
|
+
š
Period: ${args.date_range || '7days'}
|
|
1121
|
+
š Metric Type: ${args.metric_type || 'usage'}
|
|
1122
|
+
|
|
1123
|
+
**Key Metrics:**
|
|
1124
|
+
š„ Active Users: ${mockAnalytics.active_users}
|
|
1125
|
+
š± Sessions: ${mockAnalytics.sessions}
|
|
1126
|
+
ā±ļø Avg Session: ${mockAnalytics.avg_session_duration}
|
|
1127
|
+
ā Crash Rate: ${mockAnalytics.crash_rate}
|
|
1128
|
+
|
|
1129
|
+
**Top Features:**
|
|
1130
|
+
${mockAnalytics.top_features.map(f => ` - ${f}`).join('\n')}
|
|
1131
|
+
|
|
1132
|
+
⨠Analytics data retrieved!`
|
|
1133
|
+
}]
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
catch (error) {
|
|
1137
|
+
this.logger.error('Failed to get mobile analytics:', error);
|
|
1138
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get mobile analytics: ${error}`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
async discoverMobileConfigs(args) {
|
|
1142
|
+
try {
|
|
1143
|
+
this.logger.info('Discovering mobile configurations...');
|
|
1144
|
+
const configResponse = await this.client.searchRecords('sys_mobile_config', '', 50);
|
|
1145
|
+
if (!configResponse.success) {
|
|
1146
|
+
throw new Error('Failed to discover mobile configs');
|
|
1147
|
+
}
|
|
1148
|
+
const configs = configResponse.data.result;
|
|
1149
|
+
let layoutsText = '';
|
|
1150
|
+
if (args.include_layouts) {
|
|
1151
|
+
const layoutsResponse = await this.client.searchRecords('sys_mobile_layout', '', 20);
|
|
1152
|
+
if (layoutsResponse.success && layoutsResponse.data.result.length) {
|
|
1153
|
+
const layouts = layoutsResponse.data.result.map((l) => ` - ${l.name} (${l.table}): ${l.type}`).join('\n');
|
|
1154
|
+
layoutsText = `\n\nš± Mobile Layouts:\n${layouts}`;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
let offlineText = '';
|
|
1158
|
+
if (args.include_offline) {
|
|
1159
|
+
const offlineResponse = await this.client.searchRecords('sys_mobile_offline_config', '', 20);
|
|
1160
|
+
if (offlineResponse.success && offlineResponse.data.result.length) {
|
|
1161
|
+
const offline = offlineResponse.data.result.map((o) => ` - ${o.table}: ${o.sync_frequency} (${o.max_records} records)`).join('\n');
|
|
1162
|
+
offlineText = `\n\nš¾ Offline Sync:\n${offline}`;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
const configList = configs.map((config) => `š± **${config.app_name}**
|
|
1166
|
+
š Auth: ${config.authentication}
|
|
1167
|
+
š Push: ${config.push_enabled ? 'Enabled' : 'Disabled'}`).join('\n\n');
|
|
1168
|
+
return {
|
|
1169
|
+
content: [{
|
|
1170
|
+
type: 'text',
|
|
1171
|
+
text: `š Mobile Configurations:
|
|
1172
|
+
|
|
1173
|
+
${configList}${layoutsText}${offlineText}
|
|
1174
|
+
|
|
1175
|
+
⨠Found ${configs.length} mobile configuration(s)`
|
|
1176
|
+
}]
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
catch (error) {
|
|
1180
|
+
this.logger.error('Failed to discover mobile configs:', error);
|
|
1181
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover mobile configs: ${error}`);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
async run() {
|
|
1185
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1186
|
+
await this.server.connect(transport);
|
|
1187
|
+
this.logger.info('ServiceNow Flow/Workspace/Mobile MCP Server running on stdio');
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
const server = new ServiceNowFlowWorkspaceMobileMCP();
|
|
1191
|
+
server.run().catch(console.error);
|
|
1192
|
+
//# sourceMappingURL=servicenow-flow-workspace-mobile-mcp.js.map
|