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 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 Figma through an AI-friendly interface.
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: 86+ MCP tools** providing comprehensive access to your development lifecycle.
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 **13 prompts** that provide formatted, context-rich output:
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 86+ tools and 12 prompts
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": "5.0.0",
4
- "description": "MCP server providing access to PowerPlatform/Dataverse, Azure DevOps, and Figma through unified interface",
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
  ],