mcp-consultant-tools 5.0.0 → 6.0.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/README.md +39 -6
- package/build/ApplicationInsightsService.js +323 -0
- package/build/index.js +694 -0
- package/build/utils/appinsights-formatters.js +181 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MCP Consultant Tools
|
|
2
2
|
|
|
3
|
-
A Model Context Protocol (MCP) server providing intelligent access to PowerPlatform/Dataverse, Azure DevOps, and
|
|
3
|
+
A Model Context Protocol (MCP) server providing intelligent access to PowerPlatform/Dataverse, Azure DevOps, Figma, and Azure Application Insights through an AI-friendly interface.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -15,10 +15,11 @@ This MCP server enables AI assistants to:
|
|
|
15
15
|
- Publishing & validation
|
|
16
16
|
- **Azure DevOps** (12 tools): Search wikis, manage work items, execute WIQL queries
|
|
17
17
|
- **Figma** (2 tools): Extract design data in simplified, AI-friendly format
|
|
18
|
+
- **Application Insights** (10 tools): Query telemetry, analyze exceptions, monitor performance, troubleshoot issues
|
|
18
19
|
|
|
19
20
|
All integrations are **optional** - configure only the services you need.
|
|
20
21
|
|
|
21
|
-
**Total:
|
|
22
|
+
**Total: 96+ MCP tools** providing comprehensive access to your development and operations lifecycle.
|
|
22
23
|
|
|
23
24
|
## Known limitations
|
|
24
25
|
- Cannot create Model-Driven-Apps
|
|
@@ -71,7 +72,13 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
71
72
|
|
|
72
73
|
"FIGMA_API_KEY": "your-figma-token",
|
|
73
74
|
"FIGMA_OAUTH_TOKEN": "",
|
|
74
|
-
"FIGMA_USE_OAUTH": "false"
|
|
75
|
+
"FIGMA_USE_OAUTH": "false",
|
|
76
|
+
|
|
77
|
+
"APPINSIGHTS_AUTH_METHOD": "entra-id",
|
|
78
|
+
"APPINSIGHTS_TENANT_ID": "your-tenant-id",
|
|
79
|
+
"APPINSIGHTS_CLIENT_ID": "your-client-id",
|
|
80
|
+
"APPINSIGHTS_CLIENT_SECRET": "your-client-secret",
|
|
81
|
+
"APPINSIGHTS_RESOURCES": "[{\"id\":\"prod-api\",\"name\":\"Production API\",\"appId\":\"your-app-id\",\"active\":true}]"
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
}
|
|
@@ -108,7 +115,13 @@ Create `.vscode/mcp.json` in your project:
|
|
|
108
115
|
|
|
109
116
|
"FIGMA_API_KEY": "your-figma-token",
|
|
110
117
|
"FIGMA_OAUTH_TOKEN": "",
|
|
111
|
-
"FIGMA_USE_OAUTH": "false"
|
|
118
|
+
"FIGMA_USE_OAUTH": "false",
|
|
119
|
+
|
|
120
|
+
"APPINSIGHTS_AUTH_METHOD": "entra-id",
|
|
121
|
+
"APPINSIGHTS_TENANT_ID": "your-tenant-id",
|
|
122
|
+
"APPINSIGHTS_CLIENT_ID": "your-client-id",
|
|
123
|
+
"APPINSIGHTS_CLIENT_SECRET": "your-client-secret",
|
|
124
|
+
"APPINSIGHTS_RESOURCES": "[{\"id\":\"prod-api\",\"name\":\"Production API\",\"appId\":\"your-app-id\",\"active\":true}]"
|
|
112
125
|
}
|
|
113
126
|
}
|
|
114
127
|
}
|
|
@@ -239,9 +252,22 @@ Reload VS Code window after saving.
|
|
|
239
252
|
- `get-figma-data` - Get design data (layout, text, styles, components)
|
|
240
253
|
- `download-figma-images` - Download images (coming in v2)
|
|
241
254
|
|
|
255
|
+
### Application Insights (10 tools)
|
|
256
|
+
|
|
257
|
+
- `appinsights-list-resources` - List all configured Application Insights resources
|
|
258
|
+
- `appinsights-get-metadata` - Get schema metadata (tables and columns)
|
|
259
|
+
- `appinsights-execute-query` - Execute custom KQL queries
|
|
260
|
+
- `appinsights-get-exceptions` - Get recent exceptions with details
|
|
261
|
+
- `appinsights-get-slow-requests` - Get slow HTTP requests (configurable threshold)
|
|
262
|
+
- `appinsights-get-operation-performance` - Get operation performance summary (count, avg, percentiles)
|
|
263
|
+
- `appinsights-get-failed-dependencies` - Get failed external API/database calls
|
|
264
|
+
- `appinsights-get-traces` - Get diagnostic traces filtered by severity
|
|
265
|
+
- `appinsights-get-availability` - Get availability test results and uptime stats
|
|
266
|
+
- `appinsights-get-custom-events` - Get custom application events
|
|
267
|
+
|
|
242
268
|
## Available Prompts
|
|
243
269
|
|
|
244
|
-
The server includes **
|
|
270
|
+
The server includes **18 prompts** that provide formatted, context-rich output:
|
|
245
271
|
|
|
246
272
|
**PowerPlatform:**
|
|
247
273
|
- `entity-overview` - Comprehensive entity overview
|
|
@@ -260,10 +286,17 @@ The server includes **13 prompts** that provide formatted, context-rich output:
|
|
|
260
286
|
- `work-item-summary` - Comprehensive work item summary
|
|
261
287
|
- `work-items-query-report` - Formatted WIQL query results
|
|
262
288
|
|
|
289
|
+
**Application Insights:**
|
|
290
|
+
- `appinsights-exception-summary` - Exception summary report with insights
|
|
291
|
+
- `appinsights-performance-report` - Performance analysis with recommendations
|
|
292
|
+
- `appinsights-dependency-health` - Dependency health report with success rates
|
|
293
|
+
- `appinsights-availability-report` - Availability and uptime report
|
|
294
|
+
- `appinsights-troubleshooting-guide` - Comprehensive troubleshooting guide combining all telemetry
|
|
295
|
+
|
|
263
296
|
## Documentation
|
|
264
297
|
|
|
265
298
|
- **[SETUP.md](SETUP.md)** - Complete setup guide with credentials, troubleshooting, and security
|
|
266
|
-
- **[TOOLS.md](TOOLS.md)** - Full reference for all
|
|
299
|
+
- **[TOOLS.md](TOOLS.md)** - Full reference for all 96+ tools and 18 prompts
|
|
267
300
|
- **[USAGE.md](USAGE.md)** - Examples and use cases for all integrations
|
|
268
301
|
- **[CLAUDE.md](CLAUDE.md)** - Architecture details and development guide
|
|
269
302
|
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
export class ApplicationInsightsService {
|
|
4
|
+
config;
|
|
5
|
+
msalClient = null;
|
|
6
|
+
accessToken = null;
|
|
7
|
+
tokenExpirationTime = 0;
|
|
8
|
+
baseUrl = 'https://api.applicationinsights.io/v1';
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
// Initialize MSAL client if using Entra ID auth
|
|
12
|
+
if (this.config.authMethod === 'entra-id') {
|
|
13
|
+
if (!this.config.tenantId || !this.config.clientId || !this.config.clientSecret) {
|
|
14
|
+
throw new Error('Entra ID authentication requires tenantId, clientId, and clientSecret');
|
|
15
|
+
}
|
|
16
|
+
this.msalClient = new ConfidentialClientApplication({
|
|
17
|
+
auth: {
|
|
18
|
+
clientId: this.config.clientId,
|
|
19
|
+
clientSecret: this.config.clientSecret,
|
|
20
|
+
authority: `https://login.microsoftonline.com/${this.config.tenantId}`,
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get an access token for the Application Insights API (Entra ID auth)
|
|
27
|
+
*/
|
|
28
|
+
async getAccessToken() {
|
|
29
|
+
if (this.config.authMethod !== 'entra-id' || !this.msalClient) {
|
|
30
|
+
throw new Error('Entra ID authentication not configured');
|
|
31
|
+
}
|
|
32
|
+
const currentTime = Date.now();
|
|
33
|
+
// Return cached token if still valid (with 5 minute buffer)
|
|
34
|
+
if (this.accessToken && this.tokenExpirationTime > currentTime) {
|
|
35
|
+
return this.accessToken;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const result = await this.msalClient.acquireTokenByClientCredential({
|
|
39
|
+
scopes: ['https://api.applicationinsights.io/.default'],
|
|
40
|
+
});
|
|
41
|
+
if (!result || !result.accessToken) {
|
|
42
|
+
throw new Error('Failed to acquire access token');
|
|
43
|
+
}
|
|
44
|
+
this.accessToken = result.accessToken;
|
|
45
|
+
// Set expiration time (subtract 5 minutes to refresh early)
|
|
46
|
+
if (result.expiresOn) {
|
|
47
|
+
this.tokenExpirationTime = result.expiresOn.getTime() - (5 * 60 * 1000);
|
|
48
|
+
}
|
|
49
|
+
return this.accessToken;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('Error acquiring access token:', error);
|
|
53
|
+
throw new Error('Application Insights authentication failed');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get authentication headers based on configuration
|
|
58
|
+
*/
|
|
59
|
+
async getAuthHeaders(resourceId) {
|
|
60
|
+
const resource = this.getResourceById(resourceId);
|
|
61
|
+
if (this.config.authMethod === 'entra-id') {
|
|
62
|
+
const token = await this.getAccessToken();
|
|
63
|
+
return {
|
|
64
|
+
'Authorization': `Bearer ${token}`,
|
|
65
|
+
'Accept': 'application/json',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// API Key authentication
|
|
70
|
+
if (!resource.apiKey) {
|
|
71
|
+
throw new Error(`API key not configured for resource '${resourceId}'`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
'x-api-key': resource.apiKey,
|
|
75
|
+
'Accept': 'application/json',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get active resources
|
|
81
|
+
*/
|
|
82
|
+
getActiveResources() {
|
|
83
|
+
return this.config.resources.filter(r => r.active);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get all resources (including inactive)
|
|
87
|
+
*/
|
|
88
|
+
getAllResources() {
|
|
89
|
+
return this.config.resources;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get resource by ID
|
|
93
|
+
*/
|
|
94
|
+
getResourceById(resourceId) {
|
|
95
|
+
const resource = this.config.resources.find(r => r.id === resourceId);
|
|
96
|
+
if (!resource) {
|
|
97
|
+
throw new Error(`Application Insights resource '${resourceId}' not found`);
|
|
98
|
+
}
|
|
99
|
+
if (!resource.active) {
|
|
100
|
+
throw new Error(`Application Insights resource '${resourceId}' is inactive`);
|
|
101
|
+
}
|
|
102
|
+
return resource;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Execute a KQL query against an Application Insights resource
|
|
106
|
+
*/
|
|
107
|
+
async executeQuery(resourceId, query, timespan) {
|
|
108
|
+
try {
|
|
109
|
+
const resource = this.getResourceById(resourceId);
|
|
110
|
+
const headers = await this.getAuthHeaders(resourceId);
|
|
111
|
+
const url = `${this.baseUrl}/apps/${resource.appId}/query`;
|
|
112
|
+
const params = { query };
|
|
113
|
+
if (timespan) {
|
|
114
|
+
params.timespan = timespan;
|
|
115
|
+
}
|
|
116
|
+
const response = await axios.get(url, {
|
|
117
|
+
headers,
|
|
118
|
+
params,
|
|
119
|
+
timeout: 30000, // 30 second timeout
|
|
120
|
+
});
|
|
121
|
+
return response.data;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// Handle timeout
|
|
125
|
+
if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
|
|
126
|
+
throw new Error('Application Insights query timed out after 30 seconds. ' +
|
|
127
|
+
'Try reducing the time range or simplifying the query.');
|
|
128
|
+
}
|
|
129
|
+
// Handle network errors
|
|
130
|
+
if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
|
|
131
|
+
throw new Error('Network error: Unable to connect to Application Insights API. ' +
|
|
132
|
+
'Check your internet connection and firewall settings.');
|
|
133
|
+
}
|
|
134
|
+
const errorDetails = error.response?.data?.error?.message || error.message;
|
|
135
|
+
console.error('Application Insights query failed:', {
|
|
136
|
+
resourceId,
|
|
137
|
+
query: query.substring(0, 100) + '...',
|
|
138
|
+
error: errorDetails,
|
|
139
|
+
});
|
|
140
|
+
// Provide user-friendly error messages
|
|
141
|
+
if (error.response?.status === 401) {
|
|
142
|
+
throw new Error('Application Insights authentication failed. Check credentials and permissions.');
|
|
143
|
+
}
|
|
144
|
+
if (error.response?.status === 403) {
|
|
145
|
+
throw new Error('Application Insights access denied. Ensure you have Reader role on the resource.');
|
|
146
|
+
}
|
|
147
|
+
if (error.response?.status === 429) {
|
|
148
|
+
const retryAfter = error.response.headers['retry-after'] || 60;
|
|
149
|
+
throw new Error(`Application Insights rate limit exceeded. ` +
|
|
150
|
+
`Please retry after ${retryAfter} seconds. ` +
|
|
151
|
+
`Current limits: ${this.config.authMethod === 'entra-id' ? '60 requests/minute' : '15 requests/minute'}`);
|
|
152
|
+
}
|
|
153
|
+
// Handle KQL syntax errors
|
|
154
|
+
if (error.response?.data?.error?.innererror?.code === 'SyntaxError') {
|
|
155
|
+
const syntaxError = error.response.data.error.innererror.message;
|
|
156
|
+
throw new Error(`KQL query syntax error: ${syntaxError}\n` +
|
|
157
|
+
`Hint: Check table names, column names, and operator syntax`);
|
|
158
|
+
}
|
|
159
|
+
// Handle semantic errors
|
|
160
|
+
if (error.response?.data?.error?.innererror?.code === 'SemanticError') {
|
|
161
|
+
const semanticError = error.response.data.error.innererror.message;
|
|
162
|
+
throw new Error(`KQL query semantic error: ${semanticError}\n` +
|
|
163
|
+
`Hint: Use appinsights-get-metadata to see available tables and columns`);
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`Application Insights query failed: ${errorDetails}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get metadata (schema) for an Application Insights resource
|
|
170
|
+
*/
|
|
171
|
+
async getMetadata(resourceId) {
|
|
172
|
+
try {
|
|
173
|
+
const resource = this.getResourceById(resourceId);
|
|
174
|
+
const headers = await this.getAuthHeaders(resourceId);
|
|
175
|
+
const url = `${this.baseUrl}/apps/${resource.appId}/metadata`;
|
|
176
|
+
const response = await axios.get(url, {
|
|
177
|
+
headers,
|
|
178
|
+
timeout: 30000,
|
|
179
|
+
});
|
|
180
|
+
return response.data;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const errorDetails = error.response?.data?.error?.message || error.message;
|
|
184
|
+
console.error('Application Insights metadata request failed:', {
|
|
185
|
+
resourceId,
|
|
186
|
+
error: errorDetails,
|
|
187
|
+
});
|
|
188
|
+
throw new Error(`Application Insights metadata request failed: ${errorDetails}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Helper method: Get recent exceptions
|
|
193
|
+
*/
|
|
194
|
+
async getRecentExceptions(resourceId, timespan = 'PT1H', limit = 50) {
|
|
195
|
+
const query = `
|
|
196
|
+
exceptions
|
|
197
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
198
|
+
| order by timestamp desc
|
|
199
|
+
| take ${limit}
|
|
200
|
+
| project timestamp, type, outerMessage, innermostMessage, operation_Name, operation_Id, cloud_RoleName
|
|
201
|
+
`.trim();
|
|
202
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Helper method: Get slow requests
|
|
206
|
+
*/
|
|
207
|
+
async getSlowRequests(resourceId, durationThresholdMs = 5000, timespan = 'PT1H', limit = 50) {
|
|
208
|
+
const query = `
|
|
209
|
+
requests
|
|
210
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
211
|
+
| where duration > ${durationThresholdMs}
|
|
212
|
+
| order by duration desc
|
|
213
|
+
| take ${limit}
|
|
214
|
+
| project timestamp, name, duration, resultCode, success, operation_Id, cloud_RoleName
|
|
215
|
+
`.trim();
|
|
216
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Helper method: Get failed dependencies
|
|
220
|
+
*/
|
|
221
|
+
async getFailedDependencies(resourceId, timespan = 'PT1H', limit = 50) {
|
|
222
|
+
const query = `
|
|
223
|
+
dependencies
|
|
224
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
225
|
+
| where success == false
|
|
226
|
+
| order by timestamp desc
|
|
227
|
+
| take ${limit}
|
|
228
|
+
| project timestamp, name, target, type, duration, resultCode, operation_Id, cloud_RoleName
|
|
229
|
+
`.trim();
|
|
230
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Helper method: Get operation performance summary
|
|
234
|
+
*/
|
|
235
|
+
async getOperationPerformance(resourceId, timespan = 'PT1H') {
|
|
236
|
+
const query = `
|
|
237
|
+
requests
|
|
238
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
239
|
+
| summarize
|
|
240
|
+
RequestCount=count(),
|
|
241
|
+
AvgDuration=avg(duration),
|
|
242
|
+
P50Duration=percentile(duration, 50),
|
|
243
|
+
P95Duration=percentile(duration, 95),
|
|
244
|
+
P99Duration=percentile(duration, 99),
|
|
245
|
+
FailureCount=countif(success == false)
|
|
246
|
+
by operation_Name
|
|
247
|
+
| order by RequestCount desc
|
|
248
|
+
`.trim();
|
|
249
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Helper method: Get traces by severity
|
|
253
|
+
*/
|
|
254
|
+
async getTracesBySeverity(resourceId, severityLevel = 2, // 0=Verbose, 1=Info, 2=Warning, 3=Error, 4=Critical
|
|
255
|
+
timespan = 'PT1H', limit = 100) {
|
|
256
|
+
const query = `
|
|
257
|
+
traces
|
|
258
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
259
|
+
| where severityLevel >= ${severityLevel}
|
|
260
|
+
| order by timestamp desc
|
|
261
|
+
| take ${limit}
|
|
262
|
+
| project timestamp, message, severityLevel, operation_Name, operation_Id, cloud_RoleName
|
|
263
|
+
`.trim();
|
|
264
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Helper method: Get availability test results
|
|
268
|
+
*/
|
|
269
|
+
async getAvailabilityResults(resourceId, timespan = 'PT24H') {
|
|
270
|
+
const query = `
|
|
271
|
+
availabilityResults
|
|
272
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
273
|
+
| summarize
|
|
274
|
+
TotalTests=count(),
|
|
275
|
+
SuccessCount=countif(success == true),
|
|
276
|
+
FailureCount=countif(success == false),
|
|
277
|
+
AvgDuration=avg(duration)
|
|
278
|
+
by name
|
|
279
|
+
| extend SuccessRate=round(100.0 * SuccessCount / TotalTests, 2)
|
|
280
|
+
| order by FailureCount desc
|
|
281
|
+
`.trim();
|
|
282
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Helper method: Get custom events
|
|
286
|
+
*/
|
|
287
|
+
async getCustomEvents(resourceId, eventName, timespan = 'PT1H', limit = 100) {
|
|
288
|
+
let query = `
|
|
289
|
+
customEvents
|
|
290
|
+
| where timestamp > ago(${this.convertTimespanToKQL(timespan)})
|
|
291
|
+
`;
|
|
292
|
+
if (eventName) {
|
|
293
|
+
query += `\n | where name == "${eventName}"`;
|
|
294
|
+
}
|
|
295
|
+
query += `
|
|
296
|
+
| order by timestamp desc
|
|
297
|
+
| take ${limit}
|
|
298
|
+
| project timestamp, name, customDimensions, operation_Id, cloud_RoleName
|
|
299
|
+
`.trim();
|
|
300
|
+
return this.executeQuery(resourceId, query, timespan);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Convert ISO 8601 duration to KQL format
|
|
304
|
+
*/
|
|
305
|
+
convertTimespanToKQL(timespan) {
|
|
306
|
+
// Convert ISO 8601 duration (PT1H, P1D) to KQL format (1h, 1d)
|
|
307
|
+
const match = timespan.match(/P(?:(\d+)D)?T?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/);
|
|
308
|
+
if (!match) {
|
|
309
|
+
return timespan; // Return as-is if not recognized
|
|
310
|
+
}
|
|
311
|
+
const [, days, hours, minutes, seconds] = match;
|
|
312
|
+
const parts = [];
|
|
313
|
+
if (days)
|
|
314
|
+
parts.push(`${days}d`);
|
|
315
|
+
if (hours)
|
|
316
|
+
parts.push(`${hours}h`);
|
|
317
|
+
if (minutes)
|
|
318
|
+
parts.push(`${minutes}m`);
|
|
319
|
+
if (seconds)
|
|
320
|
+
parts.push(`${seconds}s`);
|
|
321
|
+
return parts.length > 0 ? parts.join('') : '1h';
|
|
322
|
+
}
|
|
323
|
+
}
|
package/build/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import { z } from "zod";
|
|
|
6
6
|
import { PowerPlatformService } from "./PowerPlatformService.js";
|
|
7
7
|
import { AzureDevOpsService } from "./AzureDevOpsService.js";
|
|
8
8
|
import { FigmaService } from "./FigmaService.js";
|
|
9
|
+
import { ApplicationInsightsService } from "./ApplicationInsightsService.js";
|
|
10
|
+
import { formatTableAsMarkdown, analyzeExceptions, analyzePerformance, analyzeDependencies } from "./utils/appinsights-formatters.js";
|
|
9
11
|
// Load environment variables from .env file (silent mode to not interfere with MCP)
|
|
10
12
|
// Temporarily suppress stdout to prevent dotenv from corrupting the JSON protocol
|
|
11
13
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
@@ -39,6 +41,36 @@ const FIGMA_CONFIG = {
|
|
|
39
41
|
oauthToken: process.env.FIGMA_OAUTH_TOKEN || "",
|
|
40
42
|
useOAuth: process.env.FIGMA_USE_OAUTH === "true",
|
|
41
43
|
};
|
|
44
|
+
// Application Insights configuration
|
|
45
|
+
const APPINSIGHTS_CONFIG = {
|
|
46
|
+
resources: [],
|
|
47
|
+
authMethod: (process.env.APPINSIGHTS_AUTH_METHOD || 'entra-id'),
|
|
48
|
+
tenantId: process.env.APPINSIGHTS_TENANT_ID || '',
|
|
49
|
+
clientId: process.env.APPINSIGHTS_CLIENT_ID || '',
|
|
50
|
+
clientSecret: process.env.APPINSIGHTS_CLIENT_SECRET || '',
|
|
51
|
+
};
|
|
52
|
+
// Parse resources configuration
|
|
53
|
+
if (process.env.APPINSIGHTS_RESOURCES) {
|
|
54
|
+
try {
|
|
55
|
+
APPINSIGHTS_CONFIG.resources = JSON.parse(process.env.APPINSIGHTS_RESOURCES);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('Failed to parse APPINSIGHTS_RESOURCES:', error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (process.env.APPINSIGHTS_APP_ID) {
|
|
62
|
+
// Fallback: single resource configuration
|
|
63
|
+
APPINSIGHTS_CONFIG.resources = [
|
|
64
|
+
{
|
|
65
|
+
id: 'default',
|
|
66
|
+
name: 'Default Application Insights',
|
|
67
|
+
appId: process.env.APPINSIGHTS_APP_ID,
|
|
68
|
+
active: true,
|
|
69
|
+
apiKey: process.env.APPINSIGHTS_API_KEY || '',
|
|
70
|
+
description: 'Default Application Insights resource',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
}
|
|
42
74
|
// Create server instance
|
|
43
75
|
const server = new McpServer({
|
|
44
76
|
name: "mcp-consultant-tools",
|
|
@@ -108,6 +140,33 @@ function getFigmaService() {
|
|
|
108
140
|
}
|
|
109
141
|
return figmaService;
|
|
110
142
|
}
|
|
143
|
+
let applicationInsightsService = null;
|
|
144
|
+
// Function to initialize ApplicationInsightsService on demand
|
|
145
|
+
function getApplicationInsightsService() {
|
|
146
|
+
if (!applicationInsightsService) {
|
|
147
|
+
// Check if configuration is complete
|
|
148
|
+
const missingConfig = [];
|
|
149
|
+
if (!APPINSIGHTS_CONFIG.resources || APPINSIGHTS_CONFIG.resources.length === 0) {
|
|
150
|
+
missingConfig.push('APPINSIGHTS_RESOURCES or APPINSIGHTS_APP_ID');
|
|
151
|
+
}
|
|
152
|
+
if (APPINSIGHTS_CONFIG.authMethod === 'entra-id') {
|
|
153
|
+
if (!APPINSIGHTS_CONFIG.tenantId)
|
|
154
|
+
missingConfig.push('APPINSIGHTS_TENANT_ID');
|
|
155
|
+
if (!APPINSIGHTS_CONFIG.clientId)
|
|
156
|
+
missingConfig.push('APPINSIGHTS_CLIENT_ID');
|
|
157
|
+
if (!APPINSIGHTS_CONFIG.clientSecret)
|
|
158
|
+
missingConfig.push('APPINSIGHTS_CLIENT_SECRET');
|
|
159
|
+
}
|
|
160
|
+
if (missingConfig.length > 0) {
|
|
161
|
+
throw new Error(`Missing Application Insights configuration: ${missingConfig.join(', ')}. ` +
|
|
162
|
+
`Set these in environment variables (APPINSIGHTS_*).`);
|
|
163
|
+
}
|
|
164
|
+
// Initialize service
|
|
165
|
+
applicationInsightsService = new ApplicationInsightsService(APPINSIGHTS_CONFIG);
|
|
166
|
+
console.error('Application Insights service initialized');
|
|
167
|
+
}
|
|
168
|
+
return applicationInsightsService;
|
|
169
|
+
}
|
|
111
170
|
// Pre-defined PowerPlatform Prompts
|
|
112
171
|
const powerPlatformPrompts = {
|
|
113
172
|
// Entity exploration prompts
|
|
@@ -1169,6 +1228,306 @@ server.prompt("work-items-query-report", "Execute a WIQL query and get formatted
|
|
|
1169
1228
|
};
|
|
1170
1229
|
}
|
|
1171
1230
|
});
|
|
1231
|
+
/**
|
|
1232
|
+
* Prompt: appinsights-exception-summary
|
|
1233
|
+
* Generate an exception summary report for troubleshooting
|
|
1234
|
+
*/
|
|
1235
|
+
server.prompt("appinsights-exception-summary", "Generate a comprehensive exception summary report from Application Insights", {
|
|
1236
|
+
resourceId: z.string().describe("Resource ID"),
|
|
1237
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
1238
|
+
}, async ({ resourceId, timespan }) => {
|
|
1239
|
+
try {
|
|
1240
|
+
const service = getApplicationInsightsService();
|
|
1241
|
+
const timespanValue = timespan || 'PT1H';
|
|
1242
|
+
// Get recent exceptions
|
|
1243
|
+
const exceptionsResult = await service.getRecentExceptions(resourceId, timespanValue, 50);
|
|
1244
|
+
// Get exception type frequency
|
|
1245
|
+
const exceptionTypesResult = await service.executeQuery(resourceId, `
|
|
1246
|
+
exceptions
|
|
1247
|
+
| where timestamp > ago(${timespanValue.replace(/^P(T)?/, '')})
|
|
1248
|
+
| summarize Count=count() by type
|
|
1249
|
+
| order by Count desc
|
|
1250
|
+
`.trim(), timespanValue);
|
|
1251
|
+
// Format results
|
|
1252
|
+
const exceptionsList = formatTableAsMarkdown(exceptionsResult.tables[0]);
|
|
1253
|
+
const exceptionTypes = formatTableAsMarkdown(exceptionTypesResult.tables[0]);
|
|
1254
|
+
const insights = analyzeExceptions(exceptionsResult.tables[0]);
|
|
1255
|
+
const report = `# Application Insights Exception Summary Report\n\n` +
|
|
1256
|
+
`**Resource**: ${resourceId}\n` +
|
|
1257
|
+
`**Time Range**: ${timespanValue}\n\n` +
|
|
1258
|
+
`## Key Insights\n\n${insights}\n\n` +
|
|
1259
|
+
`## Recent Exceptions\n\n${exceptionsList}\n\n` +
|
|
1260
|
+
`## Exception Types (Frequency)\n\n${exceptionTypes}\n\n` +
|
|
1261
|
+
`## Recommendations\n\n` +
|
|
1262
|
+
`- Review the most frequent exception types to identify systemic issues\n` +
|
|
1263
|
+
`- Investigate exceptions in critical operations first\n` +
|
|
1264
|
+
`- Check for patterns in timestamps (e.g., deployment times, peak traffic)\n` +
|
|
1265
|
+
`- Use operation_Id to correlate exceptions with requests and dependencies`;
|
|
1266
|
+
return {
|
|
1267
|
+
messages: [
|
|
1268
|
+
{
|
|
1269
|
+
role: "assistant",
|
|
1270
|
+
content: {
|
|
1271
|
+
type: "text",
|
|
1272
|
+
text: report,
|
|
1273
|
+
},
|
|
1274
|
+
},
|
|
1275
|
+
],
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
catch (error) {
|
|
1279
|
+
console.error("Error generating exception summary:", error);
|
|
1280
|
+
return {
|
|
1281
|
+
messages: [
|
|
1282
|
+
{
|
|
1283
|
+
role: "assistant",
|
|
1284
|
+
content: {
|
|
1285
|
+
type: "text",
|
|
1286
|
+
text: `Failed to generate exception summary: ${error.message}`,
|
|
1287
|
+
},
|
|
1288
|
+
},
|
|
1289
|
+
],
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
/**
|
|
1294
|
+
* Prompt: appinsights-performance-report
|
|
1295
|
+
* Generate a comprehensive performance analysis report
|
|
1296
|
+
*/
|
|
1297
|
+
server.prompt("appinsights-performance-report", "Generate a comprehensive performance analysis report from Application Insights", {
|
|
1298
|
+
resourceId: z.string().describe("Resource ID"),
|
|
1299
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
1300
|
+
}, async ({ resourceId, timespan }) => {
|
|
1301
|
+
try {
|
|
1302
|
+
const service = getApplicationInsightsService();
|
|
1303
|
+
const timespanValue = timespan || 'PT1H';
|
|
1304
|
+
// Get operation performance
|
|
1305
|
+
const performanceResult = await service.getOperationPerformance(resourceId, timespanValue);
|
|
1306
|
+
// Get slow requests
|
|
1307
|
+
const slowRequestsResult = await service.getSlowRequests(resourceId, 5000, timespanValue, 20);
|
|
1308
|
+
// Format results
|
|
1309
|
+
const performanceTable = formatTableAsMarkdown(performanceResult.tables[0]);
|
|
1310
|
+
const slowRequestsTable = formatTableAsMarkdown(slowRequestsResult.tables[0]);
|
|
1311
|
+
const insights = analyzePerformance(performanceResult.tables[0]);
|
|
1312
|
+
const report = `# Application Insights Performance Report\n\n` +
|
|
1313
|
+
`**Resource**: ${resourceId}\n` +
|
|
1314
|
+
`**Time Range**: ${timespanValue}\n\n` +
|
|
1315
|
+
`## Key Insights\n\n${insights}\n\n` +
|
|
1316
|
+
`## Operation Performance Summary\n\n${performanceTable}\n\n` +
|
|
1317
|
+
`## Slowest Requests (>5s)\n\n${slowRequestsTable}\n\n` +
|
|
1318
|
+
`## Performance Recommendations\n\n` +
|
|
1319
|
+
`- Focus optimization efforts on operations with high P95/P99 duration\n` +
|
|
1320
|
+
`- Investigate operations with high failure counts\n` +
|
|
1321
|
+
`- Monitor operations with high request counts for scalability issues\n` +
|
|
1322
|
+
`- Use operation_Id to trace slow requests through dependencies`;
|
|
1323
|
+
return {
|
|
1324
|
+
messages: [
|
|
1325
|
+
{
|
|
1326
|
+
role: "assistant",
|
|
1327
|
+
content: {
|
|
1328
|
+
type: "text",
|
|
1329
|
+
text: report,
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
],
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
catch (error) {
|
|
1336
|
+
console.error("Error generating performance report:", error);
|
|
1337
|
+
return {
|
|
1338
|
+
messages: [
|
|
1339
|
+
{
|
|
1340
|
+
role: "assistant",
|
|
1341
|
+
content: {
|
|
1342
|
+
type: "text",
|
|
1343
|
+
text: `Failed to generate performance report: ${error.message}`,
|
|
1344
|
+
},
|
|
1345
|
+
},
|
|
1346
|
+
],
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
/**
|
|
1351
|
+
* Prompt: appinsights-dependency-health
|
|
1352
|
+
* Generate a dependency health report
|
|
1353
|
+
*/
|
|
1354
|
+
server.prompt("appinsights-dependency-health", "Generate a dependency health report showing external service issues", {
|
|
1355
|
+
resourceId: z.string().describe("Resource ID"),
|
|
1356
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
1357
|
+
}, async ({ resourceId, timespan }) => {
|
|
1358
|
+
try {
|
|
1359
|
+
const service = getApplicationInsightsService();
|
|
1360
|
+
const timespanValue = timespan || 'PT1H';
|
|
1361
|
+
// Get failed dependencies
|
|
1362
|
+
const failedDepsResult = await service.getFailedDependencies(resourceId, timespanValue, 50);
|
|
1363
|
+
// Get dependency success rates
|
|
1364
|
+
const successRatesResult = await service.executeQuery(resourceId, `
|
|
1365
|
+
dependencies
|
|
1366
|
+
| where timestamp > ago(${timespanValue.replace(/^P(T)?/, '')})
|
|
1367
|
+
| summarize Total=count(), Failed=countif(success == false), AvgDuration=avg(duration) by target, type
|
|
1368
|
+
| extend SuccessRate=round(100.0 * (Total - Failed) / Total, 2)
|
|
1369
|
+
| order by SuccessRate asc
|
|
1370
|
+
`.trim(), timespanValue);
|
|
1371
|
+
// Format results
|
|
1372
|
+
const failedDepsTable = formatTableAsMarkdown(failedDepsResult.tables[0]);
|
|
1373
|
+
const successRatesTable = formatTableAsMarkdown(successRatesResult.tables[0]);
|
|
1374
|
+
const insights = analyzeDependencies(failedDepsResult.tables[0]);
|
|
1375
|
+
const report = `# Application Insights Dependency Health Report\n\n` +
|
|
1376
|
+
`**Resource**: ${resourceId}\n` +
|
|
1377
|
+
`**Time Range**: ${timespanValue}\n\n` +
|
|
1378
|
+
`## Key Insights\n\n${insights}\n\n` +
|
|
1379
|
+
`## Failed Dependencies\n\n${failedDepsTable}\n\n` +
|
|
1380
|
+
`## Dependency Success Rates\n\n${successRatesTable}\n\n` +
|
|
1381
|
+
`## Recommendations\n\n` +
|
|
1382
|
+
`- Investigate dependencies with success rates below 99%\n` +
|
|
1383
|
+
`- Check if external service degradation matches known incidents\n` +
|
|
1384
|
+
`- Review timeout configurations for slow dependencies\n` +
|
|
1385
|
+
`- Consider implementing circuit breakers for unreliable dependencies`;
|
|
1386
|
+
return {
|
|
1387
|
+
messages: [
|
|
1388
|
+
{
|
|
1389
|
+
role: "assistant",
|
|
1390
|
+
content: {
|
|
1391
|
+
type: "text",
|
|
1392
|
+
text: report,
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1395
|
+
],
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
catch (error) {
|
|
1399
|
+
console.error("Error generating dependency health report:", error);
|
|
1400
|
+
return {
|
|
1401
|
+
messages: [
|
|
1402
|
+
{
|
|
1403
|
+
role: "assistant",
|
|
1404
|
+
content: {
|
|
1405
|
+
type: "text",
|
|
1406
|
+
text: `Failed to generate dependency health report: ${error.message}`,
|
|
1407
|
+
},
|
|
1408
|
+
},
|
|
1409
|
+
],
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
/**
|
|
1414
|
+
* Prompt: appinsights-availability-report
|
|
1415
|
+
* Generate an availability and uptime report
|
|
1416
|
+
*/
|
|
1417
|
+
server.prompt("appinsights-availability-report", "Generate an availability and uptime report from Application Insights", {
|
|
1418
|
+
resourceId: z.string().describe("Resource ID"),
|
|
1419
|
+
timespan: z.string().optional().describe("Time range (default: PT24H)"),
|
|
1420
|
+
}, async ({ resourceId, timespan }) => {
|
|
1421
|
+
try {
|
|
1422
|
+
const service = getApplicationInsightsService();
|
|
1423
|
+
const timespanValue = timespan || 'PT24H';
|
|
1424
|
+
// Get availability results
|
|
1425
|
+
const availabilityResult = await service.getAvailabilityResults(resourceId, timespanValue);
|
|
1426
|
+
// Format results
|
|
1427
|
+
const availabilityTable = formatTableAsMarkdown(availabilityResult.tables[0]);
|
|
1428
|
+
const report = `# Application Insights Availability Report\n\n` +
|
|
1429
|
+
`**Resource**: ${resourceId}\n` +
|
|
1430
|
+
`**Time Range**: ${timespanValue}\n\n` +
|
|
1431
|
+
`## Availability Test Results\n\n${availabilityTable}\n\n` +
|
|
1432
|
+
`## Recommendations\n\n` +
|
|
1433
|
+
`- Investigate any tests with success rates below 99.9%\n` +
|
|
1434
|
+
`- Review failed tests for patterns (geographic, time-based)\n` +
|
|
1435
|
+
`- Consider adding availability tests for critical endpoints if missing\n` +
|
|
1436
|
+
`- Set up alerts for availability degradation`;
|
|
1437
|
+
return {
|
|
1438
|
+
messages: [
|
|
1439
|
+
{
|
|
1440
|
+
role: "assistant",
|
|
1441
|
+
content: {
|
|
1442
|
+
type: "text",
|
|
1443
|
+
text: report,
|
|
1444
|
+
},
|
|
1445
|
+
},
|
|
1446
|
+
],
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
catch (error) {
|
|
1450
|
+
console.error("Error generating availability report:", error);
|
|
1451
|
+
return {
|
|
1452
|
+
messages: [
|
|
1453
|
+
{
|
|
1454
|
+
role: "assistant",
|
|
1455
|
+
content: {
|
|
1456
|
+
type: "text",
|
|
1457
|
+
text: `Failed to generate availability report: ${error.message}`,
|
|
1458
|
+
},
|
|
1459
|
+
},
|
|
1460
|
+
],
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
/**
|
|
1465
|
+
* Prompt: appinsights-troubleshooting-guide
|
|
1466
|
+
* Generate a comprehensive troubleshooting guide
|
|
1467
|
+
*/
|
|
1468
|
+
server.prompt("appinsights-troubleshooting-guide", "Generate a comprehensive troubleshooting guide combining exceptions, performance, and dependencies", {
|
|
1469
|
+
resourceId: z.string().describe("Resource ID"),
|
|
1470
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
1471
|
+
}, async ({ resourceId, timespan }) => {
|
|
1472
|
+
try {
|
|
1473
|
+
const service = getApplicationInsightsService();
|
|
1474
|
+
const timespanValue = timespan || 'PT1H';
|
|
1475
|
+
// Get data from multiple sources
|
|
1476
|
+
const exceptionsResult = await service.getRecentExceptions(resourceId, timespanValue, 20);
|
|
1477
|
+
const slowRequestsResult = await service.getSlowRequests(resourceId, 5000, timespanValue, 20);
|
|
1478
|
+
const failedDepsResult = await service.getFailedDependencies(resourceId, timespanValue, 20);
|
|
1479
|
+
const tracesResult = await service.getTracesBySeverity(resourceId, 3, timespanValue, 30); // Error level
|
|
1480
|
+
// Format results
|
|
1481
|
+
const exceptionsTable = formatTableAsMarkdown(exceptionsResult.tables[0]);
|
|
1482
|
+
const slowRequestsTable = formatTableAsMarkdown(slowRequestsResult.tables[0]);
|
|
1483
|
+
const failedDepsTable = formatTableAsMarkdown(failedDepsResult.tables[0]);
|
|
1484
|
+
const tracesTable = formatTableAsMarkdown(tracesResult.tables[0]);
|
|
1485
|
+
const report = `# Application Insights Troubleshooting Guide\n\n` +
|
|
1486
|
+
`**Resource**: ${resourceId}\n` +
|
|
1487
|
+
`**Time Range**: ${timespanValue}\n` +
|
|
1488
|
+
`**Generated**: ${new Date().toISOString()}\n\n` +
|
|
1489
|
+
`## 1. Recent Errors and Exceptions\n\n${exceptionsTable}\n\n` +
|
|
1490
|
+
`## 2. Performance Issues\n\n${slowRequestsTable}\n\n` +
|
|
1491
|
+
`## 3. Dependency Failures\n\n${failedDepsTable}\n\n` +
|
|
1492
|
+
`## 4. Diagnostic Logs (Errors)\n\n${tracesTable}\n\n` +
|
|
1493
|
+
`## 5. Investigation Steps\n\n` +
|
|
1494
|
+
`1. **Identify the pattern**: Check if errors are isolated or widespread\n` +
|
|
1495
|
+
`2. **Correlate events**: Use operation_Id to trace requests across services\n` +
|
|
1496
|
+
`3. **Check timeline**: Look for correlation with deployments or external events\n` +
|
|
1497
|
+
`4. **Review dependencies**: Verify external service health\n` +
|
|
1498
|
+
`5. **Analyze traces**: Review detailed logs for error context\n\n` +
|
|
1499
|
+
`## 6. Common Patterns and Root Causes\n\n` +
|
|
1500
|
+
`- **High exception rate + dependency failures**: External service degradation\n` +
|
|
1501
|
+
`- **Slow requests + high dependency duration**: Network or external API latency\n` +
|
|
1502
|
+
`- **Exceptions in specific operations**: Code defect or invalid input\n` +
|
|
1503
|
+
`- **Timeouts**: Insufficient resources or inefficient queries`;
|
|
1504
|
+
return {
|
|
1505
|
+
messages: [
|
|
1506
|
+
{
|
|
1507
|
+
role: "assistant",
|
|
1508
|
+
content: {
|
|
1509
|
+
type: "text",
|
|
1510
|
+
text: report,
|
|
1511
|
+
},
|
|
1512
|
+
},
|
|
1513
|
+
],
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
catch (error) {
|
|
1517
|
+
console.error("Error generating troubleshooting guide:", error);
|
|
1518
|
+
return {
|
|
1519
|
+
messages: [
|
|
1520
|
+
{
|
|
1521
|
+
role: "assistant",
|
|
1522
|
+
content: {
|
|
1523
|
+
type: "text",
|
|
1524
|
+
text: `Failed to generate troubleshooting guide: ${error.message}`,
|
|
1525
|
+
},
|
|
1526
|
+
},
|
|
1527
|
+
],
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
});
|
|
1172
1531
|
// PowerPlatform entity metadata
|
|
1173
1532
|
server.tool("get-entity-metadata", "Get metadata about a PowerPlatform entity", {
|
|
1174
1533
|
entityName: z.string().describe("The logical name of the entity"),
|
|
@@ -4685,6 +5044,341 @@ server.tool("check-delete-eligibility", "Check if a component can be safely dele
|
|
|
4685
5044
|
};
|
|
4686
5045
|
}
|
|
4687
5046
|
});
|
|
5047
|
+
/**
|
|
5048
|
+
* Tool: appinsights-list-resources
|
|
5049
|
+
* List all configured Application Insights resources
|
|
5050
|
+
*/
|
|
5051
|
+
server.tool("appinsights-list-resources", "List all configured Application Insights resources (active and inactive)", {}, async () => {
|
|
5052
|
+
try {
|
|
5053
|
+
const service = getApplicationInsightsService();
|
|
5054
|
+
const resources = service.getAllResources();
|
|
5055
|
+
return {
|
|
5056
|
+
content: [
|
|
5057
|
+
{
|
|
5058
|
+
type: "text",
|
|
5059
|
+
text: JSON.stringify(resources, null, 2),
|
|
5060
|
+
},
|
|
5061
|
+
],
|
|
5062
|
+
};
|
|
5063
|
+
}
|
|
5064
|
+
catch (error) {
|
|
5065
|
+
console.error("Error listing Application Insights resources:", error);
|
|
5066
|
+
return {
|
|
5067
|
+
content: [
|
|
5068
|
+
{
|
|
5069
|
+
type: "text",
|
|
5070
|
+
text: `Failed to list Application Insights resources: ${error.message}`,
|
|
5071
|
+
},
|
|
5072
|
+
],
|
|
5073
|
+
isError: true
|
|
5074
|
+
};
|
|
5075
|
+
}
|
|
5076
|
+
});
|
|
5077
|
+
/**
|
|
5078
|
+
* Tool: appinsights-get-metadata
|
|
5079
|
+
* Get schema metadata for an Application Insights resource
|
|
5080
|
+
*/
|
|
5081
|
+
server.tool("appinsights-get-metadata", "Get schema metadata (tables and columns) for an Application Insights resource", {
|
|
5082
|
+
resourceId: z.string().describe("Resource ID (use appinsights-list-resources to find IDs)"),
|
|
5083
|
+
}, async ({ resourceId }) => {
|
|
5084
|
+
try {
|
|
5085
|
+
const service = getApplicationInsightsService();
|
|
5086
|
+
const metadata = await service.getMetadata(resourceId);
|
|
5087
|
+
return {
|
|
5088
|
+
content: [
|
|
5089
|
+
{
|
|
5090
|
+
type: "text",
|
|
5091
|
+
text: JSON.stringify(metadata, null, 2),
|
|
5092
|
+
},
|
|
5093
|
+
],
|
|
5094
|
+
};
|
|
5095
|
+
}
|
|
5096
|
+
catch (error) {
|
|
5097
|
+
console.error("Error getting Application Insights metadata:", error);
|
|
5098
|
+
return {
|
|
5099
|
+
content: [
|
|
5100
|
+
{
|
|
5101
|
+
type: "text",
|
|
5102
|
+
text: `Failed to get metadata: ${error.message}`,
|
|
5103
|
+
},
|
|
5104
|
+
],
|
|
5105
|
+
isError: true
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
});
|
|
5109
|
+
/**
|
|
5110
|
+
* Tool: appinsights-execute-query
|
|
5111
|
+
* Execute a custom KQL query against an Application Insights resource
|
|
5112
|
+
*/
|
|
5113
|
+
server.tool("appinsights-execute-query", "Execute a KQL (Kusto Query Language) query against Application Insights", {
|
|
5114
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5115
|
+
query: z.string().describe("KQL query string"),
|
|
5116
|
+
timespan: z.string().optional().describe("Time range (e.g., 'PT1H' for 1 hour, 'P1D' for 1 day, 'PT12H' for 12 hours)"),
|
|
5117
|
+
}, async ({ resourceId, query, timespan }) => {
|
|
5118
|
+
try {
|
|
5119
|
+
const service = getApplicationInsightsService();
|
|
5120
|
+
const result = await service.executeQuery(resourceId, query, timespan);
|
|
5121
|
+
return {
|
|
5122
|
+
content: [
|
|
5123
|
+
{
|
|
5124
|
+
type: "text",
|
|
5125
|
+
text: JSON.stringify(result, null, 2),
|
|
5126
|
+
},
|
|
5127
|
+
],
|
|
5128
|
+
};
|
|
5129
|
+
}
|
|
5130
|
+
catch (error) {
|
|
5131
|
+
console.error("Error executing Application Insights query:", error);
|
|
5132
|
+
return {
|
|
5133
|
+
content: [
|
|
5134
|
+
{
|
|
5135
|
+
type: "text",
|
|
5136
|
+
text: `Failed to execute query: ${error.message}`,
|
|
5137
|
+
},
|
|
5138
|
+
],
|
|
5139
|
+
isError: true
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
});
|
|
5143
|
+
/**
|
|
5144
|
+
* Tool: appinsights-get-exceptions
|
|
5145
|
+
* Get recent exceptions from Application Insights
|
|
5146
|
+
*/
|
|
5147
|
+
server.tool("appinsights-get-exceptions", "Get recent exceptions from Application Insights with timestamps, types, and messages", {
|
|
5148
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5149
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5150
|
+
limit: z.number().optional().describe("Maximum number of results (default: 50)"),
|
|
5151
|
+
}, async ({ resourceId, timespan, limit }) => {
|
|
5152
|
+
try {
|
|
5153
|
+
const service = getApplicationInsightsService();
|
|
5154
|
+
const result = await service.getRecentExceptions(resourceId, timespan || 'PT1H', limit || 50);
|
|
5155
|
+
return {
|
|
5156
|
+
content: [
|
|
5157
|
+
{
|
|
5158
|
+
type: "text",
|
|
5159
|
+
text: JSON.stringify(result, null, 2),
|
|
5160
|
+
},
|
|
5161
|
+
],
|
|
5162
|
+
};
|
|
5163
|
+
}
|
|
5164
|
+
catch (error) {
|
|
5165
|
+
console.error("Error getting Application Insights exceptions:", error);
|
|
5166
|
+
return {
|
|
5167
|
+
content: [
|
|
5168
|
+
{
|
|
5169
|
+
type: "text",
|
|
5170
|
+
text: `Failed to get exceptions: ${error.message}`,
|
|
5171
|
+
},
|
|
5172
|
+
],
|
|
5173
|
+
isError: true
|
|
5174
|
+
};
|
|
5175
|
+
}
|
|
5176
|
+
});
|
|
5177
|
+
/**
|
|
5178
|
+
* Tool: appinsights-get-slow-requests
|
|
5179
|
+
* Get slow HTTP requests from Application Insights
|
|
5180
|
+
*/
|
|
5181
|
+
server.tool("appinsights-get-slow-requests", "Get slow HTTP requests (above duration threshold) from Application Insights", {
|
|
5182
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5183
|
+
durationThresholdMs: z.number().optional().describe("Duration threshold in milliseconds (default: 5000)"),
|
|
5184
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5185
|
+
limit: z.number().optional().describe("Maximum number of results (default: 50)"),
|
|
5186
|
+
}, async ({ resourceId, durationThresholdMs, timespan, limit }) => {
|
|
5187
|
+
try {
|
|
5188
|
+
const service = getApplicationInsightsService();
|
|
5189
|
+
const result = await service.getSlowRequests(resourceId, durationThresholdMs || 5000, timespan || 'PT1H', limit || 50);
|
|
5190
|
+
return {
|
|
5191
|
+
content: [
|
|
5192
|
+
{
|
|
5193
|
+
type: "text",
|
|
5194
|
+
text: JSON.stringify(result, null, 2),
|
|
5195
|
+
},
|
|
5196
|
+
],
|
|
5197
|
+
};
|
|
5198
|
+
}
|
|
5199
|
+
catch (error) {
|
|
5200
|
+
console.error("Error getting slow requests:", error);
|
|
5201
|
+
return {
|
|
5202
|
+
content: [
|
|
5203
|
+
{
|
|
5204
|
+
type: "text",
|
|
5205
|
+
text: `Failed to get slow requests: ${error.message}`,
|
|
5206
|
+
},
|
|
5207
|
+
],
|
|
5208
|
+
isError: true
|
|
5209
|
+
};
|
|
5210
|
+
}
|
|
5211
|
+
});
|
|
5212
|
+
/**
|
|
5213
|
+
* Tool: appinsights-get-operation-performance
|
|
5214
|
+
* Get operation performance summary from Application Insights
|
|
5215
|
+
*/
|
|
5216
|
+
server.tool("appinsights-get-operation-performance", "Get performance summary by operation (request count, avg duration, percentiles)", {
|
|
5217
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5218
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5219
|
+
}, async ({ resourceId, timespan }) => {
|
|
5220
|
+
try {
|
|
5221
|
+
const service = getApplicationInsightsService();
|
|
5222
|
+
const result = await service.getOperationPerformance(resourceId, timespan || 'PT1H');
|
|
5223
|
+
return {
|
|
5224
|
+
content: [
|
|
5225
|
+
{
|
|
5226
|
+
type: "text",
|
|
5227
|
+
text: JSON.stringify(result, null, 2),
|
|
5228
|
+
},
|
|
5229
|
+
],
|
|
5230
|
+
};
|
|
5231
|
+
}
|
|
5232
|
+
catch (error) {
|
|
5233
|
+
console.error("Error getting operation performance:", error);
|
|
5234
|
+
return {
|
|
5235
|
+
content: [
|
|
5236
|
+
{
|
|
5237
|
+
type: "text",
|
|
5238
|
+
text: `Failed to get operation performance: ${error.message}`,
|
|
5239
|
+
},
|
|
5240
|
+
],
|
|
5241
|
+
isError: true
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
});
|
|
5245
|
+
/**
|
|
5246
|
+
* Tool: appinsights-get-failed-dependencies
|
|
5247
|
+
* Get failed dependency calls from Application Insights
|
|
5248
|
+
*/
|
|
5249
|
+
server.tool("appinsights-get-failed-dependencies", "Get failed dependency calls (external APIs, databases, etc.) from Application Insights", {
|
|
5250
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5251
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5252
|
+
limit: z.number().optional().describe("Maximum number of results (default: 50)"),
|
|
5253
|
+
}, async ({ resourceId, timespan, limit }) => {
|
|
5254
|
+
try {
|
|
5255
|
+
const service = getApplicationInsightsService();
|
|
5256
|
+
const result = await service.getFailedDependencies(resourceId, timespan || 'PT1H', limit || 50);
|
|
5257
|
+
return {
|
|
5258
|
+
content: [
|
|
5259
|
+
{
|
|
5260
|
+
type: "text",
|
|
5261
|
+
text: JSON.stringify(result, null, 2),
|
|
5262
|
+
},
|
|
5263
|
+
],
|
|
5264
|
+
};
|
|
5265
|
+
}
|
|
5266
|
+
catch (error) {
|
|
5267
|
+
console.error("Error getting failed dependencies:", error);
|
|
5268
|
+
return {
|
|
5269
|
+
content: [
|
|
5270
|
+
{
|
|
5271
|
+
type: "text",
|
|
5272
|
+
text: `Failed to get failed dependencies: ${error.message}`,
|
|
5273
|
+
},
|
|
5274
|
+
],
|
|
5275
|
+
isError: true
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5278
|
+
});
|
|
5279
|
+
/**
|
|
5280
|
+
* Tool: appinsights-get-traces
|
|
5281
|
+
* Get diagnostic traces from Application Insights filtered by severity
|
|
5282
|
+
*/
|
|
5283
|
+
server.tool("appinsights-get-traces", "Get diagnostic traces/logs from Application Insights filtered by severity level", {
|
|
5284
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5285
|
+
severityLevel: z.number().optional().describe("Minimum severity level (0=Verbose, 1=Info, 2=Warning, 3=Error, 4=Critical) (default: 2)"),
|
|
5286
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5287
|
+
limit: z.number().optional().describe("Maximum number of results (default: 100)"),
|
|
5288
|
+
}, async ({ resourceId, severityLevel, timespan, limit }) => {
|
|
5289
|
+
try {
|
|
5290
|
+
const service = getApplicationInsightsService();
|
|
5291
|
+
const result = await service.getTracesBySeverity(resourceId, severityLevel ?? 2, timespan || 'PT1H', limit || 100);
|
|
5292
|
+
return {
|
|
5293
|
+
content: [
|
|
5294
|
+
{
|
|
5295
|
+
type: "text",
|
|
5296
|
+
text: JSON.stringify(result, null, 2),
|
|
5297
|
+
},
|
|
5298
|
+
],
|
|
5299
|
+
};
|
|
5300
|
+
}
|
|
5301
|
+
catch (error) {
|
|
5302
|
+
console.error("Error getting traces:", error);
|
|
5303
|
+
return {
|
|
5304
|
+
content: [
|
|
5305
|
+
{
|
|
5306
|
+
type: "text",
|
|
5307
|
+
text: `Failed to get traces: ${error.message}`,
|
|
5308
|
+
},
|
|
5309
|
+
],
|
|
5310
|
+
isError: true
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
});
|
|
5314
|
+
/**
|
|
5315
|
+
* Tool: appinsights-get-availability
|
|
5316
|
+
* Get availability test results from Application Insights
|
|
5317
|
+
*/
|
|
5318
|
+
server.tool("appinsights-get-availability", "Get availability test results and uptime statistics from Application Insights", {
|
|
5319
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5320
|
+
timespan: z.string().optional().describe("Time range (default: PT24H)"),
|
|
5321
|
+
}, async ({ resourceId, timespan }) => {
|
|
5322
|
+
try {
|
|
5323
|
+
const service = getApplicationInsightsService();
|
|
5324
|
+
const result = await service.getAvailabilityResults(resourceId, timespan || 'PT24H');
|
|
5325
|
+
return {
|
|
5326
|
+
content: [
|
|
5327
|
+
{
|
|
5328
|
+
type: "text",
|
|
5329
|
+
text: JSON.stringify(result, null, 2),
|
|
5330
|
+
},
|
|
5331
|
+
],
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
catch (error) {
|
|
5335
|
+
console.error("Error getting availability results:", error);
|
|
5336
|
+
return {
|
|
5337
|
+
content: [
|
|
5338
|
+
{
|
|
5339
|
+
type: "text",
|
|
5340
|
+
text: `Failed to get availability results: ${error.message}`,
|
|
5341
|
+
},
|
|
5342
|
+
],
|
|
5343
|
+
isError: true
|
|
5344
|
+
};
|
|
5345
|
+
}
|
|
5346
|
+
});
|
|
5347
|
+
/**
|
|
5348
|
+
* Tool: appinsights-get-custom-events
|
|
5349
|
+
* Get custom application events from Application Insights
|
|
5350
|
+
*/
|
|
5351
|
+
server.tool("appinsights-get-custom-events", "Get custom application events from Application Insights", {
|
|
5352
|
+
resourceId: z.string().describe("Resource ID"),
|
|
5353
|
+
eventName: z.string().optional().describe("Filter by specific event name"),
|
|
5354
|
+
timespan: z.string().optional().describe("Time range (default: PT1H)"),
|
|
5355
|
+
limit: z.number().optional().describe("Maximum number of results (default: 100)"),
|
|
5356
|
+
}, async ({ resourceId, eventName, timespan, limit }) => {
|
|
5357
|
+
try {
|
|
5358
|
+
const service = getApplicationInsightsService();
|
|
5359
|
+
const result = await service.getCustomEvents(resourceId, eventName, timespan || 'PT1H', limit || 100);
|
|
5360
|
+
return {
|
|
5361
|
+
content: [
|
|
5362
|
+
{
|
|
5363
|
+
type: "text",
|
|
5364
|
+
text: JSON.stringify(result, null, 2),
|
|
5365
|
+
},
|
|
5366
|
+
],
|
|
5367
|
+
};
|
|
5368
|
+
}
|
|
5369
|
+
catch (error) {
|
|
5370
|
+
console.error("Error getting custom events:", error);
|
|
5371
|
+
return {
|
|
5372
|
+
content: [
|
|
5373
|
+
{
|
|
5374
|
+
type: "text",
|
|
5375
|
+
text: `Failed to get custom events: ${error.message}`,
|
|
5376
|
+
},
|
|
5377
|
+
],
|
|
5378
|
+
isError: true
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
});
|
|
4688
5382
|
async function main() {
|
|
4689
5383
|
const transport = new StdioServerTransport();
|
|
4690
5384
|
await server.connect(transport);
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for formatting Application Insights query results
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format Application Insights table result as markdown table
|
|
6
|
+
*/
|
|
7
|
+
export function formatTableAsMarkdown(table) {
|
|
8
|
+
if (!table || !table.rows || table.rows.length === 0) {
|
|
9
|
+
return '*No results*';
|
|
10
|
+
}
|
|
11
|
+
// Create header row
|
|
12
|
+
const header = '| ' + table.columns.map(c => c.name).join(' | ') + ' |';
|
|
13
|
+
const separator = '| ' + table.columns.map(() => '---').join(' | ') + ' |';
|
|
14
|
+
// Create data rows
|
|
15
|
+
const rows = table.rows.map(row => {
|
|
16
|
+
return '| ' + row.map(cell => {
|
|
17
|
+
if (cell === null || cell === undefined)
|
|
18
|
+
return '';
|
|
19
|
+
if (typeof cell === 'object')
|
|
20
|
+
return JSON.stringify(cell);
|
|
21
|
+
return String(cell);
|
|
22
|
+
}).join(' | ') + ' |';
|
|
23
|
+
});
|
|
24
|
+
return [header, separator, ...rows].join('\n');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert Application Insights query result to CSV
|
|
28
|
+
*/
|
|
29
|
+
export function formatTableAsCSV(table) {
|
|
30
|
+
if (!table || !table.rows || table.rows.length === 0) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
// Create header row
|
|
34
|
+
const header = table.columns.map(c => c.name).join(',');
|
|
35
|
+
// Create data rows
|
|
36
|
+
const rows = table.rows.map(row => {
|
|
37
|
+
return row.map(cell => {
|
|
38
|
+
if (cell === null || cell === undefined)
|
|
39
|
+
return '';
|
|
40
|
+
if (typeof cell === 'object')
|
|
41
|
+
return JSON.stringify(cell);
|
|
42
|
+
const str = String(cell);
|
|
43
|
+
// Escape CSV values
|
|
44
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
45
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
46
|
+
}
|
|
47
|
+
return str;
|
|
48
|
+
}).join(',');
|
|
49
|
+
});
|
|
50
|
+
return [header, ...rows].join('\n');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract key insights from exception data
|
|
54
|
+
*/
|
|
55
|
+
export function analyzeExceptions(exceptionsTable) {
|
|
56
|
+
if (!exceptionsTable || !exceptionsTable.rows || exceptionsTable.rows.length === 0) {
|
|
57
|
+
return 'No exceptions found in the specified time range.';
|
|
58
|
+
}
|
|
59
|
+
const insights = [];
|
|
60
|
+
// Count unique exception types
|
|
61
|
+
const typeIndex = exceptionsTable.columns.findIndex((c) => c.name === 'type');
|
|
62
|
+
if (typeIndex >= 0) {
|
|
63
|
+
const types = new Set(exceptionsTable.rows.map((r) => r[typeIndex]));
|
|
64
|
+
insights.push(`- Found ${types.size} unique exception type(s)`);
|
|
65
|
+
}
|
|
66
|
+
// Count total exceptions
|
|
67
|
+
insights.push(`- Total exceptions: ${exceptionsTable.rows.length}`);
|
|
68
|
+
// Identify most common operation
|
|
69
|
+
const operationIndex = exceptionsTable.columns.findIndex((c) => c.name === 'operation_Name');
|
|
70
|
+
if (operationIndex >= 0) {
|
|
71
|
+
const operations = {};
|
|
72
|
+
exceptionsTable.rows.forEach((r) => {
|
|
73
|
+
const op = r[operationIndex];
|
|
74
|
+
operations[op] = (operations[op] || 0) + 1;
|
|
75
|
+
});
|
|
76
|
+
const mostCommon = Object.entries(operations).sort((a, b) => b[1] - a[1])[0];
|
|
77
|
+
if (mostCommon) {
|
|
78
|
+
insights.push(`- Most affected operation: ${mostCommon[0]} (${mostCommon[1]} exceptions)`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return insights.join('\n');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extract key insights from performance data
|
|
85
|
+
*/
|
|
86
|
+
export function analyzePerformance(performanceTable) {
|
|
87
|
+
if (!performanceTable || !performanceTable.rows || performanceTable.rows.length === 0) {
|
|
88
|
+
return 'No performance data found in the specified time range.';
|
|
89
|
+
}
|
|
90
|
+
const insights = [];
|
|
91
|
+
// Find slowest operation
|
|
92
|
+
const avgDurationIndex = performanceTable.columns.findIndex((c) => c.name === 'AvgDuration');
|
|
93
|
+
const operationIndex = performanceTable.columns.findIndex((c) => c.name === 'operation_Name');
|
|
94
|
+
if (avgDurationIndex >= 0 && operationIndex >= 0) {
|
|
95
|
+
const sorted = [...performanceTable.rows].sort((a, b) => b[avgDurationIndex] - a[avgDurationIndex]);
|
|
96
|
+
const slowest = sorted[0];
|
|
97
|
+
insights.push(`- Slowest operation: ${slowest[operationIndex]} (avg: ${Math.round(slowest[avgDurationIndex])}ms)`);
|
|
98
|
+
}
|
|
99
|
+
// Find operation with most failures
|
|
100
|
+
const failureIndex = performanceTable.columns.findIndex((c) => c.name === 'FailureCount');
|
|
101
|
+
if (failureIndex >= 0 && operationIndex >= 0) {
|
|
102
|
+
const sorted = [...performanceTable.rows].sort((a, b) => b[failureIndex] - a[failureIndex]);
|
|
103
|
+
const mostFailed = sorted[0];
|
|
104
|
+
if (mostFailed[failureIndex] > 0) {
|
|
105
|
+
insights.push(`- Operation with most failures: ${mostFailed[operationIndex]} (${mostFailed[failureIndex]} failures)`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return insights.join('\n');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract key insights from dependency data
|
|
112
|
+
*/
|
|
113
|
+
export function analyzeDependencies(dependenciesTable) {
|
|
114
|
+
if (!dependenciesTable || !dependenciesTable.rows || dependenciesTable.rows.length === 0) {
|
|
115
|
+
return 'No dependency failures found in the specified time range.';
|
|
116
|
+
}
|
|
117
|
+
const insights = [];
|
|
118
|
+
// Count unique targets
|
|
119
|
+
const targetIndex = dependenciesTable.columns.findIndex((c) => c.name === 'target');
|
|
120
|
+
if (targetIndex >= 0) {
|
|
121
|
+
const targets = new Set(dependenciesTable.rows.map((r) => r[targetIndex]));
|
|
122
|
+
insights.push(`- Affected targets: ${targets.size}`);
|
|
123
|
+
}
|
|
124
|
+
// Count total failures
|
|
125
|
+
insights.push(`- Total failed dependency calls: ${dependenciesTable.rows.length}`);
|
|
126
|
+
// Identify most failing target
|
|
127
|
+
if (targetIndex >= 0) {
|
|
128
|
+
const targets = {};
|
|
129
|
+
dependenciesTable.rows.forEach((r) => {
|
|
130
|
+
const target = r[targetIndex];
|
|
131
|
+
targets[target] = (targets[target] || 0) + 1;
|
|
132
|
+
});
|
|
133
|
+
const mostFailing = Object.entries(targets).sort((a, b) => b[1] - a[1])[0];
|
|
134
|
+
if (mostFailing) {
|
|
135
|
+
insights.push(`- Most failing target: ${mostFailing[0]} (${mostFailing[1]} failures)`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return insights.join('\n');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Sanitize error messages to remove sensitive information
|
|
142
|
+
*/
|
|
143
|
+
export function sanitizeErrorMessage(message) {
|
|
144
|
+
// Remove potential connection strings
|
|
145
|
+
message = message.replace(/Server=.+?;/gi, 'Server=***;');
|
|
146
|
+
message = message.replace(/Password=.+?;/gi, 'Password=***;');
|
|
147
|
+
message = message.replace(/ApiKey=.+?;/gi, 'ApiKey=***;');
|
|
148
|
+
// Remove potential API keys and tokens
|
|
149
|
+
message = message.replace(/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, 'Bearer ***');
|
|
150
|
+
message = message.replace(/[A-Za-z0-9]{32,}/g, '***'); // Remove long alphanumeric strings
|
|
151
|
+
return message;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Parse and validate ISO 8601 duration strings
|
|
155
|
+
*/
|
|
156
|
+
export function parseTimespan(timespan) {
|
|
157
|
+
const iso8601Pattern = /^P(?:(\d+)D)?T?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
|
|
158
|
+
const match = timespan.match(iso8601Pattern);
|
|
159
|
+
if (!match) {
|
|
160
|
+
return {
|
|
161
|
+
valid: false,
|
|
162
|
+
error: 'Invalid timespan format. Use ISO 8601 duration (e.g., PT1H, P1D, PT30M)',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return { valid: true };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get common timespan presets
|
|
169
|
+
*/
|
|
170
|
+
export function getTimespanPresets() {
|
|
171
|
+
return {
|
|
172
|
+
'15min': 'PT15M',
|
|
173
|
+
'30min': 'PT30M',
|
|
174
|
+
'1hour': 'PT1H',
|
|
175
|
+
'6hours': 'PT6H',
|
|
176
|
+
'12hours': 'PT12H',
|
|
177
|
+
'1day': 'P1D',
|
|
178
|
+
'7days': 'P7D',
|
|
179
|
+
'30days': 'P30D',
|
|
180
|
+
};
|
|
181
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-consultant-tools",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "MCP server providing access to PowerPlatform/Dataverse, Azure DevOps, and
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "MCP server providing access to PowerPlatform/Dataverse, Azure DevOps, Figma, and Azure Application Insights through unified interface",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mcp-consultant-tools": "build/index.js"
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"dataverse",
|
|
23
23
|
"azure-devops",
|
|
24
24
|
"figma",
|
|
25
|
+
"application-insights",
|
|
26
|
+
"telemetry",
|
|
27
|
+
"monitoring",
|
|
25
28
|
"design-tools",
|
|
26
29
|
"devops"
|
|
27
30
|
],
|