snow-flow 2.6.4 → 2.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/mcp/servicenow-development-assistant-mcp.js +29 -4
  2. package/dist/queen/queen-memory.d.ts +12 -6
  3. package/dist/queen/queen-memory.js +264 -187
  4. package/dist/queen/types.d.ts +1 -0
  5. package/dist/version.d.ts +1 -0
  6. package/dist/version.js +9 -0
  7. package/package.json +1 -1
  8. package/dist/mcp/example-refactored-server.d.ts +0 -18
  9. package/dist/mcp/example-refactored-server.js +0 -57
  10. package/dist/mcp/servicenow-automation-mcp-refactored.d.ts +0 -26
  11. package/dist/mcp/servicenow-automation-mcp-refactored.js +0 -632
  12. package/dist/mcp/servicenow-deployment-mcp-refactored.d.ts +0 -74
  13. package/dist/mcp/servicenow-deployment-mcp-refactored.js +0 -1044
  14. package/dist/mcp/servicenow-flow-composer-mcp-refactored.d.ts +0 -25
  15. package/dist/mcp/servicenow-flow-composer-mcp-refactored.js +0 -565
  16. package/dist/mcp/servicenow-graph-memory-mcp-refactored.d.ts +0 -32
  17. package/dist/mcp/servicenow-graph-memory-mcp-refactored.js +0 -642
  18. package/dist/mcp/servicenow-integration-mcp-refactored.d.ts +0 -24
  19. package/dist/mcp/servicenow-integration-mcp-refactored.js +0 -612
  20. package/dist/mcp/servicenow-intelligent-mcp-refactored.d.ts +0 -63
  21. package/dist/mcp/servicenow-intelligent-mcp-refactored.js +0 -782
  22. package/dist/mcp/servicenow-operations-mcp-refactored.d.ts +0 -63
  23. package/dist/mcp/servicenow-operations-mcp-refactored.js +0 -1770
  24. package/dist/mcp/servicenow-platform-development-mcp-refactored.d.ts +0 -25
  25. package/dist/mcp/servicenow-platform-development-mcp-refactored.js +0 -542
  26. package/dist/mcp/servicenow-reporting-analytics-mcp-refactored.d.ts +0 -26
  27. package/dist/mcp/servicenow-reporting-analytics-mcp-refactored.js +0 -648
  28. package/dist/mcp/servicenow-security-compliance-mcp-refactored.d.ts +0 -25
  29. package/dist/mcp/servicenow-security-compliance-mcp-refactored.js +0 -600
  30. package/dist/mcp/servicenow-update-set-mcp-refactored.d.ts +0 -31
  31. package/dist/mcp/servicenow-update-set-mcp-refactored.js +0 -662
@@ -1,1044 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- /**
4
- * ServiceNow Deployment MCP Server - Agent-Integrated Version
5
- * Provides specialized deployment tools with full agent coordination
6
- * NEW v1.3.1: Complete XML Update Set auto-import with preview and commit controls
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ServiceNowDeploymentMCP = void 0;
10
- const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
11
- const base_mcp_server_js_1 = require("./shared/base-mcp-server.js");
12
- const scope_manager_js_1 = require("../managers/scope-manager.js");
13
- const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
14
- const artifact_tracker_js_1 = require("../utils/artifact-tracker.js");
15
- const fs_1 = require("fs");
16
- class ServiceNowDeploymentMCP extends base_mcp_server_js_1.BaseMCPServer {
17
- constructor() {
18
- super('servicenow-deployment', '2.0.0');
19
- // Initialize scope management
20
- this.scopeManager = new scope_manager_js_1.ScopeManager({
21
- defaultScope: global_scope_strategy_js_1.ScopeType.GLOBAL,
22
- allowFallback: true,
23
- validatePermissions: true,
24
- enableMigration: false
25
- });
26
- this.globalScopeStrategy = new global_scope_strategy_js_1.GlobalScopeStrategy();
27
- // Start artifact tracking session
28
- artifact_tracker_js_1.artifactTracker.startSession();
29
- this.setupHandlers();
30
- }
31
- setupHandlers() {
32
- // Define available tools
33
- this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
34
- tools: [
35
- {
36
- name: 'snow_deploy',
37
- description: 'AGENT-AWARE DEPLOYMENT - Complete deployment workflow with automatic update set management, agent coordination, and memory integration',
38
- inputSchema: {
39
- type: 'object',
40
- properties: {
41
- type: {
42
- type: 'string',
43
- enum: ['widget', 'flow', 'application', 'script', 'business_rule', 'table', 'batch', 'xml_update_set'],
44
- description: 'Type of artifact to deploy (use xml_update_set for XML import)'
45
- },
46
- xml_file_path: {
47
- type: 'string',
48
- description: 'Path to XML update set file (for xml_update_set type)'
49
- },
50
- auto_preview: {
51
- type: 'boolean',
52
- description: 'Automatically preview the update set after import (default: true)',
53
- default: true
54
- },
55
- auto_commit: {
56
- type: 'boolean',
57
- description: 'Automatically commit the update set if preview is clean (default: true)',
58
- default: true
59
- },
60
- instruction: {
61
- type: 'string',
62
- description: 'Natural language instruction for what to create (for flows/widgets)'
63
- },
64
- config: {
65
- type: 'object',
66
- description: 'Artifact configuration (alternative to instruction for direct config)'
67
- },
68
- artifacts: {
69
- type: 'array',
70
- description: 'For batch deployments - array of artifacts',
71
- items: {
72
- type: 'object',
73
- properties: {
74
- type: { type: 'string' },
75
- config: { type: 'object' },
76
- instruction: { type: 'string' }
77
- }
78
- }
79
- },
80
- // Agent context parameters
81
- session_id: {
82
- type: 'string',
83
- description: 'Agent session ID for coordination'
84
- },
85
- agent_id: {
86
- type: 'string',
87
- description: 'Deploying agent ID'
88
- },
89
- agent_type: {
90
- type: 'string',
91
- description: 'Type of agent performing deployment'
92
- },
93
- // Deployment options
94
- auto_update_set: {
95
- type: 'boolean',
96
- description: 'Automatically ensure active Update Set session (default: true)',
97
- default: true
98
- },
99
- fallback_strategy: {
100
- type: 'string',
101
- enum: ['manual_steps', 'update_set_only', 'none'],
102
- description: 'Strategy when direct deployment fails (default: manual_steps)',
103
- default: 'manual_steps'
104
- },
105
- permission_escalation: {
106
- type: 'string',
107
- enum: ['auto_request', 'manual', 'none'],
108
- description: 'How to handle permission errors (default: auto_request)',
109
- default: 'auto_request'
110
- },
111
- deployment_context: {
112
- type: 'string',
113
- description: 'Context for Update Set naming (e.g., "incident widget", "approval flow")'
114
- },
115
- // Batch options
116
- parallel: {
117
- type: 'boolean',
118
- description: 'Deploy batch artifacts in parallel',
119
- default: false
120
- },
121
- transaction_mode: {
122
- type: 'boolean',
123
- description: 'All-or-nothing batch deployment',
124
- default: true
125
- },
126
- dry_run: {
127
- type: 'boolean',
128
- description: 'Validate without deploying',
129
- default: false
130
- }
131
- },
132
- required: ['type']
133
- }
134
- },
135
- {
136
- name: 'snow_deployment_status',
137
- description: 'Check deployment status and history with agent tracking',
138
- inputSchema: {
139
- type: 'object',
140
- properties: {
141
- session_id: { type: 'string', description: 'Session ID to filter by' },
142
- limit: { type: 'number', description: 'Number of recent deployments to show', default: 10 },
143
- }
144
- }
145
- },
146
- {
147
- name: 'snow_validate_deployment',
148
- description: 'Validate a deployment before executing with agent coordination',
149
- inputSchema: {
150
- type: 'object',
151
- properties: {
152
- type: { type: 'string', enum: ['widget', 'workflow', 'application'] },
153
- artifact: { type: 'object', description: 'The artifact to validate' },
154
- session_id: { type: 'string' },
155
- agent_id: { type: 'string' }
156
- },
157
- required: ['type', 'artifact']
158
- }
159
- },
160
- {
161
- name: 'snow_rollback_deployment',
162
- description: 'Rollback a deployment with agent coordination',
163
- inputSchema: {
164
- type: 'object',
165
- properties: {
166
- update_set_id: { type: 'string', description: 'Update set sys_id to rollback' },
167
- reason: { type: 'string', description: 'Reason for rollback' },
168
- session_id: { type: 'string' },
169
- agent_id: { type: 'string' }
170
- },
171
- required: ['update_set_id', 'reason']
172
- }
173
- }
174
- ]
175
- }));
176
- // Handle tool calls
177
- this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => {
178
- const { name, arguments: args } = request.params;
179
- try {
180
- switch (name) {
181
- case 'snow_deploy':
182
- return await this.deployWithAgentCoordination(args);
183
- case 'snow_deployment_status':
184
- return await this.getDeploymentStatusWithContext(args);
185
- case 'snow_validate_deployment':
186
- return await this.validateDeploymentWithContext(args);
187
- case 'snow_rollback_deployment':
188
- return await this.rollbackDeploymentWithContext(args);
189
- default:
190
- throw new Error(`Unknown tool: ${name}`);
191
- }
192
- }
193
- catch (error) {
194
- return this.createErrorResponse(`Tool execution failed: ${name}`, error);
195
- }
196
- });
197
- }
198
- /**
199
- * Main deployment method with full agent integration and intelligent workflow
200
- */
201
- async deployWithAgentCoordination(args) {
202
- return await this.executeWithAgentContext('snow_deploy', args, async (context) => {
203
- // 🔧 STEP 1: MANDATORY Authentication and Connection Validation
204
- this.logger.info('🔍 Step 1: Validating ServiceNow connection...');
205
- const connectionResult = await this.validateServiceNowConnection();
206
- if (!connectionResult.success) {
207
- return this.createAuthenticationError(connectionResult.error);
208
- }
209
- // Assert no mock data
210
- this.assertNoMockData('deployment');
211
- // 🔧 STEP 2: Update Set Management
212
- this.logger.info('📦 Step 2: Ensuring Update Set for tracking...');
213
- await this.reportProgress(context, 15, 'Setting up Update Set');
214
- const purpose = args.type === 'batch' ? 'Batch Deployment' : `${args.type} Deployment`;
215
- const updateSetId = await this.ensureUpdateSet(context, purpose);
216
- if (!updateSetId) {
217
- this.logger.warn('⚠️ No Update Set created - changes will not be tracked');
218
- }
219
- // 🔧 STEP 3: Smart Artifact Discovery (DRY principle)
220
- if (args.type !== 'batch' && (args.config?.name || args.instruction)) {
221
- this.logger.info('🔍 Step 3: Discovering existing artifacts...');
222
- await this.reportProgress(context, 25, 'Checking for existing artifacts');
223
- const artifactName = args.config?.name || this.extractNameFromInstruction(args.instruction);
224
- if (artifactName) {
225
- const discovery = await this.discoverExistingArtifacts(args.type, artifactName, this.extractSearchTermsFromInstruction(args.instruction));
226
- if (discovery.found) {
227
- this.logger.info(`🔍 Found ${discovery.artifacts.length} existing artifacts`);
228
- // Store discovery info in memory for reference
229
- await this.memory.updateSharedContext({
230
- session_id: context.session_id,
231
- context_key: `discovery_${args.type}`,
232
- context_value: JSON.stringify({
233
- artifacts: discovery.artifacts,
234
- suggestions: discovery.suggestions,
235
- timestamp: new Date().toISOString()
236
- }),
237
- created_by_agent: context.agent_id
238
- });
239
- }
240
- }
241
- }
242
- // Get session context for coordination
243
- const sessionContext = await this.getSessionContext(context.session_id);
244
- // Report progress
245
- await this.reportProgress(context, 35, 'Starting deployment');
246
- try {
247
- // Handle different deployment types
248
- let result;
249
- if (args.type === 'batch') {
250
- result = await this.deployBatch(args, context, updateSetId);
251
- }
252
- else if (args.type === 'xml_update_set') {
253
- result = await this.deployXMLUpdateSet(args, context);
254
- }
255
- else {
256
- result = await this.deploySingleArtifact(args, context, updateSetId);
257
- }
258
- // Add discovery information to successful results (from memory context)
259
- try {
260
- const discoveryResults = await this.memory.query('SELECT context_value FROM shared_context WHERE session_id = ? AND context_key = ?', [context.session_id, `discovery_${args.type}`]);
261
- if (discoveryResults.length > 0) {
262
- const discovery = JSON.parse(discoveryResults[0].context_value);
263
- if (discovery.artifacts && discovery.artifacts.length > 0) {
264
- result.content.push({
265
- type: 'text',
266
- text: `\n📋 Artifact Discovery Results:\n${discovery.suggestions?.join('\n') || ''}`
267
- });
268
- }
269
- }
270
- }
271
- catch (discoveryError) {
272
- this.logger.warn('Could not retrieve discovery information', discoveryError);
273
- }
274
- return result;
275
- }
276
- catch (error) {
277
- this.logger.error('Deployment failed, attempting intelligent recovery...', error);
278
- // Use enhanced error handling with fallback options
279
- const fallbackOptions = {
280
- enableRetry: true,
281
- enableScopeEscalation: args.permission_escalation === 'auto_request',
282
- enableManualSteps: args.fallback_strategy === 'manual_steps'
283
- };
284
- const errorResult = await this.handleServiceNowError(error, `${args.type} deployment`, context, fallbackOptions);
285
- // If error handling provided a recovery solution
286
- if (errorResult.content.some(c => c.text?.includes('can_continue: true'))) {
287
- this.logger.info('✅ Error recovered - retrying deployment...');
288
- try {
289
- // Retry the deployment after recovery
290
- await this.reportProgress(context, 85, 'Retrying after error recovery');
291
- let retryResult;
292
- if (args.type === 'batch') {
293
- retryResult = await this.deployBatch(args, context, updateSetId);
294
- }
295
- else {
296
- retryResult = await this.deploySingleArtifact(args, context, updateSetId);
297
- }
298
- // Add recovery note to success result
299
- retryResult.content.push({
300
- type: 'text',
301
- text: '\n✅ Deployment succeeded after automatic error recovery'
302
- });
303
- return retryResult;
304
- }
305
- catch (retryError) {
306
- this.logger.error('Retry after recovery also failed', retryError);
307
- // Request Queen intervention for persistent failures
308
- await this.requestQueenIntervention(context, {
309
- type: 'deployment_failure_persistent',
310
- priority: 'critical',
311
- description: `Failed to deploy ${args.type} even after error recovery: ${retryError instanceof Error ? retryError.message : String(retryError)}`,
312
- attempted_solutions: ['direct_deployment', 'error_recovery', 'retry_after_recovery']
313
- });
314
- return errorResult; // Return the error recovery guidance
315
- }
316
- }
317
- // If no automatic recovery possible, request Queen intervention
318
- await this.requestQueenIntervention(context, {
319
- type: 'deployment_failure',
320
- priority: 'high',
321
- description: `Failed to deploy ${args.type}: ${error instanceof Error ? error.message : String(error)}`,
322
- attempted_solutions: ['direct_deployment', 'intelligent_error_handling']
323
- });
324
- return errorResult; // Return the error recovery guidance instead of throwing
325
- }
326
- });
327
- }
328
- /**
329
- * Deploy a single artifact with memory tracking
330
- */
331
- async deploySingleArtifact(args, context, updateSetId) {
332
- const { type, config, instruction } = args;
333
- // Report planning phase
334
- await this.reportProgress(context, 20, 'Planning deployment');
335
- // Ensure update set if requested
336
- let finalUpdateSetId = updateSetId;
337
- if (args.auto_update_set !== false && !finalUpdateSetId) {
338
- finalUpdateSetId = await this.ensureUpdateSet(context, config?.name || 'artifact');
339
- await this.reportProgress(context, 30, 'Update set ready');
340
- }
341
- // Deploy based on type
342
- let result;
343
- await this.reportProgress(context, 50, `Deploying ${type}`);
344
- switch (type) {
345
- case 'widget':
346
- result = await this.deployWidget(config, context, finalUpdateSetId);
347
- break;
348
- case 'flow':
349
- result = await this.deployFlow(config || { instruction }, context, finalUpdateSetId);
350
- break;
351
- case 'application':
352
- result = await this.deployApplication(config, context, finalUpdateSetId);
353
- break;
354
- case 'script':
355
- case 'business_rule':
356
- result = await this.deployScript(type, config, context, finalUpdateSetId);
357
- break;
358
- case 'xml_update_set':
359
- result = await this.deployXMLUpdateSet(config, context);
360
- break;
361
- default:
362
- throw new Error(`Unsupported deployment type: ${type}`);
363
- }
364
- // Store artifact in memory
365
- await this.storeArtifact(context, {
366
- sys_id: result.sys_id,
367
- type,
368
- name: result.name || config?.name || 'unnamed',
369
- description: config?.description,
370
- config,
371
- update_set_id: finalUpdateSetId
372
- });
373
- // Record deployment in history
374
- await this.memory.recordDeployment(context.session_id, result.sys_id, type, true, context.agent_id);
375
- await this.reportProgress(context, 90, 'Finalizing deployment');
376
- // Notify next agent if needed
377
- if (result.next_agent) {
378
- await this.notifyHandoff(context, result.next_agent, {
379
- type,
380
- sys_id: result.sys_id,
381
- next_steps: result.next_steps || []
382
- });
383
- }
384
- await this.reportProgress(context, 100, 'Deployment complete');
385
- return this.createSuccessResponse(`Successfully deployed ${type}: ${result.name}`, {
386
- sys_id: result.sys_id,
387
- name: result.name,
388
- update_set_id: finalUpdateSetId,
389
- deployment_details: result
390
- }, {
391
- agent_id: context.agent_id,
392
- session_id: context.session_id,
393
- artifacts_created: [result.sys_id]
394
- });
395
- }
396
- /**
397
- * Deploy multiple artifacts in batch
398
- */
399
- async deployBatch(args, context, updateSetId) {
400
- const { artifacts, parallel, transaction_mode, dry_run } = args;
401
- if (!artifacts || !Array.isArray(artifacts)) {
402
- throw new Error('Batch deployment requires artifacts array');
403
- }
404
- await this.reportProgress(context, 10, `Preparing batch deployment of ${artifacts.length} artifacts`);
405
- // Dry run validation
406
- if (dry_run) {
407
- const validationResults = await Promise.all(artifacts.map(artifact => this.validateArtifact(artifact, context)));
408
- return this.createSuccessResponse('Dry run completed - all artifacts validated', { validationResults });
409
- }
410
- // Create update set for batch
411
- const batchUpdateSetId = await this.ensureUpdateSet(context, `Batch deployment`);
412
- const results = [];
413
- const errors = [];
414
- try {
415
- if (parallel) {
416
- // Deploy in parallel
417
- await this.reportProgress(context, 30, 'Deploying artifacts in parallel');
418
- const deploymentPromises = artifacts.map((artifact, index) => this.deploySingleArtifact({ ...artifact, auto_update_set: false }, context).catch(error => {
419
- errors.push({ artifact, error: error.message });
420
- return null;
421
- }));
422
- const parallelResults = await Promise.all(deploymentPromises);
423
- results.push(...parallelResults.filter(r => r !== null));
424
- }
425
- else {
426
- // Deploy sequentially
427
- for (let i = 0; i < artifacts.length; i++) {
428
- const artifact = artifacts[i];
429
- await this.reportProgress(context, 30 + (60 * i / artifacts.length), `Deploying artifact ${i + 1} of ${artifacts.length}`);
430
- try {
431
- const result = await this.deploySingleArtifact({ ...artifact, auto_update_set: false }, context);
432
- results.push(result);
433
- }
434
- catch (error) {
435
- errors.push({ artifact, error: error instanceof Error ? error.message : String(error) });
436
- if (transaction_mode) {
437
- // Rollback on error in transaction mode
438
- await this.rollbackBatch(results, batchUpdateSetId, context);
439
- throw new Error(`Batch deployment failed at artifact ${i + 1}: ${error}`);
440
- }
441
- }
442
- }
443
- }
444
- // Check if we need to rollback
445
- if (transaction_mode && errors.length > 0) {
446
- await this.rollbackBatch(results, batchUpdateSetId, context);
447
- throw new Error(`Batch deployment failed with ${errors.length} errors`);
448
- }
449
- await this.reportProgress(context, 100, 'Batch deployment complete');
450
- return this.createSuccessResponse(`Batch deployment completed: ${results.length} succeeded, ${errors.length} failed`, {
451
- successful: results.length,
452
- failed: errors.length,
453
- errors: errors.length > 0 ? errors : undefined,
454
- update_set_id: batchUpdateSetId
455
- });
456
- }
457
- catch (error) {
458
- // Record failed deployment
459
- await this.memory.recordDeployment(context.session_id, 'batch_deployment', 'batch', false, context.agent_id, error instanceof Error ? error.message : String(error));
460
- throw error;
461
- }
462
- }
463
- /**
464
- * Deploy XML Update Set to ServiceNow
465
- */
466
- async deployXMLUpdateSet(args, context) {
467
- const { xml_file_path, auto_preview = true, auto_commit = true } = args;
468
- if (!xml_file_path) {
469
- throw new Error('XML file path is required for xml_update_set deployment');
470
- }
471
- this.logger.info('🚀 Deploying XML Update Set', {
472
- file: xml_file_path,
473
- auto_preview,
474
- auto_commit
475
- });
476
- try {
477
- // Read XML file
478
- await this.reportProgress(context, 20, 'Reading XML file');
479
- const xmlContent = await fs_1.promises.readFile(xml_file_path, 'utf-8');
480
- // Import XML as remote update set
481
- await this.reportProgress(context, 40, 'Importing XML to ServiceNow');
482
- const importResponse = await this.client.makeRequest({
483
- method: 'POST',
484
- url: '/api/now/table/sys_remote_update_set',
485
- headers: {
486
- 'Content-Type': 'application/xml',
487
- 'Accept': 'application/json'
488
- },
489
- data: xmlContent
490
- });
491
- if (!importResponse.result || !importResponse.result.sys_id) {
492
- throw new Error('Failed to import XML update set');
493
- }
494
- const remoteUpdateSetId = importResponse.result.sys_id;
495
- this.logger.info('✅ XML imported successfully', { sys_id: remoteUpdateSetId });
496
- // Load the update set
497
- await this.reportProgress(context, 60, 'Loading update set');
498
- await this.client.makeRequest({
499
- method: 'PUT',
500
- url: `/api/now/table/sys_remote_update_set/${remoteUpdateSetId}`,
501
- data: {
502
- state: 'loaded'
503
- }
504
- });
505
- // Find the loaded update set
506
- const loadedResponse = await this.client.makeRequest({
507
- method: 'GET',
508
- url: '/api/now/table/sys_update_set',
509
- params: {
510
- sysparm_query: `remote_sys_id=${remoteUpdateSetId}`,
511
- sysparm_limit: 1
512
- }
513
- });
514
- if (!loadedResponse.result || loadedResponse.result.length === 0) {
515
- throw new Error('Failed to find loaded update set');
516
- }
517
- const updateSetId = loadedResponse.result[0].sys_id;
518
- const updateSetName = loadedResponse.result[0].name;
519
- // Preview if requested
520
- if (auto_preview) {
521
- await this.reportProgress(context, 80, 'Previewing update set');
522
- const previewResponse = await this.client.makeRequest({
523
- method: 'POST',
524
- url: `/api/now/table/sys_update_set/${updateSetId}/preview`
525
- });
526
- // Check preview results
527
- const previewProblems = await this.client.makeRequest({
528
- method: 'GET',
529
- url: '/api/now/table/sys_update_preview_problem',
530
- params: {
531
- sysparm_query: `update_set=${updateSetId}`,
532
- sysparm_limit: 100
533
- }
534
- });
535
- if (previewProblems.result && previewProblems.result.length > 0) {
536
- const problems = previewProblems.result.map((p) => `- ${p.type}: ${p.description}`).join('\n');
537
- if (auto_commit) {
538
- this.logger.warn('Preview found problems, skipping auto-commit', { problems });
539
- }
540
- return this.createSuccessResponse('XML imported and previewed with problems', {
541
- update_set_id: updateSetId,
542
- update_set_name: updateSetName,
543
- preview_status: 'problems_found',
544
- problems: previewProblems.result,
545
- next_steps: [
546
- '1. Review preview problems in ServiceNow',
547
- '2. Resolve any issues',
548
- '3. Commit manually when ready'
549
- ]
550
- });
551
- }
552
- // Commit if clean and requested
553
- if (auto_commit) {
554
- await this.reportProgress(context, 95, 'Committing update set');
555
- await this.client.makeRequest({
556
- method: 'POST',
557
- url: `/api/now/table/sys_update_set/${updateSetId}/commit`
558
- });
559
- return this.createSuccessResponse('✅ XML Update Set imported, previewed, and committed successfully!', {
560
- update_set_id: updateSetId,
561
- update_set_name: updateSetName,
562
- status: 'committed',
563
- flow_location: 'Flow Designer > Designer',
564
- next_steps: [
565
- '1. Navigate to Flow Designer',
566
- '2. Your flow should be visible in the list',
567
- '3. Open the flow to verify all components'
568
- ]
569
- });
570
- }
571
- }
572
- // Return success without preview/commit
573
- return this.createSuccessResponse('XML Update Set imported successfully', {
574
- update_set_id: updateSetId,
575
- update_set_name: updateSetName,
576
- status: 'imported',
577
- next_steps: [
578
- '1. Navigate to System Update Sets > Local Update Sets',
579
- '2. Find your update set: ' + updateSetName,
580
- '3. Click Preview Update Set',
581
- '4. Review and commit when ready'
582
- ]
583
- });
584
- }
585
- catch (error) {
586
- const errorMsg = error instanceof Error ? error.message : String(error);
587
- if (errorMsg.includes('ENOENT')) {
588
- throw new Error(`XML file not found: ${xml_file_path}`);
589
- }
590
- throw new Error(`Failed to deploy XML update set: ${errorMsg}`);
591
- }
592
- }
593
- /**
594
- * Deploy widget with agent tracking and intelligent error handling
595
- */
596
- async deployWidget(config, context, updateSetId) {
597
- this.logger.info('Deploying widget', {
598
- name: config.name,
599
- agent_id: context.agent_id
600
- });
601
- try {
602
- // Validate widget configuration first
603
- const validationErrors = this.validateWidgetConfig(config);
604
- if (validationErrors.length > 0) {
605
- throw new Error(`Widget validation failed: ${validationErrors.join(', ')}`);
606
- }
607
- // Create widget in ServiceNow
608
- const widgetData = {
609
- name: config.name,
610
- id: config.name,
611
- template: config.template || '<div>Widget Template</div>',
612
- css: config.css || '',
613
- server_script: config.server_script || '',
614
- client_script: config.client_script || '',
615
- public: false,
616
- roles: '',
617
- active: true
618
- };
619
- const response = await this.client.createRecord('sp_widget', widgetData);
620
- if (!response.success || !response.result) {
621
- // Try to provide more specific error information
622
- const errorMsg = response.error || 'Unknown error creating widget';
623
- // Check for common widget creation issues
624
- if (errorMsg.includes('duplicate') || errorMsg.includes('already exists')) {
625
- throw new Error(`Widget '${config.name}' already exists. Consider using a different name or updating the existing widget.`);
626
- }
627
- if (errorMsg.includes('permission') || errorMsg.includes('access') || errorMsg.includes('403')) {
628
- throw new Error(`
629
- 🚫 Service Portal Permission Error (403)
630
-
631
- Even with sp_admin role, this can happen due to:
632
-
633
- 1. **OAuth Scope Restrictions**
634
- - Go to: System OAuth > Application Registry
635
- - Set "Accessible from" to "All application scopes"
636
-
637
- 2. **Cross-Scope Access**
638
- - Switch to Global scope and try again
639
- - Check ACLs on sp_widget table
640
-
641
- 3. **Missing System Properties**
642
- - Ensure: glide.service_portal.enable_api_access = true
643
-
644
- 4. **Update Set Required**
645
- - Widgets must be created in an active Update Set
646
- - Use: snow_ensure_active_update_set() first
647
-
648
- 🔧 Quick Fix: Try deployment with global scope:
649
- await snow_deploy({
650
- type: "widget",
651
- config: {...},
652
- scope_preference: "global"
653
- });
654
-
655
- 📚 See SERVICE_PORTAL_403_FIX.md for detailed troubleshooting.
656
- `);
657
- }
658
- throw new Error(`Failed to create widget in ServiceNow: ${errorMsg}`);
659
- }
660
- const result = response.result;
661
- const widgetSysId = Array.isArray(result) ? result[0]?.sys_id : result?.sys_id;
662
- // 🔧 Track artifact in Update Set
663
- if (updateSetId) {
664
- try {
665
- await this.trackArtifact(widgetSysId, 'widget', config.name, updateSetId);
666
- }
667
- catch (trackingError) {
668
- this.logger.warn('Update Set tracking failed (widget created but not tracked)', trackingError);
669
- // Don't fail the deployment, just warn
670
- }
671
- }
672
- // Determine next agent based on widget complexity
673
- let nextAgent;
674
- let nextSteps = [];
675
- if (config.requires_styling || config.template?.includes('class=')) {
676
- nextAgent = 'ui_specialist';
677
- nextSteps = ['responsive_styling', 'accessibility_compliance'];
678
- }
679
- else if (config.requires_testing) {
680
- nextAgent = 'test_agent';
681
- nextSteps = ['functional_testing', 'performance_testing'];
682
- }
683
- // Log successful deployment
684
- this.logger.info(`✅ Widget deployed successfully: ${config.name} (${widgetSysId})`);
685
- return {
686
- sys_id: widgetSysId,
687
- name: config.name,
688
- table: 'sp_widget',
689
- next_agent: nextAgent,
690
- next_steps: nextSteps,
691
- success: true
692
- };
693
- }
694
- catch (error) {
695
- this.logger.error('Widget deployment failed', error);
696
- // Use intelligent error handling for specific recovery guidance
697
- const errorResult = await this.handleServiceNowError(error, 'widget deployment', context, {
698
- enableRetry: true,
699
- enableScopeEscalation: true,
700
- enableManualSteps: true
701
- });
702
- // Re-throw with enhanced error information for higher-level handling
703
- const enhancedError = new Error(`Widget deployment failed: ${error instanceof Error ? error.message : String(error)}`);
704
- enhancedError.errorRecoveryGuidance = errorResult;
705
- throw enhancedError;
706
- }
707
- }
708
- /**
709
- * Validate widget configuration before deployment
710
- */
711
- validateWidgetConfig(config) {
712
- const errors = [];
713
- if (!config.name) {
714
- errors.push('Widget name is required');
715
- }
716
- else {
717
- // Check for valid widget naming
718
- if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(config.name)) {
719
- errors.push('Widget name must start with letter and contain only letters, numbers, and underscores');
720
- }
721
- if (config.name.length > 40) {
722
- errors.push('Widget name must be 40 characters or less');
723
- }
724
- }
725
- if (!config.template) {
726
- errors.push('Widget template is required');
727
- }
728
- else {
729
- // Basic HTML validation
730
- if (config.template.length > 100000) {
731
- errors.push('Widget template is too large (max 100KB)');
732
- }
733
- }
734
- // Validate scripts if provided
735
- if (config.server_script && config.server_script.length > 1000000) {
736
- errors.push('Server script is too large (max 1MB)');
737
- }
738
- if (config.client_script && config.client_script.length > 1000000) {
739
- errors.push('Client script is too large (max 1MB)');
740
- }
741
- return errors;
742
- }
743
- /**
744
- * Deploy flow with agent tracking
745
- */
746
- async deployFlow(config, context, updateSetId) {
747
- // For flows with natural language instructions, delegate to flow composer
748
- if (config.instruction) {
749
- // Store instruction in shared context for flow builder agent
750
- await this.memory.updateSharedContext({
751
- session_id: context.session_id,
752
- context_key: 'flow_instruction',
753
- context_value: config.instruction,
754
- created_by_agent: context.agent_id
755
- });
756
- return {
757
- sys_id: `pending_flow_${Date.now()}`,
758
- name: 'Flow from instruction',
759
- instruction: config.instruction,
760
- next_agent: 'flow_builder',
761
- next_steps: ['analyze_requirements', 'create_flow_structure', 'deploy_flow']
762
- };
763
- }
764
- // Direct flow deployment
765
- const flowData = {
766
- name: config.name,
767
- description: config.description,
768
- active: config.active !== false,
769
- sys_scope: 'global'
770
- };
771
- const response = await this.client.createRecord('sys_hub_flow', flowData);
772
- if (!response.success || !response.result) {
773
- throw new Error('Failed to create flow in ServiceNow');
774
- }
775
- const result = response.result;
776
- return {
777
- sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
778
- name: config.name,
779
- table: 'sys_hub_flow'
780
- };
781
- }
782
- /**
783
- * Deploy application with scope management
784
- */
785
- async deployApplication(config, context, updateSetId) {
786
- // Determine deployment scope (simplified)
787
- const scopeStrategy = config.scope_strategy || 'auto';
788
- const targetScope = scopeStrategy === 'auto' ? 'global' : (config.scope || 'global');
789
- const appData = {
790
- name: config.name,
791
- scope: targetScope,
792
- short_description: config.short_description,
793
- version: config.version,
794
- vendor: config.vendor || 'Custom',
795
- vendor_prefix: config.vendor_prefix || 'x',
796
- active: config.active !== false
797
- };
798
- const response = await this.client.createRecord('sys_app', appData);
799
- if (!response.success || !response.result) {
800
- throw new Error('Failed to create application in ServiceNow');
801
- }
802
- const result = response.result;
803
- return {
804
- sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
805
- name: config.name,
806
- scope: targetScope,
807
- table: 'sys_app'
808
- };
809
- }
810
- /**
811
- * Deploy script or business rule
812
- */
813
- async deployScript(type, config, context, updateSetId) {
814
- const table = type === 'script' ? 'sys_script' : 'sys_script';
815
- const scriptData = {
816
- name: config.name,
817
- script: config.script,
818
- active: config.active !== false,
819
- description: config.description
820
- };
821
- const response = await this.client.createRecord(table, scriptData);
822
- if (!response.success || !response.result) {
823
- throw new Error(`Failed to create ${type} in ServiceNow`);
824
- }
825
- // Scripts often need testing
826
- const result = response.result;
827
- return {
828
- sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
829
- name: config.name,
830
- table,
831
- next_agent: 'test_agent',
832
- next_steps: ['syntax_validation', 'unit_testing']
833
- };
834
- }
835
- /**
836
- * Get deployment status with session filtering
837
- */
838
- async getDeploymentStatusWithContext(args) {
839
- return await this.executeWithAgentContext('snow_deployment_status', args, async (context) => {
840
- const session_id = args.session_id || context.session_id;
841
- // Get artifacts from memory
842
- const artifacts = await this.memory.getSessionArtifacts(session_id);
843
- // Get active agents
844
- const activeAgents = await this.memory.query(`
845
- SELECT * FROM agent_coordination
846
- WHERE session_id = ?
847
- ORDER BY last_activity DESC
848
- `, [session_id]);
849
- // Get recent deployments
850
- const deployments = await this.memory.query(`
851
- SELECT * FROM deployment_history
852
- WHERE session_id = ?
853
- ORDER BY deployment_time DESC
854
- LIMIT ?
855
- `, [session_id, args.limit || 10]);
856
- return this.createSuccessResponse(`Deployment status for session ${session_id}`, {
857
- artifacts: artifacts.length,
858
- active_agents: activeAgents.length,
859
- recent_deployments: deployments,
860
- session_summary: {
861
- total_artifacts: artifacts.length,
862
- artifact_types: this.groupByType(artifacts),
863
- success_rate: this.calculateSuccessRate(deployments)
864
- }
865
- });
866
- });
867
- }
868
- /**
869
- * Validate deployment with agent context
870
- */
871
- async validateDeploymentWithContext(args) {
872
- return await this.executeWithAgentContext('snow_validate_deployment', args, async (context) => {
873
- const { type, artifact } = args;
874
- const validationResult = await this.validateArtifact({ type, config: artifact }, context);
875
- if (validationResult.valid) {
876
- return this.createSuccessResponse(`Validation passed for ${type}`, validationResult);
877
- }
878
- else {
879
- // Store validation failure in context
880
- await this.memory.updateSharedContext({
881
- session_id: context.session_id,
882
- context_key: `validation_failure_${type}`,
883
- context_value: JSON.stringify(validationResult),
884
- created_by_agent: context.agent_id
885
- });
886
- return this.createErrorResponse(`Validation failed for ${type}`, validationResult.errors);
887
- }
888
- });
889
- }
890
- /**
891
- * Rollback deployment with coordination
892
- */
893
- async rollbackDeploymentWithContext(args) {
894
- return await this.executeWithAgentContext('snow_rollback_deployment', args, async (context) => {
895
- const { update_set_id, reason } = args;
896
- // Notify all agents about rollback
897
- await this.memory.sendAgentMessage({
898
- session_id: context.session_id,
899
- from_agent: context.agent_id,
900
- to_agent: 'all',
901
- message_type: 'status_update',
902
- content: JSON.stringify({
903
- action: 'rollback_initiated',
904
- update_set_id,
905
- reason
906
- })
907
- });
908
- // Perform rollback
909
- const rollbackResult = await this.client.updateRecord('sys_update_set', update_set_id, {
910
- state: 'reverted',
911
- description: `Rolled back: ${reason}`
912
- });
913
- if (rollbackResult.success) {
914
- // Update deployment history
915
- await this.memory.recordDeployment(context.session_id, update_set_id, 'rollback', true, context.agent_id);
916
- return this.createSuccessResponse(`Successfully rolled back update set ${update_set_id}`, { reason });
917
- }
918
- else {
919
- throw new Error('Rollback failed');
920
- }
921
- });
922
- }
923
- // Helper methods
924
- async validateArtifact(artifact, context) {
925
- // Simplified validation logic
926
- const errors = [];
927
- if (!artifact.type)
928
- errors.push('Missing artifact type');
929
- if (!artifact.config && !artifact.instruction)
930
- errors.push('Missing configuration or instruction');
931
- if (artifact.type === 'widget') {
932
- if (!artifact.config?.name)
933
- errors.push('Widget name is required');
934
- if (!artifact.config?.template)
935
- errors.push('Widget template is required');
936
- }
937
- return {
938
- valid: errors.length === 0,
939
- errors,
940
- warnings: []
941
- };
942
- }
943
- async rollbackBatch(results, updateSetId, context) {
944
- // Rollback logic for batch deployments
945
- for (const result of results) {
946
- if (result?.data?.sys_id) {
947
- try {
948
- await this.client.deleteRecord(result.data.table || 'sys_metadata', result.data.sys_id);
949
- }
950
- catch (error) {
951
- this.logger.error('Failed to rollback artifact', error);
952
- }
953
- }
954
- }
955
- }
956
- groupByType(artifacts) {
957
- return artifacts.reduce((acc, artifact) => {
958
- acc[artifact.artifact_type] = (acc[artifact.artifact_type] || 0) + 1;
959
- return acc;
960
- }, {});
961
- }
962
- calculateSuccessRate(deployments) {
963
- if (deployments.length === 0)
964
- return 0;
965
- const successful = deployments.filter(d => d.success).length;
966
- return Math.round((successful / deployments.length) * 100);
967
- }
968
- /**
969
- * Extract artifact name from natural language instruction
970
- */
971
- extractNameFromInstruction(instruction) {
972
- if (!instruction)
973
- return null;
974
- // Look for patterns like "create a widget called X" or "make a flow named Y"
975
- const patterns = [
976
- /(?:create|make|build)\s+(?:a|an)?\s*\w+\s+(?:called|named|for)\s+["']?([^"']+)["']?/i,
977
- /(?:create|make|build)\s+["']([^"']+)["']\s+\w+/i,
978
- /(?:widget|flow|script|rule|table|application)\s+(?:called|named|for)\s+["']?([^"']+)["']?/i,
979
- /"([^"]+)"\s+(?:widget|flow|script|rule|table|application)/i,
980
- /'([^']+)'\s+(?:widget|flow|script|rule|table|application)/i
981
- ];
982
- for (const pattern of patterns) {
983
- const match = instruction.match(pattern);
984
- if (match && match[1]) {
985
- return match[1].trim();
986
- }
987
- }
988
- // Fallback: look for quoted text that might be a name
989
- const quotedMatch = instruction.match(/["']([^"']{3,30})["']/);
990
- if (quotedMatch && quotedMatch[1]) {
991
- return quotedMatch[1].trim();
992
- }
993
- return null;
994
- }
995
- /**
996
- * Extract search terms from instruction for discovery
997
- */
998
- extractSearchTermsFromInstruction(instruction) {
999
- if (!instruction)
1000
- return [];
1001
- const terms = [];
1002
- // Common keywords that might indicate similar functionality
1003
- const keywords = [
1004
- 'incident', 'dashboard', 'report', 'approval', 'notification', 'user', 'profile',
1005
- 'management', 'tracking', 'monitoring', 'analytics', 'workflow', 'request',
1006
- 'ticket', 'service', 'catalog', 'portal', 'form', 'table', 'list', 'chart',
1007
- 'graph', 'widget', 'flow', 'automation', 'integration', 'api', 'rest',
1008
- 'email', 'alert', 'escalation', 'assignment', 'routing', 'sla', 'metric'
1009
- ];
1010
- const lowercaseInstruction = instruction.toLowerCase();
1011
- // Extract keywords that appear in the instruction
1012
- for (const keyword of keywords) {
1013
- if (lowercaseInstruction.includes(keyword)) {
1014
- terms.push(keyword);
1015
- }
1016
- }
1017
- // Extract quoted terms
1018
- const quotedTerms = instruction.match(/["']([^"']{2,20})["']/g);
1019
- if (quotedTerms) {
1020
- quotedTerms.forEach(quoted => {
1021
- const term = quoted.replace(/["']/g, '').trim();
1022
- if (term.length >= 2 && !terms.includes(term.toLowerCase())) {
1023
- terms.push(term.toLowerCase());
1024
- }
1025
- });
1026
- }
1027
- // Extract important words (3+ characters, not common words)
1028
- const words = lowercaseInstruction.match(/\b[a-z]{3,}\b/g) || [];
1029
- const commonWords = ['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'man', 'new', 'now', 'old', 'see', 'two', 'who', 'boy', 'did', 'its', 'let', 'put', 'say', 'she', 'too', 'use'];
1030
- for (const word of words) {
1031
- if (!commonWords.includes(word) && word.length >= 3 && !terms.includes(word)) {
1032
- terms.push(word);
1033
- }
1034
- }
1035
- return terms.slice(0, 5); // Limit to 5 terms to avoid too many searches
1036
- }
1037
- }
1038
- exports.ServiceNowDeploymentMCP = ServiceNowDeploymentMCP;
1039
- // Start the server
1040
- if (require.main === module) {
1041
- const server = new ServiceNowDeploymentMCP();
1042
- server.start().catch(console.error);
1043
- }
1044
- //# sourceMappingURL=servicenow-deployment-mcp-refactored.js.map