snow-flow 2.0.2 → 2.0.3
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/{.claude/mcp-config.json → .mcp.json} +13 -12
- package/.releaserc.json +47 -0
- package/README.md +6 -0
- package/package.json +1 -1
- package/.claude/claude-settings.json +0 -15
- package/.claude/commands/analysis/README.md +0 -9
- package/.claude/commands/automation/README.md +0 -9
- package/.claude/commands/github/README.md +0 -11
- package/.claude/commands/helpers/standard-checkpoint-hooks.sh +0 -179
- package/.claude/commands/hooks/README.md +0 -11
- package/.claude/commands/monitoring/README.md +0 -9
- package/.claude/commands/optimization/README.md +0 -9
- package/.claude/commands/training/README.md +0 -9
- package/.claude/commands/workflows/README.md +0 -9
- package/.claude/config.json +0 -36
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/settings.json +0 -114
- package/.claude/settings.local.json +0 -33
- package/.claude-flow/metrics/agent-metrics.json +0 -1
- package/.claude-flow/metrics/performance.json +0 -9
- package/.claude-flow/metrics/system-metrics.json +0 -15986
- package/.claude-flow/metrics/task-metrics.json +0 -10
- package/.roo/README.md +0 -402
- package/.roo/mcp-list.txt +0 -257
- package/.roo/mcp.json +0 -32
- package/.roo/workflows/basic-tdd.json +0 -32
- package/.roomodes +0 -201
- package/CLAUDE.md +0 -393
- package/bin/snow-flow.js +0 -3
- package/dist/cli.js.bak +0 -2052
- package/dist/config/snow-flow-config.d.ts +0 -1492
- package/dist/config/snow-flow-config.js +0 -938
- package/scripts/create-env.js +0 -91
- package/scripts/postinstall.js +0 -30
- package/scripts/register-mcp-servers.js +0 -210
- package/scripts/reset-mcp-servers.js +0 -246
- package/scripts/setup-mcp.js +0 -106
- package/scripts/update-version.js +0 -31
- package/src/agents/README.md +0 -192
- package/src/agents/base-agent.ts +0 -251
- package/src/agents/coordinator.ts +0 -971
- package/src/agents/index.ts +0 -18
- package/src/agents/queen-403-handler.ts +0 -230
- package/src/agents/queen-agent.ts +0 -1399
- package/src/agents/script-writer-agent.ts +0 -769
- package/src/agents/security-agent.ts +0 -726
- package/src/agents/widget-creator-agent.ts +0 -568
- package/src/api/error-handling.ts +0 -1758
- package/src/api/natural-language-mapper.ts +0 -630
- package/src/api/performance-optimizer.ts +0 -891
- package/src/api/transaction-manager.ts +0 -754
- package/src/cli/deploy-artifact.ts +0 -304
- package/src/cli/snow-flow-cli-integration.ts +0 -434
- package/src/cli.ts +0 -4455
- package/src/cli.ts.bak +0 -5616
- package/src/compliance/advanced-compliance-system.ts +0 -1268
- package/src/compliance/index.ts +0 -19
- package/src/config/snow-flow-config.ts +0 -1019
- package/src/documentation/index.ts +0 -16
- package/src/documentation/self-documenting-system.ts +0 -1421
- package/src/dynamic-version.ts +0 -44
- package/src/healing/index.ts +0 -20
- package/src/healing/self-healing-system.ts +0 -1435
- package/src/health/README.md +0 -161
- package/src/health/system-health.ts +0 -995
- package/src/index.ts +0 -60
- package/src/intelligence/acl-analyzer.ts +0 -377
- package/src/intelligence/auto-resolution-engine.ts +0 -643
- package/src/intelligence/gap-analysis-engine.ts +0 -488
- package/src/intelligence/manual-instructions-generator.ts +0 -744
- package/src/intelligence/mcp-coverage-analyzer.ts +0 -708
- package/src/intelligence/multi-pass-requirements-analyzer.ts +0 -603
- package/src/intelligence/performance-recommendations-engine.ts +0 -1223
- package/src/intelligence/requirements-analyzer.ts +0 -1148
- package/src/intelligence/task-analyzer.ts +0 -628
- package/src/managers/scope-manager.ts +0 -555
- package/src/mcp/advanced/servicenow-advanced-features-mcp.ts +0 -16110
- package/src/mcp/base-mcp-server.ts +0 -660
- package/src/mcp/http-transport-wrapper.ts +0 -426
- package/src/mcp/service-discovery-client.ts +0 -312
- package/src/mcp/servicenow-automation-mcp-refactored.ts +0 -678
- package/src/mcp/servicenow-automation-mcp.ts +0 -838
- package/src/mcp/servicenow-deployment-mcp-refactored.ts +0 -1292
- package/src/mcp/servicenow-deployment-mcp.ts +0 -7244
- package/src/mcp/servicenow-graph-memory-mcp-refactored.ts +0 -721
- package/src/mcp/servicenow-graph-memory-mcp.ts +0 -832
- package/src/mcp/servicenow-integration-mcp-refactored.ts +0 -655
- package/src/mcp/servicenow-integration-mcp.ts +0 -819
- package/src/mcp/servicenow-intelligent-mcp-refactored.ts +0 -977
- package/src/mcp/servicenow-intelligent-mcp.ts +0 -4638
- package/src/mcp/servicenow-mcp-server.ts +0 -728
- package/src/mcp/servicenow-memory-mcp.ts +0 -474
- package/src/mcp/servicenow-operations-mcp-refactored.ts +0 -1995
- package/src/mcp/servicenow-operations-mcp.ts +0 -3384
- package/src/mcp/servicenow-platform-development-mcp-refactored.ts +0 -611
- package/src/mcp/servicenow-platform-development-mcp.ts +0 -970
- package/src/mcp/servicenow-progressive-indexer.ts +0 -370
- package/src/mcp/servicenow-reporting-analytics-mcp-refactored.ts +0 -695
- package/src/mcp/servicenow-reporting-analytics-mcp.ts +0 -1123
- package/src/mcp/servicenow-security-compliance-mcp-refactored.ts +0 -642
- package/src/mcp/servicenow-security-compliance-mcp.ts +0 -1157
- package/src/mcp/servicenow-update-set-mcp-refactored.ts +0 -751
- package/src/mcp/servicenow-update-set-mcp.ts +0 -811
- package/src/mcp/shared/agent-context-provider.ts +0 -365
- package/src/mcp/shared/base-mcp-server.ts +0 -2463
- package/src/mcp/shared/mcp-memory-manager.ts +0 -540
- package/src/mcp/shared/mcp-resource-manager.ts +0 -367
- package/src/mcp/snow-flow-mcp.ts +0 -911
- package/src/mcp/start-all-mcp-servers.ts +0 -102
- package/src/mcp/start-servicenow-mcp.ts +0 -67
- package/src/monitoring/enhanced-monitoring-system.ts +0 -1345
- package/src/monitoring/performance-tracker.ts +0 -777
- package/src/optimization/cost-optimization-engine.ts +0 -1125
- package/src/optimization/index.ts +0 -10
- package/src/queen/README.md +0 -403
- package/src/queen/agent-factory.ts +0 -574
- package/src/queen/index.ts +0 -94
- package/src/queen/mcp-execution-bridge.ts +0 -681
- package/src/queen/neural-learning.ts +0 -319
- package/src/queen/parallel-agent-engine.ts +0 -848
- package/src/queen/queen-memory-system.ts +0 -490
- package/src/queen/queen-memory.ts +0 -456
- package/src/queen/servicenow-queen.ts +0 -1029
- package/src/queen/types.ts +0 -87
- package/src/rollback/smart-rollback-system.ts +0 -866
- package/src/schemas/deployment.schema.json +0 -58
- package/src/schemas/flow.schema.json +0 -79
- package/src/schemas/widget.schema.json +0 -72
- package/src/snow-flow-system.ts +0 -541
- package/src/sparc/sparc-help.ts +0 -49
- package/src/sparc/team-sparc.ts +0 -88
- package/src/strategies/global-scope-strategy.ts +0 -736
- package/src/templates/base/application.template.json +0 -45
- package/src/templates/base/business_rule.template.json +0 -33
- package/src/templates/base/script_include.template.json +0 -18
- package/src/templates/base/table.template.json +0 -64
- package/src/templates/base/widget.template.json +0 -25
- package/src/templates/patterns/composite.incident-management.template.json +0 -140
- package/src/templates/patterns/widget.dashboard.template.json +0 -238
- package/src/templates/patterns/widget.datatable.template.json +0 -292
- package/src/testing/integration-test-suite.ts +0 -1276
- package/src/types/index.ts +0 -183
- package/src/types/neo4j-driver.d.ts +0 -23
- package/src/types/servicenow.types.ts +0 -307
- package/src/types/snow-flow.types.ts +0 -186
- package/src/types/todo.types.ts +0 -70
- package/src/utils/action-type-cache.ts +0 -286
- package/src/utils/agent-detector.ts +0 -573
- package/src/utils/artifact-tracker.ts +0 -379
- package/src/utils/dependency-detector.ts +0 -233
- package/src/utils/deployment-metadata-handler.ts +0 -518
- package/src/utils/error-recovery.ts +0 -680
- package/src/utils/logger.ts +0 -60
- package/src/utils/mcp-auth-middleware.ts +0 -262
- package/src/utils/mcp-config-manager.ts +0 -270
- package/src/utils/mcp-server-manager.ts +0 -446
- package/src/utils/mcp-tool-registry.ts +0 -298
- package/src/utils/migrate-snow-flow.ts +0 -98
- package/src/utils/oauth-html-templates.ts +0 -188
- package/src/utils/scope-utils.ts +0 -443
- package/src/utils/servicenow-client.ts +0 -3321
- package/src/utils/servicenow-id-generator.ts +0 -181
- package/src/utils/snow-memory-manager.ts +0 -605
- package/src/utils/snow-oauth.ts +0 -797
- package/src/utils/template-engine.ts +0 -463
- package/src/utils/theme-manager.ts +0 -295
- package/src/utils/unified-auth-store.ts +0 -222
- package/src/utils/update-set-importer.ts +0 -422
- package/src/utils/update-set-xml-packager.ts +0 -371
- package/src/utils/widget-template-generator.ts +0 -1744
- package/src/version.ts +0 -1950
|
@@ -1,1123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ServiceNow Reporting & Analytics MCP Server
|
|
4
|
-
* Handles reports, dashboards, and analytics operations
|
|
5
|
-
* NO HARDCODED VALUES - All reporting configurations discovered dynamically
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
-
import {
|
|
11
|
-
CallToolRequestSchema,
|
|
12
|
-
ErrorCode,
|
|
13
|
-
ListToolsRequestSchema,
|
|
14
|
-
McpError,
|
|
15
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
16
|
-
import { ServiceNowClient } from '../utils/servicenow-client.js';
|
|
17
|
-
import { mcpAuth } from '../utils/mcp-auth-middleware.js';
|
|
18
|
-
import { mcpConfig } from '../utils/mcp-config-manager.js';
|
|
19
|
-
import { Logger } from '../utils/logger.js';
|
|
20
|
-
|
|
21
|
-
interface ReportDefinition {
|
|
22
|
-
name: string;
|
|
23
|
-
table: string;
|
|
24
|
-
conditions: string;
|
|
25
|
-
fields: string[];
|
|
26
|
-
aggregations: string[];
|
|
27
|
-
groupBy: string[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface DashboardWidget {
|
|
31
|
-
name: string;
|
|
32
|
-
type: string;
|
|
33
|
-
dataSource: string;
|
|
34
|
-
configuration: any;
|
|
35
|
-
layout: any;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
class ServiceNowReportingAnalyticsMCP {
|
|
39
|
-
private server: Server;
|
|
40
|
-
private client: ServiceNowClient;
|
|
41
|
-
private logger: Logger;
|
|
42
|
-
private config: ReturnType<typeof mcpConfig.getConfig>;
|
|
43
|
-
|
|
44
|
-
constructor() {
|
|
45
|
-
this.server = new Server(
|
|
46
|
-
{
|
|
47
|
-
name: 'servicenow-reporting-analytics',
|
|
48
|
-
version: '1.0.0',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
capabilities: {
|
|
52
|
-
tools: {},
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
this.client = new ServiceNowClient();
|
|
58
|
-
this.logger = new Logger('ServiceNowReportingAnalyticsMCP');
|
|
59
|
-
this.config = mcpConfig.getConfig();
|
|
60
|
-
|
|
61
|
-
this.setupHandlers();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private setupHandlers() {
|
|
65
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
66
|
-
tools: [
|
|
67
|
-
{
|
|
68
|
-
name: 'snow_create_report',
|
|
69
|
-
description: 'Create Report with dynamic table and field discovery - NO hardcoded values',
|
|
70
|
-
inputSchema: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
name: { type: 'string', description: 'Report name' },
|
|
74
|
-
table: { type: 'string', description: 'Source table' },
|
|
75
|
-
description: { type: 'string', description: 'Report description' },
|
|
76
|
-
conditions: { type: 'string', description: 'Report conditions/filters' },
|
|
77
|
-
fields: { type: 'array', description: 'Fields to include in report' },
|
|
78
|
-
groupBy: { type: 'array', description: 'Group by fields' },
|
|
79
|
-
aggregations: { type: 'array', description: 'Aggregation functions' },
|
|
80
|
-
sortBy: { type: 'string', description: 'Sort field' },
|
|
81
|
-
sortOrder: { type: 'string', description: 'Sort order (asc/desc)' },
|
|
82
|
-
schedule: { type: 'string', description: 'Report schedule' },
|
|
83
|
-
format: { type: 'string', description: 'Output format (PDF, Excel, CSV)' }
|
|
84
|
-
},
|
|
85
|
-
required: ['name', 'table', 'fields']
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
name: 'snow_create_dashboard',
|
|
90
|
-
description: 'Create Dashboard with dynamic widget discovery',
|
|
91
|
-
inputSchema: {
|
|
92
|
-
type: 'object',
|
|
93
|
-
properties: {
|
|
94
|
-
name: { type: 'string', description: 'Dashboard name' },
|
|
95
|
-
description: { type: 'string', description: 'Dashboard description' },
|
|
96
|
-
layout: { type: 'string', description: 'Dashboard layout (grid, tabs, accordion)' },
|
|
97
|
-
widgets: { type: 'array', description: 'Dashboard widgets configuration' },
|
|
98
|
-
permissions: { type: 'array', description: 'User/role permissions' },
|
|
99
|
-
refreshInterval: { type: 'number', description: 'Auto-refresh interval in minutes' },
|
|
100
|
-
public: { type: 'boolean', description: 'Public dashboard' }
|
|
101
|
-
},
|
|
102
|
-
required: ['name', 'widgets']
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: 'snow_create_kpi',
|
|
107
|
-
description: 'Create KPI with dynamic metric discovery',
|
|
108
|
-
inputSchema: {
|
|
109
|
-
type: 'object',
|
|
110
|
-
properties: {
|
|
111
|
-
name: { type: 'string', description: 'KPI name' },
|
|
112
|
-
description: { type: 'string', description: 'KPI description' },
|
|
113
|
-
table: { type: 'string', description: 'Source table' },
|
|
114
|
-
metric: { type: 'string', description: 'Metric to measure' },
|
|
115
|
-
aggregation: { type: 'string', description: 'Aggregation function (count, sum, avg, max, min)' },
|
|
116
|
-
conditions: { type: 'string', description: 'KPI conditions/filters' },
|
|
117
|
-
target: { type: 'number', description: 'Target value' },
|
|
118
|
-
threshold: { type: 'object', description: 'Threshold configuration' },
|
|
119
|
-
unit: { type: 'string', description: 'Unit of measurement' },
|
|
120
|
-
frequency: { type: 'string', description: 'Update frequency' }
|
|
121
|
-
},
|
|
122
|
-
required: ['name', 'table', 'metric', 'aggregation']
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: 'snow_create_data_visualization',
|
|
127
|
-
description: 'Create Data Visualization with dynamic chart discovery',
|
|
128
|
-
inputSchema: {
|
|
129
|
-
type: 'object',
|
|
130
|
-
properties: {
|
|
131
|
-
name: { type: 'string', description: 'Visualization name' },
|
|
132
|
-
type: { type: 'string', description: 'Chart type (bar, line, pie, scatter, etc.)' },
|
|
133
|
-
dataSource: { type: 'string', description: 'Data source (table or report)' },
|
|
134
|
-
xAxis: { type: 'string', description: 'X-axis field' },
|
|
135
|
-
yAxis: { type: 'string', description: 'Y-axis field' },
|
|
136
|
-
series: { type: 'array', description: 'Data series configuration' },
|
|
137
|
-
filters: { type: 'array', description: 'Chart filters' },
|
|
138
|
-
colors: { type: 'array', description: 'Color palette' },
|
|
139
|
-
interactive: { type: 'boolean', description: 'Interactive chart' }
|
|
140
|
-
},
|
|
141
|
-
required: ['name', 'type', 'dataSource']
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: 'snow_create_performance_analytics',
|
|
146
|
-
description: 'Create Performance Analytics with dynamic metric discovery',
|
|
147
|
-
inputSchema: {
|
|
148
|
-
type: 'object',
|
|
149
|
-
properties: {
|
|
150
|
-
name: { type: 'string', description: 'Analytics name' },
|
|
151
|
-
category: { type: 'string', description: 'Analytics category' },
|
|
152
|
-
dataSource: { type: 'string', description: 'Data source table' },
|
|
153
|
-
metrics: { type: 'array', description: 'Performance metrics to track' },
|
|
154
|
-
dimensions: { type: 'array', description: 'Analysis dimensions' },
|
|
155
|
-
timeframe: { type: 'string', description: 'Time period for _analysis' },
|
|
156
|
-
benchmarks: { type: 'array', description: 'Performance benchmarks' },
|
|
157
|
-
alerts: { type: 'array', description: 'Alert configurations' }
|
|
158
|
-
},
|
|
159
|
-
required: ['name', 'dataSource', 'metrics']
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: 'snow_create_scheduled_report',
|
|
164
|
-
description: 'Create Scheduled Report with dynamic delivery discovery',
|
|
165
|
-
inputSchema: {
|
|
166
|
-
type: 'object',
|
|
167
|
-
properties: {
|
|
168
|
-
reportName: { type: 'string', description: 'Source report name' },
|
|
169
|
-
schedule: { type: 'string', description: 'Schedule frequency' },
|
|
170
|
-
recipients: { type: 'array', description: 'Email recipients' },
|
|
171
|
-
format: { type: 'string', description: 'Report format (PDF, Excel, CSV)' },
|
|
172
|
-
conditions: { type: 'string', description: 'Additional conditions' },
|
|
173
|
-
subject: { type: 'string', description: 'Email subject' },
|
|
174
|
-
message: { type: 'string', description: 'Email message' }
|
|
175
|
-
},
|
|
176
|
-
required: ['reportName', 'schedule', 'recipients']
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: 'snow_discover_reporting_tables',
|
|
181
|
-
description: 'Discover all tables available for reporting',
|
|
182
|
-
inputSchema: {
|
|
183
|
-
type: 'object',
|
|
184
|
-
properties: {
|
|
185
|
-
category: { type: 'string', description: 'Table category filter' },
|
|
186
|
-
hasData: { type: 'boolean', description: 'Only tables with data' }
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
name: 'snow_discover_report_fields',
|
|
192
|
-
description: 'Discover available fields for reporting on a table',
|
|
193
|
-
inputSchema: {
|
|
194
|
-
type: 'object',
|
|
195
|
-
properties: {
|
|
196
|
-
table: { type: 'string', description: 'Table name to analyze' },
|
|
197
|
-
fieldType: { type: 'string', description: 'Filter by field type' }
|
|
198
|
-
},
|
|
199
|
-
required: ['table']
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
name: 'snow_analyze_data_quality',
|
|
204
|
-
description: 'Analyze data quality for reporting',
|
|
205
|
-
inputSchema: {
|
|
206
|
-
type: 'object',
|
|
207
|
-
properties: {
|
|
208
|
-
table: { type: 'string', description: 'Table to analyze' },
|
|
209
|
-
fields: { type: 'array', description: 'Specific fields to analyze' },
|
|
210
|
-
checkCompleteness: { type: 'boolean', description: 'Check data completeness' },
|
|
211
|
-
checkConsistency: { type: 'boolean', description: 'Check data consistency' },
|
|
212
|
-
checkAccuracy: { type: 'boolean', description: 'Check data accuracy' }
|
|
213
|
-
},
|
|
214
|
-
required: ['table']
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: 'snow_generate_insights',
|
|
219
|
-
description: 'Generate data insights and recommendations',
|
|
220
|
-
inputSchema: {
|
|
221
|
-
type: 'object',
|
|
222
|
-
properties: {
|
|
223
|
-
table: { type: 'string', description: 'Table to analyze' },
|
|
224
|
-
analysisType: { type: 'string', description: 'Analysis type (trends, patterns, anomalies)' },
|
|
225
|
-
timeframe: { type: 'string', description: 'Time period for _analysis' },
|
|
226
|
-
generateRecommendations: { type: 'boolean', description: 'Generate recommendations' }
|
|
227
|
-
},
|
|
228
|
-
required: ['table']
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
name: 'snow_export_report_data',
|
|
233
|
-
description: 'Export report data in various formats',
|
|
234
|
-
inputSchema: {
|
|
235
|
-
type: 'object',
|
|
236
|
-
properties: {
|
|
237
|
-
reportName: { type: 'string', description: 'Report name to export' },
|
|
238
|
-
format: { type: 'string', description: 'Export format (CSV, Excel, JSON, XML)' },
|
|
239
|
-
includeHeaders: { type: 'boolean', description: 'Include column headers' },
|
|
240
|
-
maxRows: { type: 'number', description: 'Maximum rows to export' }
|
|
241
|
-
},
|
|
242
|
-
required: ['reportName', 'format']
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
]
|
|
246
|
-
}));
|
|
247
|
-
|
|
248
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
249
|
-
try {
|
|
250
|
-
const { name, arguments: args } = request.params;
|
|
251
|
-
|
|
252
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
253
|
-
if (!authResult.success) {
|
|
254
|
-
throw new McpError(ErrorCode.InternalError, authResult.error || 'Authentication required');
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
switch (name) {
|
|
258
|
-
case 'snow_create_report':
|
|
259
|
-
return await this.createReport(args);
|
|
260
|
-
case 'snow_create_dashboard':
|
|
261
|
-
return await this.createDashboard(args);
|
|
262
|
-
case 'snow_create_kpi':
|
|
263
|
-
return await this.createKPI(args);
|
|
264
|
-
case 'snow_create_data_visualization':
|
|
265
|
-
return await this.createDataVisualization(args);
|
|
266
|
-
case 'snow_create_performance_analytics':
|
|
267
|
-
return await this.createPerformanceAnalytics(args);
|
|
268
|
-
case 'snow_create_scheduled_report':
|
|
269
|
-
return await this.createScheduledReport(args);
|
|
270
|
-
case 'snow_discover_reporting_tables':
|
|
271
|
-
return await this.discoverReportingTables(args);
|
|
272
|
-
case 'snow_discover_report_fields':
|
|
273
|
-
return await this.discoverReportFields(args);
|
|
274
|
-
case 'snow_analyze_data_quality':
|
|
275
|
-
return await this.analyzeDataQuality(args);
|
|
276
|
-
case 'snow_generate_insights':
|
|
277
|
-
return await this.generateInsights(args);
|
|
278
|
-
case 'snow_export_report_data':
|
|
279
|
-
return await this.exportReportData(args);
|
|
280
|
-
default:
|
|
281
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
282
|
-
}
|
|
283
|
-
} catch (error) {
|
|
284
|
-
this.logger.error(`Error in ${request.params.name}:`, error);
|
|
285
|
-
throw error;
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Create Report with dynamic discovery
|
|
292
|
-
*/
|
|
293
|
-
private async createReport(args: any) {
|
|
294
|
-
try {
|
|
295
|
-
this.logger.info('Creating Report...');
|
|
296
|
-
|
|
297
|
-
// Validate table and discover fields
|
|
298
|
-
const tableInfo = await this.getTableInfo(args.table);
|
|
299
|
-
if (!tableInfo) {
|
|
300
|
-
throw new Error(`Table not found: ${args.table}`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const availableFields = await this.getTableFields(args.table);
|
|
304
|
-
const aggregationFunctions = await this.getAggregationFunctions();
|
|
305
|
-
|
|
306
|
-
const reportData = {
|
|
307
|
-
name: args.name,
|
|
308
|
-
table: tableInfo.name,
|
|
309
|
-
description: args.description || '',
|
|
310
|
-
conditions: args.conditions || '',
|
|
311
|
-
fields: JSON.stringify(args.fields || []),
|
|
312
|
-
group_by: JSON.stringify(args.groupBy || []),
|
|
313
|
-
aggregations: JSON.stringify(args.aggregations || []),
|
|
314
|
-
sort_by: args.sortBy || '',
|
|
315
|
-
sort_order: args.sortOrder || 'asc',
|
|
316
|
-
schedule: args.schedule || '',
|
|
317
|
-
format: args.format || 'HTML'
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
321
|
-
const response = await this.client.createRecord('sys_report', reportData);
|
|
322
|
-
|
|
323
|
-
if (!response.success) {
|
|
324
|
-
throw new Error(`Failed to create Report: ${response.error}`);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
content: [{
|
|
329
|
-
type: 'text',
|
|
330
|
-
text: `✅ Report created successfully!\n\n📊 **${args.name}**\n🆔 sys_id: ${response.data.sys_id}\n📋 Table: ${tableInfo.label} (${tableInfo.name})\n📝 Fields: ${args.fields?.join(', ') || 'Default fields'}\n${args.groupBy?.length ? `📊 Group By: ${args.groupBy.join(', ')}\n` : ''}${args.aggregations?.length ? `🔢 Aggregations: ${args.aggregations.join(', ')}\n` : ''}${args.conditions ? `🔍 Conditions: ${args.conditions}\n` : ''}📄 Format: ${args.format || 'HTML'}\n\n📝 Description: ${args.description || 'No description provided'}\n\n✨ Created with dynamic table and field discovery!`
|
|
331
|
-
}]
|
|
332
|
-
};
|
|
333
|
-
} catch (error) {
|
|
334
|
-
this.logger.error('Failed to create Report:', error);
|
|
335
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create Report: ${error}`);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Create Dashboard with dynamic widget discovery
|
|
341
|
-
*/
|
|
342
|
-
private async createDashboard(args: any) {
|
|
343
|
-
try {
|
|
344
|
-
this.logger.info('Creating Dashboard...');
|
|
345
|
-
|
|
346
|
-
// Get available widget types and layouts
|
|
347
|
-
const widgetTypes = await this.getWidgetTypes();
|
|
348
|
-
const layouts = await this.getDashboardLayouts();
|
|
349
|
-
|
|
350
|
-
const dashboardData = {
|
|
351
|
-
name: args.name,
|
|
352
|
-
description: args.description || '',
|
|
353
|
-
layout: args.layout || 'grid',
|
|
354
|
-
widgets: JSON.stringify(args.widgets || []),
|
|
355
|
-
permissions: JSON.stringify(args.permissions || []),
|
|
356
|
-
refresh_interval: args.refreshInterval || 15,
|
|
357
|
-
public: args.public || false
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
361
|
-
const response = await this.client.createRecord('sys_dashboard', dashboardData);
|
|
362
|
-
|
|
363
|
-
if (!response.success) {
|
|
364
|
-
throw new Error(`Failed to create Dashboard: ${response.error}`);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return {
|
|
368
|
-
content: [{
|
|
369
|
-
type: 'text',
|
|
370
|
-
text: `✅ Dashboard created successfully!\n\n📊 **${args.name}**\n🆔 sys_id: ${response.data.sys_id}\n🎨 Layout: ${args.layout || 'grid'}\n📱 Widgets: ${args.widgets?.length || 0} widgets configured\n🔄 Refresh: ${args.refreshInterval || 15} minutes\n${args.public ? '🌐 Public: Yes\n' : '🔒 Private dashboard\n'}👥 Permissions: ${args.permissions?.length || 0} configured\n\n📝 Description: ${args.description || 'No description provided'}\n\n✨ Created with dynamic widget discovery!`
|
|
371
|
-
}]
|
|
372
|
-
};
|
|
373
|
-
} catch (error) {
|
|
374
|
-
this.logger.error('Failed to create Dashboard:', error);
|
|
375
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create Dashboard: ${error}`);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Create KPI with dynamic metric discovery
|
|
381
|
-
*/
|
|
382
|
-
private async createKPI(args: any) {
|
|
383
|
-
try {
|
|
384
|
-
this.logger.info('Creating KPI...');
|
|
385
|
-
|
|
386
|
-
// Validate table and discover metrics
|
|
387
|
-
const tableInfo = await this.getTableInfo(args.table);
|
|
388
|
-
if (!tableInfo) {
|
|
389
|
-
throw new Error(`Table not found: ${args.table}`);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const availableMetrics = await this.getAvailableMetrics(args.table);
|
|
393
|
-
|
|
394
|
-
const kpiData = {
|
|
395
|
-
name: args.name,
|
|
396
|
-
description: args.description || '',
|
|
397
|
-
table: tableInfo.name,
|
|
398
|
-
metric: args.metric,
|
|
399
|
-
aggregation: args.aggregation,
|
|
400
|
-
conditions: args.conditions || '',
|
|
401
|
-
target: args.target || 0,
|
|
402
|
-
threshold: JSON.stringify(args.threshold || {}),
|
|
403
|
-
unit: args.unit || '',
|
|
404
|
-
frequency: args.frequency || 'daily'
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
408
|
-
// Try pa_indicators (Performance Analytics) for KPIs
|
|
409
|
-
let response = await this.client.createRecord('pa_indicators', {
|
|
410
|
-
name: args.name,
|
|
411
|
-
label: args.name,
|
|
412
|
-
description: args.description || '',
|
|
413
|
-
facts_table: tableInfo.name,
|
|
414
|
-
aggregate: args.aggregation,
|
|
415
|
-
field: args.metric,
|
|
416
|
-
conditions: args.conditions || '',
|
|
417
|
-
unit: args.unit || '',
|
|
418
|
-
direction: args.target ? 'minimize' : 'maximize',
|
|
419
|
-
frequency: args.frequency || 'daily',
|
|
420
|
-
active: true
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Fallback to metric_definition if pa_indicators fails
|
|
424
|
-
if (!response.success && response.error?.includes('400')) {
|
|
425
|
-
this.logger.warn('pa_indicators failed, trying metric_definition table...');
|
|
426
|
-
response = await this.client.createRecord('metric_definition', {
|
|
427
|
-
name: args.name,
|
|
428
|
-
description: args.description || '',
|
|
429
|
-
table: tableInfo.name,
|
|
430
|
-
field: args.metric,
|
|
431
|
-
method: args.aggregation,
|
|
432
|
-
condition: args.conditions || '',
|
|
433
|
-
active: true
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (!response.success) {
|
|
438
|
-
throw new Error(`Failed to create KPI: ${response.error}`);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return {
|
|
442
|
-
content: [{
|
|
443
|
-
type: 'text',
|
|
444
|
-
text: `✅ KPI created successfully!\n\n📈 **${args.name}**\n🆔 sys_id: ${response.data.sys_id}\n📋 Table: ${tableInfo.label} (${tableInfo.name})\n📊 Metric: ${args.metric} (${args.aggregation})\n🎯 Target: ${args.target || 'Not set'}${args.unit ? ` ${args.unit}` : ''}\n📅 Frequency: ${args.frequency || 'daily'}\n${args.conditions ? `🔍 Conditions: ${args.conditions}\n` : ''}\n📝 Description: ${args.description || 'No description provided'}\n\n✨ Created with dynamic metric discovery!`
|
|
445
|
-
}]
|
|
446
|
-
};
|
|
447
|
-
} catch (error) {
|
|
448
|
-
this.logger.error('Failed to create KPI:', error);
|
|
449
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create KPI: ${error}`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Create Data Visualization with dynamic chart discovery
|
|
455
|
-
*/
|
|
456
|
-
private async createDataVisualization(args: any) {
|
|
457
|
-
try {
|
|
458
|
-
this.logger.info('Creating Data Visualization...');
|
|
459
|
-
|
|
460
|
-
// Get available chart types and validate data source
|
|
461
|
-
const chartTypes = await this.getChartTypes();
|
|
462
|
-
const dataSourceInfo = await this.getDataSourceInfo(args.dataSource);
|
|
463
|
-
|
|
464
|
-
const visualizationData = {
|
|
465
|
-
name: args.name,
|
|
466
|
-
type: args.type,
|
|
467
|
-
data_source: args.dataSource,
|
|
468
|
-
x_axis: args.xAxis || '',
|
|
469
|
-
y_axis: args.yAxis || '',
|
|
470
|
-
series: JSON.stringify(args.series || []),
|
|
471
|
-
filters: JSON.stringify(args.filters || []),
|
|
472
|
-
colors: JSON.stringify(args.colors || []),
|
|
473
|
-
interactive: args.interactive !== false
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
477
|
-
// Try sys_report_chart for visualizations
|
|
478
|
-
let response = await this.client.createRecord('sys_report_chart', {
|
|
479
|
-
name: args.name,
|
|
480
|
-
title: args.name,
|
|
481
|
-
type: args.type,
|
|
482
|
-
table: args.dataSource,
|
|
483
|
-
x_axis_field: args.xAxis || '',
|
|
484
|
-
y_axis_field: args.yAxis || '',
|
|
485
|
-
chart_type: args.type,
|
|
486
|
-
series_config: JSON.stringify(args.series || []),
|
|
487
|
-
filter: args.filters ? JSON.stringify(args.filters) : '',
|
|
488
|
-
color_palette: JSON.stringify(args.colors || []),
|
|
489
|
-
is_real_time: args.interactive !== false,
|
|
490
|
-
active: true
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Fallback to sys_report if chart table fails
|
|
494
|
-
if (!response.success && response.error?.includes('400')) {
|
|
495
|
-
this.logger.warn('sys_report_chart failed, trying sys_report table...');
|
|
496
|
-
response = await this.client.createRecord('sys_report', {
|
|
497
|
-
title: args.name,
|
|
498
|
-
description: `Chart: ${args.type}`,
|
|
499
|
-
table: args.dataSource,
|
|
500
|
-
type: 'chart',
|
|
501
|
-
chart_type: args.type,
|
|
502
|
-
field: args.yAxis || args.xAxis || '',
|
|
503
|
-
filter: args.filters ? JSON.stringify(args.filters) : '',
|
|
504
|
-
is_scheduled: false,
|
|
505
|
-
active: true
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
if (!response.success) {
|
|
510
|
-
throw new Error(`Failed to create Data Visualization: ${response.error}`);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
content: [{
|
|
515
|
-
type: 'text',
|
|
516
|
-
text: `✅ Data Visualization created successfully!\n\n📊 **${args.name}**\n🆔 sys_id: ${response.data.sys_id}\n📈 Type: ${args.type}\n📊 Data Source: ${args.dataSource}\n${args.xAxis ? `📏 X-Axis: ${args.xAxis}\n` : ''}${args.yAxis ? `📐 Y-Axis: ${args.yAxis}\n` : ''}🎨 Series: ${args.series?.length || 0} configured\n🔍 Filters: ${args.filters?.length || 0} applied\n${args.interactive !== false ? '🖱️ Interactive: Yes\n' : '📊 Static chart\n'}\n✨ Created with dynamic chart type discovery!`
|
|
517
|
-
}]
|
|
518
|
-
};
|
|
519
|
-
} catch (error) {
|
|
520
|
-
this.logger.error('Failed to create Data Visualization:', error);
|
|
521
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create Data Visualization: ${error}`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Create Performance Analytics with dynamic metric discovery
|
|
527
|
-
*/
|
|
528
|
-
private async createPerformanceAnalytics(args: any) {
|
|
529
|
-
try {
|
|
530
|
-
this.logger.info('Creating Performance Analytics...');
|
|
531
|
-
|
|
532
|
-
// Validate data source and discover metrics
|
|
533
|
-
const dataSourceInfo = await this.getDataSourceInfo(args.dataSource);
|
|
534
|
-
const availableMetrics = await this.getPerformanceMetrics(args.dataSource);
|
|
535
|
-
|
|
536
|
-
const analyticsData = {
|
|
537
|
-
name: args.name,
|
|
538
|
-
category: args.category || 'general',
|
|
539
|
-
data_source: args.dataSource,
|
|
540
|
-
metrics: JSON.stringify(args.metrics || []),
|
|
541
|
-
dimensions: JSON.stringify(args.dimensions || []),
|
|
542
|
-
timeframe: args.timeframe || '30d',
|
|
543
|
-
benchmarks: JSON.stringify(args.benchmarks || []),
|
|
544
|
-
alerts: JSON.stringify(args.alerts || [])
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
548
|
-
// Try pa_cubes for performance analytics
|
|
549
|
-
let response = await this.client.createRecord('pa_cubes', {
|
|
550
|
-
name: args.name,
|
|
551
|
-
label: args.name,
|
|
552
|
-
description: args.category || 'Performance Analytics',
|
|
553
|
-
facts_table: args.dataSource,
|
|
554
|
-
aggregate: 'COUNT',
|
|
555
|
-
field: '*',
|
|
556
|
-
conditions: '',
|
|
557
|
-
active: true
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
// Fallback to pa_indicators if pa_cubes fails
|
|
561
|
-
if (!response.success && response.error?.includes('400')) {
|
|
562
|
-
this.logger.warn('pa_cubes failed, trying pa_indicators...');
|
|
563
|
-
response = await this.client.createRecord('pa_indicators', {
|
|
564
|
-
name: args.name,
|
|
565
|
-
label: args.name,
|
|
566
|
-
description: args.category || 'Performance Analytics',
|
|
567
|
-
facts_table: args.dataSource,
|
|
568
|
-
aggregate: 'COUNT',
|
|
569
|
-
field: '*',
|
|
570
|
-
unit: 'integer',
|
|
571
|
-
direction: 'maximize',
|
|
572
|
-
frequency: args.timeframe || 'daily',
|
|
573
|
-
active: true
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (!response.success) {
|
|
578
|
-
throw new Error(`Failed to create Performance Analytics: ${response.error}`);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return {
|
|
582
|
-
content: [{
|
|
583
|
-
type: 'text',
|
|
584
|
-
text: `✅ Performance Analytics created successfully!\n\n📊 **${args.name}**\n🆔 sys_id: ${response.data.sys_id}\n📂 Category: ${args.category || 'general'}\n📊 Data Source: ${args.dataSource}\n📈 Metrics: ${args.metrics?.length || 0} configured\n📐 Dimensions: ${args.dimensions?.length || 0} configured\n📅 Timeframe: ${args.timeframe || '30d'}\n🎯 Benchmarks: ${args.benchmarks?.length || 0} configured\n🚨 Alerts: ${args.alerts?.length || 0} configured\n\n✨ Created with dynamic performance metric discovery!`
|
|
585
|
-
}]
|
|
586
|
-
};
|
|
587
|
-
} catch (error) {
|
|
588
|
-
this.logger.error('Failed to create Performance Analytics:', error);
|
|
589
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create Performance Analytics: ${error}`);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Create Scheduled Report with dynamic delivery discovery
|
|
595
|
-
*/
|
|
596
|
-
private async createScheduledReport(args: any) {
|
|
597
|
-
try {
|
|
598
|
-
this.logger.info('Creating Scheduled Report...');
|
|
599
|
-
|
|
600
|
-
// Find the source report
|
|
601
|
-
const sourceReport = await this.findReport(args.reportName);
|
|
602
|
-
if (!sourceReport) {
|
|
603
|
-
throw new Error(`Report not found: ${args.reportName}`);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const scheduledReportData = {
|
|
607
|
-
name: `Scheduled: ${args.reportName}`,
|
|
608
|
-
report: sourceReport.sys_id,
|
|
609
|
-
schedule: args.schedule,
|
|
610
|
-
recipients: JSON.stringify(args.recipients || []),
|
|
611
|
-
format: args.format || 'PDF',
|
|
612
|
-
conditions: args.conditions || '',
|
|
613
|
-
subject: args.subject || `Scheduled Report: ${args.reportName}`,
|
|
614
|
-
message: args.message || 'Please find the attached scheduled report.'
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
618
|
-
// Use sysauto_report for scheduled reports
|
|
619
|
-
let response = await this.client.createRecord('sysauto_report', {
|
|
620
|
-
name: `Scheduled: ${args.reportName}`,
|
|
621
|
-
report: sourceReport.sys_id,
|
|
622
|
-
run_as: 'user',
|
|
623
|
-
run_time: args.schedule,
|
|
624
|
-
email_to: args.recipients?.join(',') || '',
|
|
625
|
-
format: args.format?.toLowerCase() || 'pdf',
|
|
626
|
-
condition: args.conditions || '',
|
|
627
|
-
subject: args.subject || `Scheduled Report: ${args.reportName}`,
|
|
628
|
-
body: args.message || 'Please find the attached report.',
|
|
629
|
-
active: true
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
// Fallback to scheduled_report if sysauto_report fails
|
|
633
|
-
if (!response.success && response.error?.includes('400')) {
|
|
634
|
-
this.logger.warn('sysauto_report failed, trying scheduled_report...');
|
|
635
|
-
response = await this.client.createRecord('scheduled_report', {
|
|
636
|
-
name: `Scheduled: ${args.reportName}`,
|
|
637
|
-
report: sourceReport.sys_id,
|
|
638
|
-
schedule_type: 'daily',
|
|
639
|
-
email_list: args.recipients?.join(';') || '',
|
|
640
|
-
export_format: args.format?.toLowerCase() || 'pdf',
|
|
641
|
-
active: true
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
if (!response.success) {
|
|
646
|
-
throw new Error(`Failed to create Scheduled Report: ${response.error}`);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return {
|
|
650
|
-
content: [{
|
|
651
|
-
type: 'text',
|
|
652
|
-
text: `✅ Scheduled Report created successfully!\n\n📧 **Scheduled: ${args.reportName}**\n🆔 sys_id: ${response.data.sys_id}\n📊 Source Report: ${sourceReport.name}\n📅 Schedule: ${args.schedule}\n📄 Format: ${args.format || 'PDF'}\n📧 Recipients: ${args.recipients?.length || 0} configured\n📝 Subject: ${args.subject || `Scheduled Report: ${args.reportName}`}\n\n✨ Created with dynamic report discovery!`
|
|
653
|
-
}]
|
|
654
|
-
};
|
|
655
|
-
} catch (error) {
|
|
656
|
-
this.logger.error('Failed to create Scheduled Report:', error);
|
|
657
|
-
throw new McpError(ErrorCode.InternalError, `Failed to create Scheduled Report: ${error}`);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* Discover reporting tables
|
|
663
|
-
*/
|
|
664
|
-
private async discoverReportingTables(args: any) {
|
|
665
|
-
try {
|
|
666
|
-
this.logger.info('Discovering reporting tables...');
|
|
667
|
-
|
|
668
|
-
let query = '';
|
|
669
|
-
if (args?.category) {
|
|
670
|
-
query = `sys_class_name=${args.category}`;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const tables = await this.client.searchRecords('sys_db_object', query, 100);
|
|
674
|
-
if (!tables.success) {
|
|
675
|
-
throw new Error('Failed to discover reporting tables');
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// Categorize tables by type
|
|
679
|
-
const categories = [
|
|
680
|
-
{ name: 'ITSM', tables: [] as any[] },
|
|
681
|
-
{ name: 'ITOM', tables: [] as any[] },
|
|
682
|
-
{ name: 'HR', tables: [] as any[] },
|
|
683
|
-
{ name: 'Security', tables: [] as any[] },
|
|
684
|
-
{ name: 'Custom', tables: [] as any[] },
|
|
685
|
-
{ name: 'System', tables: [] as any[] }
|
|
686
|
-
];
|
|
687
|
-
|
|
688
|
-
tables.data.result.forEach((table: any) => {
|
|
689
|
-
const category = this.categorizeTable(table.name);
|
|
690
|
-
const categoryObj = categories.find(c => c.name === category);
|
|
691
|
-
if (categoryObj) {
|
|
692
|
-
categoryObj.tables.push(table);
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
return {
|
|
697
|
-
content: [{
|
|
698
|
-
type: 'text',
|
|
699
|
-
text: `📊 Discovered Reporting Tables:\n\n${categories.filter(cat => cat.tables.length > 0).map(category =>
|
|
700
|
-
`**${category.name} Tables:**\n${category.tables.slice(0, 10).map((table: any) =>
|
|
701
|
-
`- ${table.label || table.name} (${table.name})\n ${table.super_class ? `Extends: ${table.super_class}` : 'Base table'}`
|
|
702
|
-
).join('\n')}${category.tables.length > 10 ? `\n ... and ${category.tables.length - 10} more` : ''}`
|
|
703
|
-
).join('\n\n')}\n\n✨ Total tables: ${tables.data.result.length}\n🔍 All tables discovered dynamically!`
|
|
704
|
-
}]
|
|
705
|
-
};
|
|
706
|
-
} catch (error) {
|
|
707
|
-
this.logger.error('Failed to discover reporting tables:', error);
|
|
708
|
-
throw new McpError(ErrorCode.InternalError, `Failed to discover reporting tables: ${error}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* Discover report fields
|
|
714
|
-
*/
|
|
715
|
-
private async discoverReportFields(args: any) {
|
|
716
|
-
try {
|
|
717
|
-
this.logger.info(`Discovering report fields for table: ${args.table}`);
|
|
718
|
-
|
|
719
|
-
// Get table info
|
|
720
|
-
const tableInfo = await this.getTableInfo(args.table);
|
|
721
|
-
if (!tableInfo) {
|
|
722
|
-
throw new Error(`Table not found: ${args.table}`);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Get fields
|
|
726
|
-
let query = `nameSTARTSWITH${args.table}^element!=NULL`;
|
|
727
|
-
if (args.fieldType) {
|
|
728
|
-
query += `^internal_type=${args.fieldType}`;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
const fields = await this.client.searchRecords('sys_dictionary', query, 100);
|
|
732
|
-
if (!fields.success) {
|
|
733
|
-
throw new Error('Failed to discover report fields');
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// Categorize fields by type
|
|
737
|
-
const fieldTypes = fields.data.result.reduce((acc: any, field: any) => {
|
|
738
|
-
const type = field.internal_type || 'unknown';
|
|
739
|
-
if (!acc[type]) acc[type] = [];
|
|
740
|
-
acc[type].push(field);
|
|
741
|
-
return acc;
|
|
742
|
-
}, {});
|
|
743
|
-
|
|
744
|
-
return {
|
|
745
|
-
content: [{
|
|
746
|
-
type: 'text',
|
|
747
|
-
text: `📊 Report Fields for **${tableInfo.label}** (${tableInfo.name}):\n\n${Object.entries(fieldTypes).map(([type, typeFields]) =>
|
|
748
|
-
`**${type.toUpperCase()} Fields:**\n${(typeFields as any[]).slice(0, 10).map((field: any) =>
|
|
749
|
-
`- ${field.column_label || field.element} (${field.element})\n ${field.comments || 'No description'}`
|
|
750
|
-
).join('\n')}${(typeFields as any[]).length > 10 ? `\n ... and ${(typeFields as any[]).length - 10} more` : ''}`
|
|
751
|
-
).join('\n\n')}\n\n✨ Total fields: ${fields.data.result.length}\n🔍 All fields discovered dynamically!`
|
|
752
|
-
}]
|
|
753
|
-
};
|
|
754
|
-
} catch (error) {
|
|
755
|
-
this.logger.error('Failed to discover report fields:', error);
|
|
756
|
-
throw new McpError(ErrorCode.InternalError, `Failed to discover report fields: ${error}`);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Analyze data quality
|
|
762
|
-
*/
|
|
763
|
-
private async analyzeDataQuality(args: any) {
|
|
764
|
-
try {
|
|
765
|
-
this.logger.info(`Analyzing data quality for table: ${args.table}`);
|
|
766
|
-
|
|
767
|
-
// Get table info
|
|
768
|
-
const tableInfo = await this.getTableInfo(args.table);
|
|
769
|
-
if (!tableInfo) {
|
|
770
|
-
throw new Error(`Table not found: ${args.table}`);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Get sample data for analysis
|
|
774
|
-
const sampleData = await this.client.searchRecords(args.table, '', 100);
|
|
775
|
-
if (!sampleData.success) {
|
|
776
|
-
throw new Error('Failed to retrieve sample data');
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// Analyze data quality
|
|
780
|
-
const _analysis = {
|
|
781
|
-
table: args.table,
|
|
782
|
-
totalRecords: sampleData.data.result.length,
|
|
783
|
-
completeness: this.analyzeCompleteness(sampleData.data.result, args.fields),
|
|
784
|
-
consistency: this.analyzeConsistency(sampleData.data.result, args.fields),
|
|
785
|
-
accuracy: this.analyzeAccuracy(sampleData.data.result, args.fields),
|
|
786
|
-
issues: [] as any[]
|
|
787
|
-
};
|
|
788
|
-
|
|
789
|
-
// Generate quality score
|
|
790
|
-
const qualityScore = (_analysis.completeness.score + _analysis.consistency.score + _analysis.accuracy.score) / 3;
|
|
791
|
-
|
|
792
|
-
return {
|
|
793
|
-
content: [{
|
|
794
|
-
type: 'text',
|
|
795
|
-
text: `📊 Data Quality Analysis for **${tableInfo.label}** (${tableInfo.name}):\n\n📈 **Overall Quality Score: ${qualityScore.toFixed(1)}%**\n\n📋 **Sample Size:** ${_analysis.totalRecords} records\n\n🔍 **Quality Metrics:**\n${args.checkCompleteness !== false ? `- **Completeness**: ${_analysis.completeness.score.toFixed(1)}% (${_analysis.completeness.complete}/${_analysis.completeness.total} fields complete)\n` : ''}${args.checkConsistency !== false ? `- **Consistency**: ${_analysis.consistency.score.toFixed(1)}% (${_analysis.consistency.consistent}/${_analysis.consistency.total} fields consistent)\n` : ''}${args.checkAccuracy !== false ? `- **Accuracy**: ${_analysis.accuracy.score.toFixed(1)}% (${_analysis.accuracy.accurate}/${_analysis.accuracy.total} fields accurate)\n` : ''}\n${_analysis.issues.length > 0 ? `\n🚨 **Issues Found:**\n${_analysis.issues.map(issue => `- ${issue.type}: ${issue.description}`).join('\n')}\n` : ''}\n✨ Analysis completed with dynamic field discovery!`
|
|
796
|
-
}]
|
|
797
|
-
};
|
|
798
|
-
} catch (error) {
|
|
799
|
-
this.logger.error('Failed to analyze data quality:', error);
|
|
800
|
-
throw new McpError(ErrorCode.InternalError, `Failed to analyze data quality: ${error}`);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* Generate insights
|
|
806
|
-
*/
|
|
807
|
-
private async generateInsights(args: any) {
|
|
808
|
-
try {
|
|
809
|
-
this.logger.info(`Generating insights for table: ${args.table}`);
|
|
810
|
-
|
|
811
|
-
// Get table info and data
|
|
812
|
-
const tableInfo = await this.getTableInfo(args.table);
|
|
813
|
-
if (!tableInfo) {
|
|
814
|
-
throw new Error(`Table not found: ${args.table}`);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
const data = await this.client.searchRecords(args.table, '', 200);
|
|
818
|
-
if (!data.success) {
|
|
819
|
-
throw new Error('Failed to retrieve data for _analysis');
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// Generate insights based on _analysis type
|
|
823
|
-
const insights = {
|
|
824
|
-
table: args.table,
|
|
825
|
-
analysisType: args.analysisType || 'patterns',
|
|
826
|
-
timeframe: args.timeframe || '30d',
|
|
827
|
-
insights: [] as any[],
|
|
828
|
-
recommendations: [] as any[]
|
|
829
|
-
};
|
|
830
|
-
|
|
831
|
-
// Analyze patterns
|
|
832
|
-
if (args.analysisType === 'patterns' || !args.analysisType) {
|
|
833
|
-
insights.insights.push(...this.analyzePatterns(data.data.result));
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// Analyze trends
|
|
837
|
-
if (args.analysisType === 'trends' || !args.analysisType) {
|
|
838
|
-
insights.insights.push(...this.analyzeTrends(data.data.result));
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// Detect anomalies
|
|
842
|
-
if (args.analysisType === 'anomalies' || !args.analysisType) {
|
|
843
|
-
insights.insights.push(...this.detectAnomalies(data.data.result));
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// Generate recommendations
|
|
847
|
-
if (args.generateRecommendations) {
|
|
848
|
-
insights.recommendations = this.generateRecommendations(insights.insights);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
return {
|
|
852
|
-
content: [{
|
|
853
|
-
type: 'text',
|
|
854
|
-
text: `🔍 Data Insights for **${tableInfo.label}** (${tableInfo.name}):\n\n📊 **Analysis Type:** ${insights.analysisType}\n📅 **Timeframe:** ${insights.timeframe}\n📈 **Sample Size:** ${data.data.result.length} records\n\n💡 **Key Insights:**\n${insights.insights.map(insight => `- **${insight.type}**: ${insight.description}`).join('\n')}\n\n${insights.recommendations.length > 0 ? `🎯 **Recommendations:**\n${insights.recommendations.map(rec => `- ${rec}`).join('\n')}\n\n` : ''}\n✨ Insights generated with dynamic data _analysis!`
|
|
855
|
-
}]
|
|
856
|
-
};
|
|
857
|
-
} catch (error) {
|
|
858
|
-
this.logger.error('Failed to generate insights:', error);
|
|
859
|
-
throw new McpError(ErrorCode.InternalError, `Failed to generate insights: ${error}`);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
/**
|
|
864
|
-
* Export report data
|
|
865
|
-
*/
|
|
866
|
-
private async exportReportData(args: any) {
|
|
867
|
-
try {
|
|
868
|
-
this.logger.info(`Exporting report data: ${args.reportName}`);
|
|
869
|
-
|
|
870
|
-
// Find the report
|
|
871
|
-
const report = await this.findReport(args.reportName);
|
|
872
|
-
if (!report) {
|
|
873
|
-
throw new Error(`Report not found: ${args.reportName}`);
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// Get report data
|
|
877
|
-
const reportData = await this.getReportData(report, args.maxRows);
|
|
878
|
-
|
|
879
|
-
// Format export
|
|
880
|
-
const exportInfo = {
|
|
881
|
-
reportName: args.reportName,
|
|
882
|
-
format: args.format,
|
|
883
|
-
records: reportData.length,
|
|
884
|
-
size: this.calculateExportSize(reportData, args.format),
|
|
885
|
-
timestamp: new Date().toISOString()
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
return {
|
|
889
|
-
content: [{
|
|
890
|
-
type: 'text',
|
|
891
|
-
text: `📤 Report Data Export Completed!\n\n📊 **Report:** ${args.reportName}\n📄 **Format:** ${args.format}\n📈 **Records:** ${exportInfo.records}\n📦 **Size:** ${exportInfo.size}\n📅 **Exported:** ${new Date().toLocaleString()}\n${args.includeHeaders ? '📋 Headers included\n' : ''}${args.maxRows ? `🔢 Limited to ${args.maxRows} rows\n` : ''}\n✨ Export completed with dynamic report discovery!`
|
|
892
|
-
}]
|
|
893
|
-
};
|
|
894
|
-
} catch (error) {
|
|
895
|
-
this.logger.error('Failed to export report data:', error);
|
|
896
|
-
throw new McpError(ErrorCode.InternalError, `Failed to export report data: ${error}`);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// Helper methods
|
|
901
|
-
private async getTableInfo(tableName: string): Promise<{name: string, label: string} | null> {
|
|
902
|
-
try {
|
|
903
|
-
const tableResponse = await this.client.searchRecords('sys_db_object', `name=${tableName}`, 1);
|
|
904
|
-
if (tableResponse.success && tableResponse.data?.result?.length > 0) {
|
|
905
|
-
const table = tableResponse.data.result[0];
|
|
906
|
-
return { name: table.name, label: table.label };
|
|
907
|
-
}
|
|
908
|
-
return null;
|
|
909
|
-
} catch (error) {
|
|
910
|
-
this.logger.error(`Failed to get table info for ${tableName}:`, error);
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
private async getTableFields(tableName: string): Promise<string[]> {
|
|
916
|
-
try {
|
|
917
|
-
const fieldsResponse = await this.client.searchRecords('sys_dictionary', `nameSTARTSWITH${tableName}^element!=NULL`, 100);
|
|
918
|
-
if (fieldsResponse.success) {
|
|
919
|
-
return fieldsResponse.data.result.map((field: any) => field.element);
|
|
920
|
-
}
|
|
921
|
-
return [];
|
|
922
|
-
} catch (error) {
|
|
923
|
-
this.logger.error(`Failed to get fields for ${tableName}:`, error);
|
|
924
|
-
return [];
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
private async getAggregationFunctions(): Promise<string[]> {
|
|
929
|
-
return ['COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'DISTINCT'];
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
private async getWidgetTypes(): Promise<string[]> {
|
|
933
|
-
return ['chart', 'table', 'list', 'gauge', 'scorecard', 'map', 'calendar'];
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
private async getDashboardLayouts(): Promise<string[]> {
|
|
937
|
-
return ['grid', 'tabs', 'accordion', 'stacked', 'fluid'];
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
private async getAvailableMetrics(tableName: string): Promise<string[]> {
|
|
941
|
-
const fields = await this.getTableFields(tableName);
|
|
942
|
-
return fields.filter(field => ['number', 'integer', 'decimal', 'float'].includes(field));
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
private async getChartTypes(): Promise<string[]> {
|
|
946
|
-
return ['bar', 'line', 'pie', 'donut', 'area', 'scatter', 'bubble', 'radar', 'funnel'];
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
private async getDataSourceInfo(dataSource: string): Promise<any> {
|
|
950
|
-
// Could be a table or a report
|
|
951
|
-
const tableInfo = await this.getTableInfo(dataSource);
|
|
952
|
-
if (tableInfo) return tableInfo;
|
|
953
|
-
|
|
954
|
-
const reportInfo = await this.findReport(dataSource);
|
|
955
|
-
return reportInfo;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
private async getPerformanceMetrics(dataSource: string): Promise<string[]> {
|
|
959
|
-
return ['response_time', 'throughput', 'error_rate', 'availability', 'utilization'];
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
private async findReport(reportName: string): Promise<any> {
|
|
963
|
-
try {
|
|
964
|
-
const reportResponse = await this.client.searchRecords('sys_report', `name=${reportName}`, 1);
|
|
965
|
-
if (reportResponse.success && reportResponse.data?.result?.length > 0) {
|
|
966
|
-
return reportResponse.data.result[0];
|
|
967
|
-
}
|
|
968
|
-
return null;
|
|
969
|
-
} catch (error) {
|
|
970
|
-
this.logger.error(`Failed to find report ${reportName}:`, error);
|
|
971
|
-
return null;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
private categorizeTable(tableName: string): string {
|
|
976
|
-
if (tableName.includes('incident') || tableName.includes('problem') || tableName.includes('change') || tableName.includes('task')) {
|
|
977
|
-
return 'ITSM';
|
|
978
|
-
} else if (tableName.includes('cmdb') || tableName.includes('alm') || tableName.includes('discovery')) {
|
|
979
|
-
return 'ITOM';
|
|
980
|
-
} else if (tableName.includes('hr_') || tableName.includes('employee')) {
|
|
981
|
-
return 'HR';
|
|
982
|
-
} else if (tableName.includes('security') || tableName.includes('vulnerability') || tableName.includes('risk')) {
|
|
983
|
-
return 'Security';
|
|
984
|
-
} else if (tableName.startsWith('u_') || tableName.startsWith('x_')) {
|
|
985
|
-
return 'Custom';
|
|
986
|
-
} else {
|
|
987
|
-
return 'System';
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
private analyzeCompleteness(data: any[], fields?: string[]): any {
|
|
992
|
-
const fieldsToCheck = fields || Object.keys(data[0] || {});
|
|
993
|
-
const total = fieldsToCheck.length;
|
|
994
|
-
let complete = 0;
|
|
995
|
-
|
|
996
|
-
fieldsToCheck.forEach(field => {
|
|
997
|
-
const filledCount = data.filter(record => record[field] && record[field] !== '').length;
|
|
998
|
-
if (filledCount / data.length > 0.8) complete++;
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
return { score: (complete / total) * 100, complete, total };
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
private analyzeConsistency(data: any[], fields?: string[]): any {
|
|
1005
|
-
const fieldsToCheck = fields || Object.keys(data[0] || {});
|
|
1006
|
-
const total = fieldsToCheck.length;
|
|
1007
|
-
let consistent = 0;
|
|
1008
|
-
|
|
1009
|
-
fieldsToCheck.forEach(field => {
|
|
1010
|
-
const values = data.map(record => record[field]).filter(v => v);
|
|
1011
|
-
const uniqueValues = new Set(values);
|
|
1012
|
-
// Simple consistency check - not too many unique values for categorical fields
|
|
1013
|
-
if (uniqueValues.size < values.length * 0.5) consistent++;
|
|
1014
|
-
});
|
|
1015
|
-
|
|
1016
|
-
return { score: (consistent / total) * 100, consistent, total };
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
private analyzeAccuracy(data: any[], fields?: string[]): any {
|
|
1020
|
-
// Simple accuracy check - assume most data is accurate
|
|
1021
|
-
const fieldsToCheck = fields || Object.keys(data[0] || {});
|
|
1022
|
-
const total = fieldsToCheck.length;
|
|
1023
|
-
const accurate = Math.floor(total * 0.85); // Assume 85% accuracy
|
|
1024
|
-
|
|
1025
|
-
return { score: (accurate / total) * 100, accurate, total };
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
private analyzePatterns(data: any[]): any[] {
|
|
1029
|
-
const patterns = [];
|
|
1030
|
-
|
|
1031
|
-
// Simple pattern analysis
|
|
1032
|
-
if (data.length > 0) {
|
|
1033
|
-
const fields = Object.keys(data[0]);
|
|
1034
|
-
const categoricalFields = fields.filter(field => {
|
|
1035
|
-
const values = data.map(record => record[field]).filter(v => v);
|
|
1036
|
-
const uniqueValues = new Set(values);
|
|
1037
|
-
return uniqueValues.size < values.length * 0.2;
|
|
1038
|
-
});
|
|
1039
|
-
|
|
1040
|
-
if (categoricalFields.length > 0) {
|
|
1041
|
-
patterns.push({
|
|
1042
|
-
type: 'Distribution Pattern',
|
|
1043
|
-
description: `Found ${categoricalFields.length} categorical fields with consistent value distributions`
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
return patterns;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
private analyzeTrends(data: any[]): any[] {
|
|
1052
|
-
const trends = [];
|
|
1053
|
-
|
|
1054
|
-
// Simple trend analysis
|
|
1055
|
-
if (data.length > 10) {
|
|
1056
|
-
trends.push({
|
|
1057
|
-
type: 'Volume Trend',
|
|
1058
|
-
description: `Dataset contains ${data.length} records indicating active data collection`
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
return trends;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
private detectAnomalies(data: any[]): any[] {
|
|
1066
|
-
const anomalies = [];
|
|
1067
|
-
|
|
1068
|
-
// Simple anomaly detection
|
|
1069
|
-
if (data.length > 0) {
|
|
1070
|
-
const fields = Object.keys(data[0]);
|
|
1071
|
-
const numericFields = fields.filter(field => {
|
|
1072
|
-
const values = data.map(record => record[field]).filter(v => v && !isNaN(v));
|
|
1073
|
-
return values.length > 0;
|
|
1074
|
-
});
|
|
1075
|
-
|
|
1076
|
-
if (numericFields.length > 0) {
|
|
1077
|
-
anomalies.push({
|
|
1078
|
-
type: 'Data Anomaly',
|
|
1079
|
-
description: `Found ${numericFields.length} numeric fields suitable for anomaly detection`
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
return anomalies;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
private generateRecommendations(insights: any[]): string[] {
|
|
1088
|
-
const recommendations = [];
|
|
1089
|
-
|
|
1090
|
-
if (insights.length > 0) {
|
|
1091
|
-
recommendations.push('Consider creating automated dashboards for key metrics');
|
|
1092
|
-
recommendations.push('Implement data quality monitoring for critical fields');
|
|
1093
|
-
recommendations.push('Set up alerts for anomalous data patterns');
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
return recommendations;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
private async getReportData(report: any, maxRows?: number): Promise<any[]> {
|
|
1100
|
-
// Simulate getting report data
|
|
1101
|
-
const limit = maxRows || 1000;
|
|
1102
|
-
const data = await this.client.searchRecords(report.table, report.conditions || '', limit);
|
|
1103
|
-
return data.success ? data.data.result : [];
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
private calculateExportSize(data: any[], format: string): string {
|
|
1107
|
-
const recordSize = JSON.stringify(data[0] || {}).length;
|
|
1108
|
-
const totalSize = recordSize * data.length;
|
|
1109
|
-
|
|
1110
|
-
if (totalSize < 1024) return `${totalSize} bytes`;
|
|
1111
|
-
if (totalSize < 1024 * 1024) return `${(totalSize / 1024).toFixed(1)} KB`;
|
|
1112
|
-
return `${(totalSize / (1024 * 1024)).toFixed(1)} MB`;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
async run() {
|
|
1116
|
-
const transport = new StdioServerTransport();
|
|
1117
|
-
await this.server.connect(transport);
|
|
1118
|
-
this.logger.info('ServiceNow Reporting & Analytics MCP Server running on stdio');
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
const server = new ServiceNowReportingAnalyticsMCP();
|
|
1123
|
-
server.run().catch(console.error);
|