snow-flow 2.9.0 → 2.9.5
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/check-mcp-resources.sh +85 -0
- package/dist/config/snow-flow-config.js +1 -1
- package/dist/mcp/mcp-on-demand-proxy.d.ts +7 -0
- package/dist/mcp/mcp-on-demand-proxy.js +187 -0
- package/dist/mcp/servicenow-deployment-mcp.js +132 -55
- package/dist/mcp/servicenow-machine-learning-mcp.js +310 -60
- package/dist/mcp/servicenow-operations-mcp.d.ts +71 -1
- package/dist/mcp/servicenow-operations-mcp.js +10 -6
- package/dist/test-smart-limits.d.ts +13 -0
- package/dist/test-smart-limits.js +100 -0
- package/dist/utils/deployment-auth-fix.d.ts +43 -0
- package/dist/utils/deployment-auth-fix.js +272 -0
- package/dist/utils/mcp-on-demand-manager.d.ts +69 -0
- package/dist/utils/mcp-on-demand-manager.js +309 -0
- package/dist/utils/mcp-process-manager.d.ts +58 -0
- package/dist/utils/mcp-process-manager.js +220 -0
- package/dist/utils/mcp-server-manager.js +50 -9
- package/dist/utils/mcp-singleton-lock.d.ts +4 -0
- package/dist/utils/mcp-singleton-lock.js +35 -8
- package/dist/utils/ml-data-fetcher.d.ts +66 -0
- package/dist/utils/ml-data-fetcher.js +288 -0
- package/dist/utils/servicenow-client.js +43 -17
- package/dist/utils/timeout-manager.d.ts +62 -0
- package/dist/utils/timeout-manager.js +352 -0
- package/package.json +1 -1
- package/test-ml-improvements.sh +76 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "🔍 Checking MCP Server Resources"
|
|
4
|
+
echo "================================="
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
# Count MCP processes
|
|
8
|
+
echo "📊 MCP Process Count:"
|
|
9
|
+
MCP_COUNT=$(ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep | wc -l)
|
|
10
|
+
echo " Active MCP processes: $MCP_COUNT"
|
|
11
|
+
echo ""
|
|
12
|
+
|
|
13
|
+
# Show MCP processes with memory usage
|
|
14
|
+
echo "💾 MCP Process Memory Usage:"
|
|
15
|
+
ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep | awk '{printf " PID: %5s | Memory: %6s MB | CPU: %5s%% | Process: %s\n", $2, int($6/1024), $3, substr($0, index($0,$11))}'
|
|
16
|
+
echo ""
|
|
17
|
+
|
|
18
|
+
# Total memory used by MCP
|
|
19
|
+
echo "📈 Total Memory Statistics:"
|
|
20
|
+
TOTAL_MEM=$(ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep | awk '{sum += $6} END {print int(sum/1024)}')
|
|
21
|
+
echo " Total MCP memory usage: ${TOTAL_MEM:-0} MB"
|
|
22
|
+
|
|
23
|
+
# System memory
|
|
24
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
25
|
+
# macOS
|
|
26
|
+
TOTAL_SYSTEM_MEM=$(sysctl -n hw.memsize | awk '{print int($1/1024/1024)}')
|
|
27
|
+
echo " Total system memory: $TOTAL_SYSTEM_MEM MB"
|
|
28
|
+
else
|
|
29
|
+
# Linux
|
|
30
|
+
TOTAL_SYSTEM_MEM=$(free -m | awk '/^Mem:/{print $2}')
|
|
31
|
+
echo " Total system memory: $TOTAL_SYSTEM_MEM MB"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "⚠️ Warning Thresholds:"
|
|
36
|
+
if [ "$MCP_COUNT" -gt 10 ]; then
|
|
37
|
+
echo " ❌ Too many MCP processes ($MCP_COUNT > 10) - This will cause timeouts!"
|
|
38
|
+
echo " 💡 Run: pkill -f mcp"
|
|
39
|
+
else
|
|
40
|
+
echo " ✅ MCP process count OK ($MCP_COUNT <= 10)"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [ "${TOTAL_MEM:-0}" -gt 1500 ]; then
|
|
44
|
+
echo " ❌ High memory usage (${TOTAL_MEM}MB > 1500MB) - This will cause timeouts!"
|
|
45
|
+
echo " 💡 Run: npm run cleanup-mcp"
|
|
46
|
+
else
|
|
47
|
+
echo " ✅ Memory usage OK (${TOTAL_MEM:-0}MB <= 1500MB)"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo ""
|
|
51
|
+
echo "🔧 Node.js Processes:"
|
|
52
|
+
NODE_COUNT=$(ps aux | grep -E "node.*snow|node.*servicenow" | grep -v grep | wc -l)
|
|
53
|
+
echo " Active Node processes: $NODE_COUNT"
|
|
54
|
+
|
|
55
|
+
# Check for zombie processes
|
|
56
|
+
echo ""
|
|
57
|
+
echo "👻 Zombie/Defunct Processes:"
|
|
58
|
+
ZOMBIE_COUNT=$(ps aux | grep defunct | grep -v grep | wc -l)
|
|
59
|
+
if [ "$ZOMBIE_COUNT" -gt 0 ]; then
|
|
60
|
+
echo " ⚠️ Found $ZOMBIE_COUNT zombie processes"
|
|
61
|
+
ps aux | grep defunct | grep -v grep
|
|
62
|
+
else
|
|
63
|
+
echo " ✅ No zombie processes found"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
echo ""
|
|
67
|
+
echo "📋 Recommendations:"
|
|
68
|
+
echo ""
|
|
69
|
+
|
|
70
|
+
if [ "$MCP_COUNT" -gt 10 ] || [ "${TOTAL_MEM:-0}" -gt 1500 ]; then
|
|
71
|
+
echo " 🚨 RESOURCE ISSUES DETECTED!"
|
|
72
|
+
echo ""
|
|
73
|
+
echo " Quick fix:"
|
|
74
|
+
echo " 1. pkill -f mcp"
|
|
75
|
+
echo " 2. npm run cleanup-mcp"
|
|
76
|
+
echo " 3. npm run mcp:start"
|
|
77
|
+
echo ""
|
|
78
|
+
echo " Permanent fix:"
|
|
79
|
+
echo " 1. Add to .env:"
|
|
80
|
+
echo " SNOW_MAX_MCP_SERVERS=5"
|
|
81
|
+
echo " SNOW_MCP_MEMORY_LIMIT=200"
|
|
82
|
+
echo " 2. Restart snow-flow"
|
|
83
|
+
else
|
|
84
|
+
echo " ✅ Resources look healthy"
|
|
85
|
+
fi
|
|
@@ -197,7 +197,7 @@ const ConfigSchema = zod_1.z.object({
|
|
|
197
197
|
password: zod_1.z.string().optional(),
|
|
198
198
|
authType: zod_1.z.enum(['oauth', 'basic']).default('oauth'),
|
|
199
199
|
apiVersion: zod_1.z.string().default('now'),
|
|
200
|
-
timeout: zod_1.z.number().min(5000).default(
|
|
200
|
+
timeout: zod_1.z.number().min(5000).default(120000), // Increased from 60s to 120s
|
|
201
201
|
retryConfig: zod_1.z
|
|
202
202
|
.object({
|
|
203
203
|
maxRetries: zod_1.z.number().min(0).max(10).default(3),
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* MCP On-Demand Proxy
|
|
5
|
+
* Routes MCP requests to the appropriate server, starting it if needed
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
9
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
10
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
11
|
+
const mcp_on_demand_manager_js_1 = require("../utils/mcp-on-demand-manager.js");
|
|
12
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
13
|
+
const logger = new logger_js_1.Logger('MCPProxy');
|
|
14
|
+
// Tool to server mapping
|
|
15
|
+
const TOOL_SERVER_MAP = {
|
|
16
|
+
// Operations tools
|
|
17
|
+
'snow_query_table': 'servicenow-operations',
|
|
18
|
+
'snow_query_incidents': 'servicenow-operations',
|
|
19
|
+
'snow_create_incident': 'servicenow-operations',
|
|
20
|
+
'snow_update_incident': 'servicenow-operations',
|
|
21
|
+
'snow_query_users': 'servicenow-operations',
|
|
22
|
+
'snow_create_user': 'servicenow-operations',
|
|
23
|
+
// Deployment tools
|
|
24
|
+
'snow_create_widget': 'servicenow-deployment',
|
|
25
|
+
'snow_deploy_widget': 'servicenow-deployment',
|
|
26
|
+
'snow_create_flow': 'servicenow-deployment',
|
|
27
|
+
// ML tools
|
|
28
|
+
'ml_train_incident_classifier': 'servicenow-machine-learning',
|
|
29
|
+
'ml_classify_incident': 'servicenow-machine-learning',
|
|
30
|
+
'ml_train_change_risk': 'servicenow-machine-learning',
|
|
31
|
+
'ml_predict_change_risk': 'servicenow-machine-learning',
|
|
32
|
+
'ml_train_anomaly_detector': 'servicenow-machine-learning',
|
|
33
|
+
'ml_detect_anomalies': 'servicenow-machine-learning',
|
|
34
|
+
// Update Set tools
|
|
35
|
+
'snow_create_update_set': 'servicenow-update-set',
|
|
36
|
+
'snow_get_current_update_set': 'servicenow-update-set',
|
|
37
|
+
'snow_commit_update_set': 'servicenow-update-set',
|
|
38
|
+
// Platform Development tools
|
|
39
|
+
'snow_create_business_rule': 'servicenow-platform-development',
|
|
40
|
+
'snow_create_script_include': 'servicenow-platform-development',
|
|
41
|
+
'snow_create_client_script': 'servicenow-platform-development',
|
|
42
|
+
// Integration tools
|
|
43
|
+
'snow_discover_rest_endpoints': 'servicenow-integration',
|
|
44
|
+
'snow_create_rest_endpoint': 'servicenow-integration',
|
|
45
|
+
'snow_create_transform_map': 'servicenow-integration',
|
|
46
|
+
// Automation tools
|
|
47
|
+
'snow_create_scheduled_job': 'servicenow-automation',
|
|
48
|
+
'snow_create_event_rule': 'servicenow-automation',
|
|
49
|
+
'snow_create_workflow': 'servicenow-automation',
|
|
50
|
+
// Security tools
|
|
51
|
+
'snow_create_security_rule': 'servicenow-security-compliance',
|
|
52
|
+
'snow_scan_security': 'servicenow-security-compliance',
|
|
53
|
+
'snow_check_compliance': 'servicenow-security-compliance',
|
|
54
|
+
// Reporting tools
|
|
55
|
+
'snow_create_report': 'servicenow-reporting-analytics',
|
|
56
|
+
'snow_create_dashboard': 'servicenow-reporting-analytics',
|
|
57
|
+
'snow_get_kpis': 'servicenow-reporting-analytics',
|
|
58
|
+
// Intelligent tools
|
|
59
|
+
'snow_batch_api': 'servicenow-intelligent',
|
|
60
|
+
'snow_analyze_query': 'servicenow-intelligent',
|
|
61
|
+
'snow_predict_change_impact': 'servicenow-intelligent',
|
|
62
|
+
// Snow-Flow tools
|
|
63
|
+
'swarm_init': 'snow-flow',
|
|
64
|
+
'agent_spawn': 'snow-flow',
|
|
65
|
+
'task_orchestrate': 'snow-flow',
|
|
66
|
+
'memory_usage': 'snow-flow',
|
|
67
|
+
'neural_status': 'snow-flow',
|
|
68
|
+
'task_categorize': 'snow-flow'
|
|
69
|
+
};
|
|
70
|
+
class MCPOnDemandProxy {
|
|
71
|
+
constructor() {
|
|
72
|
+
this.activeServers = new Map();
|
|
73
|
+
this.server = new index_js_1.Server({
|
|
74
|
+
name: 'mcp-on-demand-proxy',
|
|
75
|
+
version: '1.0.0',
|
|
76
|
+
}, {
|
|
77
|
+
capabilities: {
|
|
78
|
+
tools: {},
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
this.manager = mcp_on_demand_manager_js_1.MCPOnDemandManager.getInstance();
|
|
82
|
+
this.setupHandlers();
|
|
83
|
+
}
|
|
84
|
+
setupHandlers() {
|
|
85
|
+
// List all available tools from all servers
|
|
86
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
87
|
+
logger.info('📋 Listing all available tools (servers will start on demand)');
|
|
88
|
+
// Return a comprehensive list of all tools
|
|
89
|
+
// In production, this could be loaded from a configuration file
|
|
90
|
+
const tools = Object.keys(TOOL_SERVER_MAP).map(toolName => ({
|
|
91
|
+
name: toolName,
|
|
92
|
+
description: `Tool ${toolName} (server starts on demand)`,
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {}
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
return { tools };
|
|
99
|
+
});
|
|
100
|
+
// Handle tool calls by routing to appropriate server
|
|
101
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
102
|
+
const { name: toolName, arguments: args } = request.params;
|
|
103
|
+
logger.info(`🔧 Tool requested: ${toolName}`);
|
|
104
|
+
// Find which server handles this tool
|
|
105
|
+
const serverName = TOOL_SERVER_MAP[toolName];
|
|
106
|
+
if (!serverName) {
|
|
107
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
108
|
+
}
|
|
109
|
+
// Get or start the server
|
|
110
|
+
logger.info(`🚀 Getting server: ${serverName} for tool: ${toolName}`);
|
|
111
|
+
try {
|
|
112
|
+
const serverProcess = await this.manager.getServer(serverName);
|
|
113
|
+
// Forward the request to the actual server
|
|
114
|
+
// This is simplified - in production you'd use proper IPC
|
|
115
|
+
return await this.forwardToServer(serverName, toolName, args);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error(`Failed to handle tool ${toolName}:`, error);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// Graceful shutdown
|
|
123
|
+
process.on('SIGINT', async () => {
|
|
124
|
+
logger.info('Shutting down MCP proxy...');
|
|
125
|
+
await this.manager.stopAll();
|
|
126
|
+
process.exit(0);
|
|
127
|
+
});
|
|
128
|
+
process.on('SIGTERM', async () => {
|
|
129
|
+
logger.info('Shutting down MCP proxy...');
|
|
130
|
+
await this.manager.stopAll();
|
|
131
|
+
process.exit(0);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Forward request to actual MCP server
|
|
136
|
+
* In production, this would use proper IPC/stdio communication
|
|
137
|
+
*/
|
|
138
|
+
async forwardToServer(serverName, toolName, args) {
|
|
139
|
+
// For now, we'll use a simple exec approach
|
|
140
|
+
// In production, you'd maintain persistent connections
|
|
141
|
+
try {
|
|
142
|
+
// Create a temporary request file
|
|
143
|
+
const request = {
|
|
144
|
+
jsonrpc: '2.0',
|
|
145
|
+
method: 'tools/call',
|
|
146
|
+
params: {
|
|
147
|
+
name: toolName,
|
|
148
|
+
arguments: args
|
|
149
|
+
},
|
|
150
|
+
id: Date.now()
|
|
151
|
+
};
|
|
152
|
+
// This is a simplified example - in production use proper IPC
|
|
153
|
+
logger.info(`Forwarding ${toolName} to ${serverName}`);
|
|
154
|
+
// Return a mock response for now
|
|
155
|
+
return {
|
|
156
|
+
content: [{
|
|
157
|
+
type: 'text',
|
|
158
|
+
text: `Tool ${toolName} executed via on-demand server ${serverName}`
|
|
159
|
+
}]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
logger.error(`Failed to forward to ${serverName}:`, error);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async run() {
|
|
168
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
169
|
+
await this.server.connect(transport);
|
|
170
|
+
logger.info('🎯 MCP On-Demand Proxy started');
|
|
171
|
+
logger.info('📊 Servers will start automatically when tools are used');
|
|
172
|
+
// Log status periodically
|
|
173
|
+
setInterval(() => {
|
|
174
|
+
const status = this.manager.getStatus();
|
|
175
|
+
if (status.running > 0) {
|
|
176
|
+
logger.info(`📊 Status: ${status.running} running, ${status.stopped} stopped`);
|
|
177
|
+
}
|
|
178
|
+
}, 60000).unref();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Start the proxy
|
|
182
|
+
const proxy = new MCPOnDemandProxy();
|
|
183
|
+
proxy.run().catch((error) => {
|
|
184
|
+
logger.error('Failed to start MCP proxy:', error);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
187
|
+
//# sourceMappingURL=mcp-on-demand-proxy.js.map
|
|
@@ -15,6 +15,7 @@ const scope_manager_js_1 = require("../managers/scope-manager.js");
|
|
|
15
15
|
const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
|
|
16
16
|
const artifact_tracker_js_1 = require("../utils/artifact-tracker.js");
|
|
17
17
|
const servicenow_id_generator_js_1 = require("../utils/servicenow-id-generator.js");
|
|
18
|
+
const deployment_auth_fix_js_1 = require("../utils/deployment-auth-fix.js");
|
|
18
19
|
const fs_1 = require("fs");
|
|
19
20
|
const path_1 = require("path");
|
|
20
21
|
class ServiceNowDeploymentMCP {
|
|
@@ -30,6 +31,7 @@ class ServiceNowDeploymentMCP {
|
|
|
30
31
|
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
31
32
|
this.oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
32
33
|
this.logger = new logger_js_1.Logger('ServiceNowDeploymentMCP');
|
|
34
|
+
this.deploymentAuthManager = new deployment_auth_fix_js_1.DeploymentAuthManager();
|
|
33
35
|
// Initialize global scope management
|
|
34
36
|
this.scopeManager = new scope_manager_js_1.ScopeManager({
|
|
35
37
|
defaultScope: global_scope_strategy_js_1.ScopeType.GLOBAL,
|
|
@@ -472,6 +474,45 @@ class ServiceNowDeploymentMCP {
|
|
|
472
474
|
updateSetName: updateSetName
|
|
473
475
|
};
|
|
474
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Create a record with automatic 403 error recovery
|
|
479
|
+
* Will attempt to refresh token and retry once if 403 error occurs
|
|
480
|
+
*/
|
|
481
|
+
async createRecordWithRetry(table, data) {
|
|
482
|
+
try {
|
|
483
|
+
// First attempt
|
|
484
|
+
return await this.client.createRecord(table, data);
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
// Check if it's a 403 error
|
|
488
|
+
if (error.response?.status === 403 || error.message?.includes('403')) {
|
|
489
|
+
this.logger.warn('Got 403 error, attempting token refresh and retry...');
|
|
490
|
+
// Refresh token
|
|
491
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
492
|
+
if (refreshResult.success && refreshResult.accessToken) {
|
|
493
|
+
// Client will use the new token automatically from unified auth store
|
|
494
|
+
// No need to call authenticate - the client reads from auth store
|
|
495
|
+
// Retry the operation
|
|
496
|
+
try {
|
|
497
|
+
this.logger.info('Retrying operation with refreshed token...');
|
|
498
|
+
return await this.client.createRecord(table, data);
|
|
499
|
+
}
|
|
500
|
+
catch (retryError) {
|
|
501
|
+
this.logger.error('Retry failed after token refresh:', retryError);
|
|
502
|
+
throw retryError;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
this.logger.error('Failed to refresh token for retry');
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
// Not a 403 error, just re-throw
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
475
516
|
/**
|
|
476
517
|
* Ensure artifact is tracked in current Update Set
|
|
477
518
|
*/
|
|
@@ -537,17 +578,26 @@ class ServiceNowDeploymentMCP {
|
|
|
537
578
|
}
|
|
538
579
|
async deployWidget(args) {
|
|
539
580
|
try {
|
|
540
|
-
//
|
|
541
|
-
const
|
|
542
|
-
if (!
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
581
|
+
// Enhanced authentication check with token refresh for deployment
|
|
582
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
583
|
+
if (!authResult.isValid) {
|
|
584
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
585
|
+
// If auth failed, try to refresh token
|
|
586
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
587
|
+
if (!refreshResult.success) {
|
|
588
|
+
return {
|
|
589
|
+
content: [
|
|
590
|
+
{
|
|
591
|
+
type: 'text',
|
|
592
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// Warn if token may lack write permissions
|
|
599
|
+
if (!authResult.hasWriteScope) {
|
|
600
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
551
601
|
}
|
|
552
602
|
this.logger.info('Deploying widget to ServiceNow', { name: args.name });
|
|
553
603
|
// ENHANCED: Mandatory Update Set management with auto-activation
|
|
@@ -695,7 +745,7 @@ class ServiceNowDeploymentMCP {
|
|
|
695
745
|
// Strategy 2: Direct table record creation (fallback)
|
|
696
746
|
try {
|
|
697
747
|
this.logger.info('🔄 Attempting fallback: Direct table record creation');
|
|
698
|
-
result = await this.
|
|
748
|
+
result = await this.createRecordWithRetry('sp_widget', {
|
|
699
749
|
name: args.name,
|
|
700
750
|
id: args.name,
|
|
701
751
|
title: args.title,
|
|
@@ -1069,17 +1119,26 @@ Use \`snow_deployment_debug\` for more information about this session.`,
|
|
|
1069
1119
|
*/
|
|
1070
1120
|
async deployPortalPage(args) {
|
|
1071
1121
|
try {
|
|
1072
|
-
//
|
|
1073
|
-
const
|
|
1074
|
-
if (!
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1122
|
+
// Enhanced authentication check with token refresh for deployment
|
|
1123
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
1124
|
+
if (!authResult.isValid) {
|
|
1125
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
1126
|
+
// If auth failed, try to refresh token
|
|
1127
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
1128
|
+
if (!refreshResult.success) {
|
|
1129
|
+
return {
|
|
1130
|
+
content: [
|
|
1131
|
+
{
|
|
1132
|
+
type: 'text',
|
|
1133
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
1134
|
+
},
|
|
1135
|
+
],
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
// Warn if token may lack write permissions
|
|
1140
|
+
if (!authResult.hasWriteScope) {
|
|
1141
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
1083
1142
|
}
|
|
1084
1143
|
this.logger.info('Deploying portal page to ServiceNow', { name: args.page_id });
|
|
1085
1144
|
// Ensure Update Set is active
|
|
@@ -1124,7 +1183,7 @@ Use \`snow_deployment_debug\` for more information about this session.`,
|
|
|
1124
1183
|
let pageResult;
|
|
1125
1184
|
try {
|
|
1126
1185
|
// Create the page record
|
|
1127
|
-
pageResult = await this.
|
|
1186
|
+
pageResult = await this.createRecordWithRetry('sp_page', {
|
|
1128
1187
|
id: args.page_id,
|
|
1129
1188
|
title: args.title,
|
|
1130
1189
|
short_description: args.description || `Portal page created by Snow-Flow`,
|
|
@@ -1512,17 +1571,26 @@ ${args.widgets && args.widgets.length > 0 ? args.widgets.map((w, i) => `
|
|
|
1512
1571
|
}
|
|
1513
1572
|
async deployFlow(args) {
|
|
1514
1573
|
try {
|
|
1515
|
-
//
|
|
1516
|
-
const
|
|
1517
|
-
if (!
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1574
|
+
// Enhanced authentication check with token refresh for deployment
|
|
1575
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
1576
|
+
if (!authResult.isValid) {
|
|
1577
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
1578
|
+
// If auth failed, try to refresh token
|
|
1579
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
1580
|
+
if (!refreshResult.success) {
|
|
1581
|
+
return {
|
|
1582
|
+
content: [
|
|
1583
|
+
{
|
|
1584
|
+
type: 'text',
|
|
1585
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
1586
|
+
},
|
|
1587
|
+
],
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
// Warn if token may lack write permissions
|
|
1592
|
+
if (!authResult.hasWriteScope) {
|
|
1593
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
1526
1594
|
}
|
|
1527
1595
|
// Ensure we have a flow definition
|
|
1528
1596
|
if (!args.flow_definition) {
|
|
@@ -1964,17 +2032,26 @@ ${isComposedFlow ? `
|
|
|
1964
2032
|
}
|
|
1965
2033
|
async deployApplication(args) {
|
|
1966
2034
|
try {
|
|
1967
|
-
//
|
|
1968
|
-
const
|
|
1969
|
-
if (!
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2035
|
+
// Enhanced authentication check with token refresh for deployment
|
|
2036
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
2037
|
+
if (!authResult.isValid) {
|
|
2038
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
2039
|
+
// If auth failed, try to refresh token
|
|
2040
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
2041
|
+
if (!refreshResult.success) {
|
|
2042
|
+
return {
|
|
2043
|
+
content: [
|
|
2044
|
+
{
|
|
2045
|
+
type: 'text',
|
|
2046
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
2047
|
+
},
|
|
2048
|
+
],
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
// Warn if token may lack write permissions
|
|
2053
|
+
if (!authResult.hasWriteScope) {
|
|
2054
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
1978
2055
|
}
|
|
1979
2056
|
this.logger.info('Deploying application with intelligent scope management', { name: args.name });
|
|
1980
2057
|
// Ensure Update Set is active
|
|
@@ -3480,13 +3557,13 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
|
|
|
3480
3557
|
async previewWidget(args) {
|
|
3481
3558
|
try {
|
|
3482
3559
|
// Check authentication first
|
|
3483
|
-
const
|
|
3484
|
-
if (!
|
|
3560
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
3561
|
+
if (!authResult.isValid) {
|
|
3485
3562
|
return {
|
|
3486
3563
|
content: [
|
|
3487
3564
|
{
|
|
3488
3565
|
type: 'text',
|
|
3489
|
-
text:
|
|
3566
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
3490
3567
|
},
|
|
3491
3568
|
],
|
|
3492
3569
|
};
|
|
@@ -3653,13 +3730,13 @@ Use \`snow_widget_test\` to run automated tests with different scenarios.`,
|
|
|
3653
3730
|
async testWidget(args) {
|
|
3654
3731
|
try {
|
|
3655
3732
|
// Check authentication first
|
|
3656
|
-
const
|
|
3657
|
-
if (!
|
|
3733
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
3734
|
+
if (!authResult.isValid) {
|
|
3658
3735
|
return {
|
|
3659
3736
|
content: [
|
|
3660
3737
|
{
|
|
3661
3738
|
type: 'text',
|
|
3662
|
-
text:
|
|
3739
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
3663
3740
|
},
|
|
3664
3741
|
],
|
|
3665
3742
|
};
|
|
@@ -4689,21 +4766,21 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
4689
4766
|
};
|
|
4690
4767
|
case 'script':
|
|
4691
4768
|
case 'script_include':
|
|
4692
|
-
const scriptResult = await this.
|
|
4769
|
+
const scriptResult = await this.createRecordWithRetry('sys_script_include', config);
|
|
4693
4770
|
return {
|
|
4694
4771
|
success: scriptResult.success,
|
|
4695
4772
|
sys_id: scriptResult.data?.sys_id,
|
|
4696
4773
|
message: scriptResult.success ? 'Script deployed' : scriptResult.error
|
|
4697
4774
|
};
|
|
4698
4775
|
case 'business_rule':
|
|
4699
|
-
const ruleResult = await this.
|
|
4776
|
+
const ruleResult = await this.createRecordWithRetry('sys_script', config);
|
|
4700
4777
|
return {
|
|
4701
4778
|
success: ruleResult.success,
|
|
4702
4779
|
sys_id: ruleResult.data?.sys_id,
|
|
4703
4780
|
message: ruleResult.success ? 'Business rule deployed' : ruleResult.error
|
|
4704
4781
|
};
|
|
4705
4782
|
case 'table':
|
|
4706
|
-
const tableResult = await this.
|
|
4783
|
+
const tableResult = await this.createRecordWithRetry('sys_db_object', config);
|
|
4707
4784
|
return {
|
|
4708
4785
|
success: tableResult.success,
|
|
4709
4786
|
sys_id: tableResult.data?.sys_id,
|