snow-flow 3.3.8 ā 3.4.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/.mcp.json.template
CHANGED
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"args": [
|
|
80
80
|
"{{PROJECT_ROOT}}/dist/mcp/servicenow-automation-mcp.js"
|
|
81
81
|
],
|
|
82
|
-
"description": "Scheduled jobs, event rules, notifications, SLA definitions, escalation rules, workflow activities,
|
|
82
|
+
"description": "Scheduled jobs, event rules, notifications, SLA definitions, escalation rules, workflow activities, script execution with output retrieval, script history, REST testing, system logs, execution tracing",
|
|
83
83
|
"env": {
|
|
84
84
|
"SNOW_INSTANCE": "{{SNOW_INSTANCE}}",
|
|
85
85
|
"SNOW_CLIENT_ID": "{{SNOW_CLIENT_ID}}",
|
package/dist/dynamic-version.js
CHANGED
|
@@ -36,7 +36,7 @@ function getDynamicVersion() {
|
|
|
36
36
|
console.warn('Warning: Could not read version from package.json:', error);
|
|
37
37
|
}
|
|
38
38
|
// Fallback to hardcoded version
|
|
39
|
-
return '3.
|
|
39
|
+
return '3.4.0';
|
|
40
40
|
}
|
|
41
41
|
// Export a constant that uses the dynamic version
|
|
42
42
|
exports.VERSION = getDynamicVersion();
|
|
@@ -300,6 +300,120 @@ class ServiceNowAutomationMCP {
|
|
|
300
300
|
active: { type: 'boolean', description: 'Filter by active status' }
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'snow_execute_script_with_output',
|
|
306
|
+
description: 'Executes a background script and retrieves the actual output. Waits for execution to complete and returns the results.',
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
script: { type: 'string', description: 'JavaScript code to execute' },
|
|
311
|
+
return_output: { type: 'boolean', description: 'Return script output', default: true },
|
|
312
|
+
max_wait: { type: 'number', description: 'Maximum wait time in milliseconds', default: 5000 },
|
|
313
|
+
capture_logs: { type: 'boolean', description: 'Capture system logs during execution', default: true }
|
|
314
|
+
},
|
|
315
|
+
required: ['script']
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: 'snow_get_script_output',
|
|
320
|
+
description: 'Retrieves the output from a previously executed script using its execution ID.',
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: 'object',
|
|
323
|
+
properties: {
|
|
324
|
+
execution_id: { type: 'string', description: 'Execution ID from previous script run' }
|
|
325
|
+
},
|
|
326
|
+
required: ['execution_id']
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'snow_execute_script_sync',
|
|
331
|
+
description: 'Synchronously executes a script and waits for the result. Returns output immediately.',
|
|
332
|
+
inputSchema: {
|
|
333
|
+
type: 'object',
|
|
334
|
+
properties: {
|
|
335
|
+
script: { type: 'string', description: 'JavaScript code to execute' },
|
|
336
|
+
timeout: { type: 'number', description: 'Timeout in milliseconds', default: 3000 },
|
|
337
|
+
capture_output: { type: 'boolean', description: 'Capture and return output', default: true }
|
|
338
|
+
},
|
|
339
|
+
required: ['script']
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'snow_get_logs',
|
|
344
|
+
description: 'Retrieves system logs with filtering options. Access script, system, and background logs.',
|
|
345
|
+
inputSchema: {
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {
|
|
348
|
+
source: { type: 'string', description: 'Log source: system, script, background, all', default: 'all' },
|
|
349
|
+
filter: { type: 'string', description: 'Filter string to search for' },
|
|
350
|
+
last_n_minutes: { type: 'number', description: 'Get logs from last N minutes', default: 5 },
|
|
351
|
+
return_content: { type: 'boolean', description: 'Return full log content', default: true },
|
|
352
|
+
limit: { type: 'number', description: 'Maximum number of log entries', default: 100 }
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: 'snow_test_rest_connection',
|
|
358
|
+
description: 'Tests a REST message connection with full response details and diagnostics.',
|
|
359
|
+
inputSchema: {
|
|
360
|
+
type: 'object',
|
|
361
|
+
properties: {
|
|
362
|
+
rest_message: { type: 'string', description: 'REST Message name' },
|
|
363
|
+
method: { type: 'string', description: 'HTTP Method name' },
|
|
364
|
+
test_params: { type: 'object', description: 'Test parameters for the request' },
|
|
365
|
+
return_full_response: { type: 'boolean', description: 'Return complete response details', default: true },
|
|
366
|
+
validate_auth: { type: 'boolean', description: 'Validate authentication', default: true }
|
|
367
|
+
},
|
|
368
|
+
required: ['rest_message']
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'snow_rest_message_test_suite',
|
|
373
|
+
description: 'Comprehensive REST message testing with authentication validation and connection diagnostics.',
|
|
374
|
+
inputSchema: {
|
|
375
|
+
type: 'object',
|
|
376
|
+
properties: {
|
|
377
|
+
rest_message: { type: 'string', description: 'REST Message to test' },
|
|
378
|
+
validate_auth: { type: 'boolean', description: 'Validate authentication', default: true },
|
|
379
|
+
test_connection: { type: 'boolean', description: 'Test actual connection', default: true },
|
|
380
|
+
return_diagnostics: { type: 'boolean', description: 'Return detailed diagnostics', default: true }
|
|
381
|
+
},
|
|
382
|
+
required: ['rest_message']
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'snow_property_manager',
|
|
387
|
+
description: 'Enhanced property management with get, set, and validation in one tool.',
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
properties: {
|
|
391
|
+
action: { type: 'string', description: 'Action: get, set, validate', enum: ['get', 'set', 'validate'] },
|
|
392
|
+
name: { type: 'string', description: 'Property name' },
|
|
393
|
+
value: { type: 'string', description: 'Property value (for set action)' },
|
|
394
|
+
mask_sensitive: { type: 'boolean', description: 'Mask sensitive values like API keys', default: true }
|
|
395
|
+
},
|
|
396
|
+
required: ['action', 'name']
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'snow_trace_execution',
|
|
401
|
+
description: 'Traces execution flow with real-time tracking of scripts, REST calls, and errors.',
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: 'object',
|
|
404
|
+
properties: {
|
|
405
|
+
track_id: { type: 'string', description: 'Tracking ID for the execution session' },
|
|
406
|
+
include: {
|
|
407
|
+
type: 'array',
|
|
408
|
+
items: { type: 'string' },
|
|
409
|
+
description: 'What to track: scripts, rest_calls, errors, queries, all',
|
|
410
|
+
default: ['all']
|
|
411
|
+
},
|
|
412
|
+
real_time: { type: 'boolean', description: 'Enable real-time tracking', default: true },
|
|
413
|
+
max_entries: { type: 'number', description: 'Maximum trace entries', default: 1000 }
|
|
414
|
+
},
|
|
415
|
+
required: ['track_id']
|
|
416
|
+
}
|
|
303
417
|
}
|
|
304
418
|
]
|
|
305
419
|
}));
|
|
@@ -347,6 +461,22 @@ class ServiceNowAutomationMCP {
|
|
|
347
461
|
return await this.createATFTestSuite(args);
|
|
348
462
|
case 'snow_discover_atf_tests':
|
|
349
463
|
return await this.discoverATFTests(args);
|
|
464
|
+
case 'snow_execute_script_with_output':
|
|
465
|
+
return await this.executeScriptWithOutput(args);
|
|
466
|
+
case 'snow_get_script_output':
|
|
467
|
+
return await this.getScriptOutput(args);
|
|
468
|
+
case 'snow_execute_script_sync':
|
|
469
|
+
return await this.executeScriptSync(args);
|
|
470
|
+
case 'snow_get_logs':
|
|
471
|
+
return await this.getLogs(args);
|
|
472
|
+
case 'snow_test_rest_connection':
|
|
473
|
+
return await this.testRESTConnection(args);
|
|
474
|
+
case 'snow_rest_message_test_suite':
|
|
475
|
+
return await this.restMessageTestSuite(args);
|
|
476
|
+
case 'snow_property_manager':
|
|
477
|
+
return await this.propertyManager(args);
|
|
478
|
+
case 'snow_trace_execution':
|
|
479
|
+
return await this.traceExecution(args);
|
|
350
480
|
default:
|
|
351
481
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
352
482
|
}
|
|
@@ -1562,6 +1692,664 @@ ${groupedResults.suites.slice(0, 10).map(suite => `- ${suite.name} ${suite.activ
|
|
|
1562
1692
|
return baseConfig;
|
|
1563
1693
|
}
|
|
1564
1694
|
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Execute script with output retrieval
|
|
1697
|
+
*/
|
|
1698
|
+
async executeScriptWithOutput(args) {
|
|
1699
|
+
try {
|
|
1700
|
+
this.logger.info('Executing script with output retrieval...');
|
|
1701
|
+
// Create a unique execution ID
|
|
1702
|
+
const executionId = `snow_flow_exec_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
1703
|
+
// Wrap the script to capture output
|
|
1704
|
+
const wrappedScript = `
|
|
1705
|
+
var snowFlowOutput = [];
|
|
1706
|
+
var snowFlowErrors = [];
|
|
1707
|
+
var snowFlowExecutionId = '${executionId}';
|
|
1708
|
+
|
|
1709
|
+
// Override gs methods to capture output
|
|
1710
|
+
var originalPrint = gs.print;
|
|
1711
|
+
var originalInfo = gs.info;
|
|
1712
|
+
var originalWarn = gs.warn;
|
|
1713
|
+
var originalError = gs.error;
|
|
1714
|
+
var originalLog = gs.log;
|
|
1715
|
+
|
|
1716
|
+
gs.print = function(msg) {
|
|
1717
|
+
snowFlowOutput.push({type: 'print', message: msg, timestamp: new GlideDateTime().getDisplayValue()});
|
|
1718
|
+
originalPrint.call(gs, msg);
|
|
1719
|
+
};
|
|
1720
|
+
|
|
1721
|
+
gs.info = function(msg) {
|
|
1722
|
+
snowFlowOutput.push({type: 'info', message: msg, timestamp: new GlideDateTime().getDisplayValue()});
|
|
1723
|
+
originalInfo.call(gs, msg);
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1726
|
+
gs.warn = function(msg) {
|
|
1727
|
+
snowFlowOutput.push({type: 'warn', message: msg, timestamp: new GlideDateTime().getDisplayValue()});
|
|
1728
|
+
originalWarn.call(gs, msg);
|
|
1729
|
+
};
|
|
1730
|
+
|
|
1731
|
+
gs.error = function(msg) {
|
|
1732
|
+
snowFlowErrors.push({type: 'error', message: msg, timestamp: new GlideDateTime().getDisplayValue()});
|
|
1733
|
+
originalError.call(gs, msg);
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
gs.log = function(msg) {
|
|
1737
|
+
snowFlowOutput.push({type: 'log', message: msg, timestamp: new GlideDateTime().getDisplayValue()});
|
|
1738
|
+
originalLog.call(gs, msg);
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
try {
|
|
1742
|
+
// User script
|
|
1743
|
+
${args.script}
|
|
1744
|
+
|
|
1745
|
+
// Store output in sys_properties temporarily
|
|
1746
|
+
var prop = new GlideRecord('sys_properties');
|
|
1747
|
+
prop.initialize();
|
|
1748
|
+
prop.name = 'snow_flow.script_output.' + snowFlowExecutionId;
|
|
1749
|
+
prop.value = JSON.stringify({
|
|
1750
|
+
executionId: snowFlowExecutionId,
|
|
1751
|
+
output: snowFlowOutput,
|
|
1752
|
+
errors: snowFlowErrors,
|
|
1753
|
+
executedAt: new GlideDateTime().getDisplayValue(),
|
|
1754
|
+
success: true
|
|
1755
|
+
});
|
|
1756
|
+
prop.type = 'string';
|
|
1757
|
+
prop.description = 'Temporary script output from Snow-Flow';
|
|
1758
|
+
prop.insert();
|
|
1759
|
+
|
|
1760
|
+
} catch(e) {
|
|
1761
|
+
snowFlowErrors.push({type: 'exception', message: e.toString(), timestamp: new GlideDateTime().getDisplayValue()});
|
|
1762
|
+
|
|
1763
|
+
// Store error output
|
|
1764
|
+
var prop = new GlideRecord('sys_properties');
|
|
1765
|
+
prop.initialize();
|
|
1766
|
+
prop.name = 'snow_flow.script_output.' + snowFlowExecutionId;
|
|
1767
|
+
prop.value = JSON.stringify({
|
|
1768
|
+
executionId: snowFlowExecutionId,
|
|
1769
|
+
output: snowFlowOutput,
|
|
1770
|
+
errors: snowFlowErrors,
|
|
1771
|
+
executedAt: new GlideDateTime().getDisplayValue(),
|
|
1772
|
+
success: false,
|
|
1773
|
+
exception: e.toString()
|
|
1774
|
+
});
|
|
1775
|
+
prop.type = 'string';
|
|
1776
|
+
prop.description = 'Temporary script output from Snow-Flow';
|
|
1777
|
+
prop.insert();
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// Restore original methods
|
|
1781
|
+
gs.print = originalPrint;
|
|
1782
|
+
gs.info = originalInfo;
|
|
1783
|
+
gs.warn = originalWarn;
|
|
1784
|
+
gs.error = originalError;
|
|
1785
|
+
gs.log = originalLog;
|
|
1786
|
+
|
|
1787
|
+
'Execution ID: ' + snowFlowExecutionId;
|
|
1788
|
+
`;
|
|
1789
|
+
// Execute the wrapped script
|
|
1790
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
1791
|
+
const response = await this.client.executeScript(wrappedScript);
|
|
1792
|
+
// Wait a moment for the property to be written
|
|
1793
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1794
|
+
// Retrieve the output from sys_properties
|
|
1795
|
+
const outputResponse = await this.client.searchRecords('sys_properties', `name=snow_flow.script_output.${executionId}`, 1);
|
|
1796
|
+
let scriptOutput = null;
|
|
1797
|
+
if (outputResponse.success && outputResponse.data?.result?.length > 0) {
|
|
1798
|
+
try {
|
|
1799
|
+
scriptOutput = JSON.parse(outputResponse.data.result[0].value);
|
|
1800
|
+
// Clean up the temporary property
|
|
1801
|
+
await this.client.deleteRecord('sys_properties', outputResponse.data.result[0].sys_id);
|
|
1802
|
+
}
|
|
1803
|
+
catch (parseError) {
|
|
1804
|
+
this.logger.warn('Could not parse script output:', parseError);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return {
|
|
1808
|
+
content: [{
|
|
1809
|
+
type: 'text',
|
|
1810
|
+
text: `ā
Script executed successfully!\n\nš **Execution ID**: ${executionId}\nā° **Executed At**: ${new Date().toISOString()}\n\n${scriptOutput ? `š **Output**:\n${scriptOutput.output.map((o) => `[${o.type}] ${o.message}`).join('\n')}\n\n${scriptOutput.errors.length > 0 ? `ā ļø **Errors**:\n${scriptOutput.errors.map((e) => `[${e.type}] ${e.message}`).join('\n')}` : 'ā
No errors'}` : 'ā ļø Output retrieval pending - use snow_get_script_output with the execution ID'}\n\nš” **Tip**: Use snow_get_script_output to retrieve the output later`
|
|
1811
|
+
}]
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
catch (error) {
|
|
1815
|
+
this.logger.error('Failed to execute script with output:', error);
|
|
1816
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to execute script: ${error}`);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Get script output from previous execution
|
|
1821
|
+
*/
|
|
1822
|
+
async getScriptOutput(args) {
|
|
1823
|
+
try {
|
|
1824
|
+
this.logger.info(`Retrieving script output for execution: ${args.executionId}`);
|
|
1825
|
+
// Try to find the output in sys_properties
|
|
1826
|
+
const outputResponse = await this.client.searchRecords('sys_properties', `nameLIKEsnow_flow.script_output.${args.executionId}`, 1);
|
|
1827
|
+
if (outputResponse.success && outputResponse.data?.result?.length > 0) {
|
|
1828
|
+
const scriptOutput = JSON.parse(outputResponse.data.result[0].value);
|
|
1829
|
+
// Optionally clean up old property
|
|
1830
|
+
if (args.cleanup !== false) {
|
|
1831
|
+
await this.client.deleteRecord('sys_properties', outputResponse.data.result[0].sys_id);
|
|
1832
|
+
}
|
|
1833
|
+
return {
|
|
1834
|
+
content: [{
|
|
1835
|
+
type: 'text',
|
|
1836
|
+
text: `š **Script Output Retrieved**\n\nš **Execution ID**: ${scriptOutput.executionId}\nā° **Executed At**: ${scriptOutput.executedAt}\nā
**Success**: ${scriptOutput.success}\n\n**Output**:\n${scriptOutput.output.map((o) => `[${o.type}] ${o.message}`).join('\n')}\n\n${scriptOutput.errors.length > 0 ? `**Errors**:\n${scriptOutput.errors.map((e) => `[${e.type}] ${e.message}`).join('\n')}` : 'No errors'}${scriptOutput.exception ? `\n\n**Exception**: ${scriptOutput.exception}` : ''}`
|
|
1837
|
+
}]
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
// Try to find in script execution history
|
|
1841
|
+
const historyResponse = await this.client.searchRecords('sys_script_execution_history', `script_nameLIKE${args.executionId}`, 5);
|
|
1842
|
+
if (historyResponse.success && historyResponse.data?.result?.length > 0) {
|
|
1843
|
+
const history = historyResponse.data.result[0];
|
|
1844
|
+
return {
|
|
1845
|
+
content: [{
|
|
1846
|
+
type: 'text',
|
|
1847
|
+
text: `š **Script Execution History**\n\nš **Execution ID**: ${args.executionId}\nā° **Executed**: ${history.sys_created_on}\nš¤ **User**: ${history.sys_created_by}\n\n**Output**:\n${history.output || 'No output captured'}\n\n**Errors**:\n${history.error_message || 'No errors'}`
|
|
1848
|
+
}]
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
return {
|
|
1852
|
+
content: [{
|
|
1853
|
+
type: 'text',
|
|
1854
|
+
text: `ā ļø No output found for execution ID: ${args.executionId}\n\nPossible reasons:\n- The script is still executing\n- The execution ID is incorrect\n- The output has been cleaned up\n\nTry running snow_get_script_history to see recent executions.`
|
|
1855
|
+
}]
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
catch (error) {
|
|
1859
|
+
this.logger.error('Failed to get script output:', error);
|
|
1860
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get script output: ${error}`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Execute script synchronously
|
|
1865
|
+
*/
|
|
1866
|
+
async executeScriptSync(args) {
|
|
1867
|
+
try {
|
|
1868
|
+
this.logger.info('Executing script synchronously...');
|
|
1869
|
+
const timeout = args.timeout || 30000; // Default 30 seconds
|
|
1870
|
+
// Create a polling script that executes and returns immediately
|
|
1871
|
+
const executionId = `sync_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
1872
|
+
const executionScript = `
|
|
1873
|
+
var result = {};
|
|
1874
|
+
var startTime = new GlideDateTime();
|
|
1875
|
+
|
|
1876
|
+
try {
|
|
1877
|
+
// Execute the user script
|
|
1878
|
+
var scriptResult = (function() {
|
|
1879
|
+
${args.script}
|
|
1880
|
+
})();
|
|
1881
|
+
|
|
1882
|
+
result = {
|
|
1883
|
+
success: true,
|
|
1884
|
+
result: scriptResult,
|
|
1885
|
+
executionTime: GlideDateTime.subtract(startTime, new GlideDateTime()).getNumericValue(),
|
|
1886
|
+
executionId: '${executionId}'
|
|
1887
|
+
};
|
|
1888
|
+
} catch(e) {
|
|
1889
|
+
result = {
|
|
1890
|
+
success: false,
|
|
1891
|
+
error: e.toString(),
|
|
1892
|
+
executionTime: GlideDateTime.subtract(startTime, new GlideDateTime()).getNumericValue(),
|
|
1893
|
+
executionId: '${executionId}'
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// Return result directly
|
|
1898
|
+
JSON.stringify(result);
|
|
1899
|
+
`;
|
|
1900
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
1901
|
+
const response = await this.client.executeScript(executionScript);
|
|
1902
|
+
let result;
|
|
1903
|
+
try {
|
|
1904
|
+
// Try to parse the response as JSON
|
|
1905
|
+
if (response.data?.result) {
|
|
1906
|
+
result = JSON.parse(response.data.result);
|
|
1907
|
+
}
|
|
1908
|
+
else {
|
|
1909
|
+
result = { success: true, result: response.data?.result || 'Script executed successfully' };
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
catch (parseError) {
|
|
1913
|
+
result = { success: true, result: response.data?.result || 'Script executed successfully' };
|
|
1914
|
+
}
|
|
1915
|
+
return {
|
|
1916
|
+
content: [{
|
|
1917
|
+
type: 'text',
|
|
1918
|
+
text: `ā
**Script Executed Synchronously**\n\nš **Execution ID**: ${executionId}\nā±ļø **Execution Time**: ${result.executionTime || 'N/A'}ms\nā
**Success**: ${result.success}\n\n**Result**:\n${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}${result.error ? `\n\n**Error**: ${result.error}` : ''}`
|
|
1919
|
+
}]
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
catch (error) {
|
|
1923
|
+
this.logger.error('Failed to execute script synchronously:', error);
|
|
1924
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to execute script: ${error}`);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Get system logs
|
|
1929
|
+
*/
|
|
1930
|
+
async getLogs(args) {
|
|
1931
|
+
try {
|
|
1932
|
+
this.logger.info('Retrieving system logs...');
|
|
1933
|
+
const limit = args.limit || 100;
|
|
1934
|
+
const source = args.source || 'all';
|
|
1935
|
+
const level = args.level || 'all';
|
|
1936
|
+
// Build query for syslog_transaction table
|
|
1937
|
+
let query = '';
|
|
1938
|
+
if (source !== 'all') {
|
|
1939
|
+
query += `source=${source}^`;
|
|
1940
|
+
}
|
|
1941
|
+
if (level !== 'all') {
|
|
1942
|
+
query += `level=${level}^`;
|
|
1943
|
+
}
|
|
1944
|
+
if (args.since) {
|
|
1945
|
+
query += `sys_created_on>javascript:gs.dateGenerate('${args.since}')^`;
|
|
1946
|
+
}
|
|
1947
|
+
if (args.message) {
|
|
1948
|
+
query += `messageLIKE${args.message}^`;
|
|
1949
|
+
}
|
|
1950
|
+
// Remove trailing ^
|
|
1951
|
+
query = query.replace(/\^$/, '');
|
|
1952
|
+
const logsResponse = await this.client.searchRecords('syslog_transaction', query, limit);
|
|
1953
|
+
if (logsResponse.success && logsResponse.data?.result?.length > 0) {
|
|
1954
|
+
const logs = logsResponse.data.result.map((log) => ({
|
|
1955
|
+
timestamp: log.sys_created_on,
|
|
1956
|
+
level: log.level,
|
|
1957
|
+
source: log.source,
|
|
1958
|
+
message: log.message,
|
|
1959
|
+
user: log.sys_created_by
|
|
1960
|
+
}));
|
|
1961
|
+
return {
|
|
1962
|
+
content: [{
|
|
1963
|
+
type: 'text',
|
|
1964
|
+
text: `š **System Logs** (${logs.length} entries)\n\n${logs.map((log) => `ā° ${log.timestamp} | ${log.level} | ${log.source}\n ${log.message}\n User: ${log.user}`).join('\n\n')}`
|
|
1965
|
+
}]
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
return {
|
|
1969
|
+
content: [{
|
|
1970
|
+
type: 'text',
|
|
1971
|
+
text: `ā ļø No logs found matching criteria:\n- Source: ${source}\n- Level: ${level}\n- Since: ${args.since || 'Not specified'}\n- Message filter: ${args.message || 'None'}`
|
|
1972
|
+
}]
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
catch (error) {
|
|
1976
|
+
this.logger.error('Failed to get logs:', error);
|
|
1977
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get logs: ${error}`);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Test REST connection
|
|
1982
|
+
*/
|
|
1983
|
+
async testRESTConnection(args) {
|
|
1984
|
+
try {
|
|
1985
|
+
this.logger.info(`Testing REST connection: ${args.name}`);
|
|
1986
|
+
// Find the REST message
|
|
1987
|
+
const restMessageResponse = await this.client.searchRecords('sys_rest_message', `name=${args.name}`, 1);
|
|
1988
|
+
if (!restMessageResponse.success || !restMessageResponse.data?.result?.length) {
|
|
1989
|
+
throw new Error(`REST message not found: ${args.name}`);
|
|
1990
|
+
}
|
|
1991
|
+
const restMessage = restMessageResponse.data.result[0];
|
|
1992
|
+
// Get REST message methods
|
|
1993
|
+
const methodsResponse = await this.client.searchRecords('sys_rest_message_fn', `rest_message=${restMessage.sys_id}`, 10);
|
|
1994
|
+
const testResults = [];
|
|
1995
|
+
if (methodsResponse.success && methodsResponse.data?.result?.length > 0) {
|
|
1996
|
+
for (const method of methodsResponse.data.result) {
|
|
1997
|
+
// Test each method
|
|
1998
|
+
const testScript = `
|
|
1999
|
+
var rm = new sn_ws.RESTMessageV2('${args.name}', '${method.function_name}');
|
|
2000
|
+
|
|
2001
|
+
// Set test parameters if provided
|
|
2002
|
+
${args.parameters ? Object.entries(args.parameters).map(([key, value]) => `rm.setStringParameter('${key}', '${value}');`).join('\n') : ''}
|
|
2003
|
+
|
|
2004
|
+
try {
|
|
2005
|
+
var response = rm.execute();
|
|
2006
|
+
var httpStatus = response.getStatusCode();
|
|
2007
|
+
var body = response.getBody();
|
|
2008
|
+
var headers = response.getAllHeaders();
|
|
2009
|
+
|
|
2010
|
+
JSON.stringify({
|
|
2011
|
+
method: '${method.function_name}',
|
|
2012
|
+
endpoint: '${method.rest_endpoint}',
|
|
2013
|
+
httpMethod: '${method.http_method}',
|
|
2014
|
+
status: httpStatus,
|
|
2015
|
+
success: httpStatus >= 200 && httpStatus < 300,
|
|
2016
|
+
responseTime: response.getEccResponseTime ? response.getEccResponseTime() : 'N/A',
|
|
2017
|
+
bodyLength: body ? body.length : 0,
|
|
2018
|
+
headers: headers
|
|
2019
|
+
});
|
|
2020
|
+
} catch(e) {
|
|
2021
|
+
JSON.stringify({
|
|
2022
|
+
method: '${method.function_name}',
|
|
2023
|
+
endpoint: '${method.rest_endpoint}',
|
|
2024
|
+
httpMethod: '${method.http_method}',
|
|
2025
|
+
success: false,
|
|
2026
|
+
error: e.toString()
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
`;
|
|
2030
|
+
const testResponse = await this.client.executeScript(testScript);
|
|
2031
|
+
if (testResponse.data?.result) {
|
|
2032
|
+
try {
|
|
2033
|
+
const result = JSON.parse(testResponse.data.result);
|
|
2034
|
+
testResults.push(result);
|
|
2035
|
+
}
|
|
2036
|
+
catch (e) {
|
|
2037
|
+
testResults.push({
|
|
2038
|
+
method: method.function_name,
|
|
2039
|
+
success: false,
|
|
2040
|
+
error: 'Could not parse test result'
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
return {
|
|
2047
|
+
content: [{
|
|
2048
|
+
type: 'text',
|
|
2049
|
+
text: `š **REST Connection Test Results**\n\nš” **REST Message**: ${args.name}\n\n${testResults.map((result) => `\n**Method**: ${result.method}\n- Endpoint: ${result.endpoint}\n- HTTP Method: ${result.httpMethod}\n- Status: ${result.status || 'N/A'}\n- Success: ${result.success ? 'ā
' : 'ā'}\n- Response Time: ${result.responseTime}\n- Response Size: ${result.bodyLength} bytes\n${result.error ? `- Error: ${result.error}` : ''}`).join('\n')}\n\n**Summary**:\n- Total Methods: ${testResults.length}\n- Successful: ${testResults.filter((r) => r.success).length}\n- Failed: ${testResults.filter((r) => !r.success).length}`
|
|
2050
|
+
}]
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
catch (error) {
|
|
2054
|
+
this.logger.error('Failed to test REST connection:', error);
|
|
2055
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to test REST connection: ${error}`);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* REST message test suite
|
|
2060
|
+
*/
|
|
2061
|
+
async restMessageTestSuite(args) {
|
|
2062
|
+
try {
|
|
2063
|
+
this.logger.info('Running REST message test suite...');
|
|
2064
|
+
// Get all REST messages or filter by pattern
|
|
2065
|
+
let query = '';
|
|
2066
|
+
if (args.pattern) {
|
|
2067
|
+
query = `nameLIKE${args.pattern}`;
|
|
2068
|
+
}
|
|
2069
|
+
const messagesResponse = await this.client.searchRecords('sys_rest_message', query, args.limit || 10);
|
|
2070
|
+
const testSuiteResults = [];
|
|
2071
|
+
if (messagesResponse.success && messagesResponse.data?.result?.length > 0) {
|
|
2072
|
+
for (const message of messagesResponse.data.result) {
|
|
2073
|
+
// Get methods for this message
|
|
2074
|
+
const methodsResponse = await this.client.searchRecords('sys_rest_message_fn', `rest_message=${message.sys_id}`, 5);
|
|
2075
|
+
const messageResults = {
|
|
2076
|
+
name: message.name,
|
|
2077
|
+
description: message.description,
|
|
2078
|
+
methods: [],
|
|
2079
|
+
totalMethods: 0,
|
|
2080
|
+
successfulMethods: 0,
|
|
2081
|
+
failedMethods: 0
|
|
2082
|
+
};
|
|
2083
|
+
if (methodsResponse.success && methodsResponse.data?.result?.length > 0) {
|
|
2084
|
+
messageResults.totalMethods = methodsResponse.data.result.length;
|
|
2085
|
+
for (const method of methodsResponse.data.result) {
|
|
2086
|
+
// Quick connectivity test
|
|
2087
|
+
const testScript = `
|
|
2088
|
+
var rm = new sn_ws.RESTMessageV2('${message.name}', '${method.function_name}');
|
|
2089
|
+
rm.setEccParameter('skip_sensor', true); // Skip sensor for test
|
|
2090
|
+
|
|
2091
|
+
try {
|
|
2092
|
+
// Just validate the configuration
|
|
2093
|
+
var endpoint = rm.getEndpoint();
|
|
2094
|
+
var isValid = endpoint && endpoint.length > 0;
|
|
2095
|
+
|
|
2096
|
+
JSON.stringify({
|
|
2097
|
+
method: '${method.function_name}',
|
|
2098
|
+
endpoint: endpoint,
|
|
2099
|
+
valid: isValid,
|
|
2100
|
+
httpMethod: '${method.http_method}'
|
|
2101
|
+
});
|
|
2102
|
+
} catch(e) {
|
|
2103
|
+
JSON.stringify({
|
|
2104
|
+
method: '${method.function_name}',
|
|
2105
|
+
valid: false,
|
|
2106
|
+
error: e.toString()
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
`;
|
|
2110
|
+
const testResponse = await this.client.executeScript(testScript);
|
|
2111
|
+
if (testResponse.data?.result) {
|
|
2112
|
+
try {
|
|
2113
|
+
const result = JSON.parse(testResponse.data.result);
|
|
2114
|
+
messageResults.methods.push(result);
|
|
2115
|
+
if (result.valid) {
|
|
2116
|
+
messageResults.successfulMethods++;
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
messageResults.failedMethods++;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
catch (e) {
|
|
2123
|
+
messageResults.failedMethods++;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
testSuiteResults.push(messageResults);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
return {
|
|
2132
|
+
content: [{
|
|
2133
|
+
type: 'text',
|
|
2134
|
+
text: `š§Ŗ **REST Message Test Suite Results**\n\n${testSuiteResults.map((result) => `\nš” **${result.name}**\n${result.description ? ` ${result.description}\n` : ''} - Total Methods: ${result.totalMethods}\n - ā
Valid: ${result.successfulMethods}\n - ā Invalid: ${result.failedMethods}\n ${result.methods.length > 0 ? `\n Methods:\n${result.methods.map((m) => ` ⢠${m.method} (${m.httpMethod}) - ${m.valid ? 'ā
' : 'ā'}`).join('\n')}` : ''}`).join('\n')}\n\n**Suite Summary**:\n- Total REST Messages: ${testSuiteResults.length}\n- Total Methods Tested: ${testSuiteResults.reduce((sum, r) => sum + r.totalMethods, 0)}\n- Valid Configurations: ${testSuiteResults.reduce((sum, r) => sum + r.successfulMethods, 0)}\n- Invalid Configurations: ${testSuiteResults.reduce((sum, r) => sum + r.failedMethods, 0)}`
|
|
2135
|
+
}]
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
catch (error) {
|
|
2139
|
+
this.logger.error('Failed to run REST test suite:', error);
|
|
2140
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to run REST test suite: ${error}`);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Enhanced property manager
|
|
2145
|
+
*/
|
|
2146
|
+
async propertyManager(args) {
|
|
2147
|
+
try {
|
|
2148
|
+
this.logger.info('Managing system properties...');
|
|
2149
|
+
const action = args.action || 'list';
|
|
2150
|
+
switch (action) {
|
|
2151
|
+
case 'get':
|
|
2152
|
+
const getPropResponse = await this.client.searchRecords('sys_properties', `name=${args.name}`, 1);
|
|
2153
|
+
if (getPropResponse.success && getPropResponse.data?.result?.length > 0) {
|
|
2154
|
+
const prop = getPropResponse.data.result[0];
|
|
2155
|
+
return {
|
|
2156
|
+
content: [{
|
|
2157
|
+
type: 'text',
|
|
2158
|
+
text: `š **Property**: ${prop.name}\n\n**Value**: ${prop.value}\n**Type**: ${prop.type}\n**Description**: ${prop.description || 'None'}\n**Private**: ${prop.is_private ? 'Yes' : 'No'}\n**Read Only**: ${prop.read_roles ? 'Restricted' : 'No'}`
|
|
2159
|
+
}]
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
throw new Error(`Property not found: ${args.name}`);
|
|
2163
|
+
case 'set':
|
|
2164
|
+
const setPropResponse = await this.client.searchRecords('sys_properties', `name=${args.name}`, 1);
|
|
2165
|
+
if (setPropResponse.success && setPropResponse.data?.result?.length > 0) {
|
|
2166
|
+
// Update existing
|
|
2167
|
+
const updateResponse = await this.client.updateRecord('sys_properties', setPropResponse.data.result[0].sys_id, { value: args.value });
|
|
2168
|
+
if (updateResponse.success) {
|
|
2169
|
+
return {
|
|
2170
|
+
content: [{
|
|
2171
|
+
type: 'text',
|
|
2172
|
+
text: `ā
Property updated: ${args.name} = ${args.value}`
|
|
2173
|
+
}]
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
else {
|
|
2178
|
+
// Create new
|
|
2179
|
+
const createResponse = await this.client.createRecord('sys_properties', {
|
|
2180
|
+
name: args.name,
|
|
2181
|
+
value: args.value,
|
|
2182
|
+
type: args.type || 'string',
|
|
2183
|
+
description: args.description || ''
|
|
2184
|
+
});
|
|
2185
|
+
if (createResponse.success) {
|
|
2186
|
+
return {
|
|
2187
|
+
content: [{
|
|
2188
|
+
type: 'text',
|
|
2189
|
+
text: `ā
Property created: ${args.name} = ${args.value}`
|
|
2190
|
+
}]
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
throw new Error('Failed to set property');
|
|
2195
|
+
case 'delete':
|
|
2196
|
+
const delPropResponse = await this.client.searchRecords('sys_properties', `name=${args.name}`, 1);
|
|
2197
|
+
if (delPropResponse.success && delPropResponse.data?.result?.length > 0) {
|
|
2198
|
+
await this.client.deleteRecord('sys_properties', delPropResponse.data.result[0].sys_id);
|
|
2199
|
+
return {
|
|
2200
|
+
content: [{
|
|
2201
|
+
type: 'text',
|
|
2202
|
+
text: `ā
Property deleted: ${args.name}`
|
|
2203
|
+
}]
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
throw new Error(`Property not found: ${args.name}`);
|
|
2207
|
+
case 'list':
|
|
2208
|
+
default:
|
|
2209
|
+
const listQuery = args.pattern ? `nameLIKE${args.pattern}` : '';
|
|
2210
|
+
const listResponse = await this.client.searchRecords('sys_properties', listQuery, args.limit || 50);
|
|
2211
|
+
if (listResponse.success && listResponse.data?.result?.length > 0) {
|
|
2212
|
+
return {
|
|
2213
|
+
content: [{
|
|
2214
|
+
type: 'text',
|
|
2215
|
+
text: `š **System Properties** (${listResponse.data.result.length} found)\n\n${listResponse.data.result.map((p) => `⢠**${p.name}**\n Value: ${p.value}\n Type: ${p.type}`).join('\n\n')}`
|
|
2216
|
+
}]
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
return {
|
|
2220
|
+
content: [{
|
|
2221
|
+
type: 'text',
|
|
2222
|
+
text: 'ā ļø No properties found matching criteria'
|
|
2223
|
+
}]
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
catch (error) {
|
|
2228
|
+
this.logger.error('Failed to manage properties:', error);
|
|
2229
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to manage properties: ${error}`);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* Trace execution of a script or flow
|
|
2234
|
+
*/
|
|
2235
|
+
async traceExecution(args) {
|
|
2236
|
+
try {
|
|
2237
|
+
this.logger.info('Tracing execution...');
|
|
2238
|
+
const traceId = `trace_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
2239
|
+
// Enhanced script with detailed tracing
|
|
2240
|
+
const tracedScript = `
|
|
2241
|
+
var snowFlowTrace = {
|
|
2242
|
+
id: '${traceId}',
|
|
2243
|
+
startTime: new GlideDateTime(),
|
|
2244
|
+
steps: [],
|
|
2245
|
+
variables: {},
|
|
2246
|
+
queries: [],
|
|
2247
|
+
apiCalls: [],
|
|
2248
|
+
errors: []
|
|
2249
|
+
};
|
|
2250
|
+
|
|
2251
|
+
// Trace function for logging steps
|
|
2252
|
+
function trace(stepName, details) {
|
|
2253
|
+
snowFlowTrace.steps.push({
|
|
2254
|
+
step: stepName,
|
|
2255
|
+
details: details,
|
|
2256
|
+
timestamp: new GlideDateTime().getDisplayValue(),
|
|
2257
|
+
memory: gs.getSession().getAvailableMemory ? gs.getSession().getAvailableMemory() : 'N/A'
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// Override GlideRecord to trace queries
|
|
2262
|
+
var OriginalGlideRecord = GlideRecord;
|
|
2263
|
+
GlideRecord = function(table) {
|
|
2264
|
+
var gr = new OriginalGlideRecord(table);
|
|
2265
|
+
var originalQuery = gr.query;
|
|
2266
|
+
|
|
2267
|
+
gr.query = function() {
|
|
2268
|
+
var queryString = gr.getEncodedQuery();
|
|
2269
|
+
snowFlowTrace.queries.push({
|
|
2270
|
+
table: table,
|
|
2271
|
+
query: queryString,
|
|
2272
|
+
timestamp: new GlideDateTime().getDisplayValue()
|
|
2273
|
+
});
|
|
2274
|
+
return originalQuery.call(gr);
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
return gr;
|
|
2278
|
+
};
|
|
2279
|
+
|
|
2280
|
+
try {
|
|
2281
|
+
trace('Script Start', 'Beginning execution');
|
|
2282
|
+
|
|
2283
|
+
// User script with tracing
|
|
2284
|
+
${args.script.replace(/;/g, ';\ntrace("Code Execution", "Line executed");')}
|
|
2285
|
+
|
|
2286
|
+
trace('Script Complete', 'Execution finished successfully');
|
|
2287
|
+
|
|
2288
|
+
snowFlowTrace.endTime = new GlideDateTime();
|
|
2289
|
+
snowFlowTrace.duration = GlideDateTime.subtract(snowFlowTrace.startTime, snowFlowTrace.endTime).getNumericValue();
|
|
2290
|
+
snowFlowTrace.success = true;
|
|
2291
|
+
|
|
2292
|
+
} catch(e) {
|
|
2293
|
+
snowFlowTrace.errors.push({
|
|
2294
|
+
error: e.toString(),
|
|
2295
|
+
stack: e.stack || 'No stack trace',
|
|
2296
|
+
timestamp: new GlideDateTime().getDisplayValue()
|
|
2297
|
+
});
|
|
2298
|
+
snowFlowTrace.success = false;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// Store trace for retrieval
|
|
2302
|
+
var prop = new GlideRecord('sys_properties');
|
|
2303
|
+
prop.initialize();
|
|
2304
|
+
prop.name = 'snow_flow.trace.' + snowFlowTrace.id;
|
|
2305
|
+
prop.value = JSON.stringify(snowFlowTrace);
|
|
2306
|
+
prop.type = 'string';
|
|
2307
|
+
prop.description = 'Execution trace from Snow-Flow';
|
|
2308
|
+
prop.insert();
|
|
2309
|
+
|
|
2310
|
+
JSON.stringify({
|
|
2311
|
+
traceId: snowFlowTrace.id,
|
|
2312
|
+
success: snowFlowTrace.success,
|
|
2313
|
+
steps: snowFlowTrace.steps.length,
|
|
2314
|
+
queries: snowFlowTrace.queries.length,
|
|
2315
|
+
errors: snowFlowTrace.errors.length
|
|
2316
|
+
});
|
|
2317
|
+
`;
|
|
2318
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
2319
|
+
const response = await this.client.executeScript(tracedScript);
|
|
2320
|
+
let traceResult;
|
|
2321
|
+
try {
|
|
2322
|
+
traceResult = JSON.parse(response.data?.result || '{}');
|
|
2323
|
+
}
|
|
2324
|
+
catch (e) {
|
|
2325
|
+
traceResult = { traceId: traceId, success: false };
|
|
2326
|
+
}
|
|
2327
|
+
// Wait and retrieve full trace
|
|
2328
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
2329
|
+
const traceResponse = await this.client.searchRecords('sys_properties', `name=snow_flow.trace.${traceId}`, 1);
|
|
2330
|
+
let fullTrace = null;
|
|
2331
|
+
if (traceResponse.success && traceResponse.data?.result?.length > 0) {
|
|
2332
|
+
try {
|
|
2333
|
+
fullTrace = JSON.parse(traceResponse.data.result[0].value);
|
|
2334
|
+
// Clean up
|
|
2335
|
+
await this.client.deleteRecord('sys_properties', traceResponse.data.result[0].sys_id);
|
|
2336
|
+
}
|
|
2337
|
+
catch (e) {
|
|
2338
|
+
this.logger.warn('Could not parse trace data');
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
return {
|
|
2342
|
+
content: [{
|
|
2343
|
+
type: 'text',
|
|
2344
|
+
text: `š **Execution Trace**\n\nš **Trace ID**: ${traceId}\nā
**Success**: ${traceResult.success}\nš **Steps**: ${traceResult.steps}\nš **Queries**: ${traceResult.queries}\nā ļø **Errors**: ${traceResult.errors}\n\n${fullTrace ? `**Detailed Trace**:\n\n${fullTrace.steps.map((s) => `ā° ${s.timestamp}\n š ${s.step}: ${s.details}`).join('\n\n')}\n\n${fullTrace.queries.length > 0 ? `**Database Queries**:\n${fullTrace.queries.map((q) => `⢠Table: ${q.table}\n Query: ${q.query || 'All records'}`).join('\n')}` : ''}\n\n${fullTrace.errors.length > 0 ? `**Errors**:\n${fullTrace.errors.map((e) => `ā ${e.error}\n ${e.stack}`).join('\n')}` : ''}` : 'ā ļø Full trace pending - check sys_properties for details'}`
|
|
2345
|
+
}]
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
catch (error) {
|
|
2349
|
+
this.logger.error('Failed to trace execution:', error);
|
|
2350
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to trace execution: ${error}`);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
1565
2353
|
async run() {
|
|
1566
2354
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
1567
2355
|
await this.server.connect(transport);
|
|
@@ -12,6 +12,7 @@ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
12
12
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
13
13
|
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
14
14
|
const logger_js_1 = require("../utils/logger.js");
|
|
15
|
+
const snow_oauth_js_1 = require("../utils/snow-oauth.js");
|
|
15
16
|
const logger = new logger_js_1.Logger('ServiceNowSystemProperties');
|
|
16
17
|
/**
|
|
17
18
|
* ServiceNow System Properties MCP Server
|
|
@@ -29,6 +30,7 @@ class ServiceNowSystemPropertiesMCP {
|
|
|
29
30
|
},
|
|
30
31
|
});
|
|
31
32
|
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
33
|
+
this.oauth = new snow_oauth_js_1.SnowOAuth();
|
|
32
34
|
this.setupHandlers();
|
|
33
35
|
this.setupTools();
|
|
34
36
|
}
|
|
@@ -318,7 +320,7 @@ class ServiceNowSystemPropertiesMCP {
|
|
|
318
320
|
const { name, arguments: args } = request.params;
|
|
319
321
|
try {
|
|
320
322
|
// Ensure authentication
|
|
321
|
-
const isAuthenticated = await this.
|
|
323
|
+
const isAuthenticated = await this.oauth.isAuthenticated();
|
|
322
324
|
if (!isAuthenticated) {
|
|
323
325
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not authenticated. Please run "snow-flow auth login" first.');
|
|
324
326
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Snow-Flow v3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
|
+
"description": "Snow-Flow v3.4.0: SCRIPT EXECUTION & DEBUGGING SUITE - 9 critical improvements for script development! New tools: script execution with output retrieval, script output history, synchronous execution, system log access, REST connection testing, REST message test suite, enhanced property management, and execution tracing. Capture all gs.print/info/warn/error output, retrieve execution results, trace script flow, test REST integrations, and access system logs. Now 200+ MCP tools across 18 specialized servers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|