query-metrics
Query Google Cloud Monitoring metrics using filters and time ranges to analyze performance data from your cloud infrastructure.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filter | Yes | The filter to apply to metrics | |
| startTime | Yes | Start time in ISO format or relative time (e.g., "1h", "2d") | |
| endTime | No | End time in ISO format (defaults to now) | |
| alignmentPeriod | No | Alignment period (e.g., "60s", "300s") |
Implementation Reference
- src/services/monitoring/tools.ts:34-121 (handler)Executes the query-metrics tool: queries GCP Monitoring API with filter and time range, handles alignment period, formats time series data as markdown table.async ({ filter, startTime, endTime, alignmentPeriod }, context) => { try { const projectId = await getProjectId(); const client = getMonitoringClient(); const start = parseRelativeTime(startTime); const end = endTime ? parseRelativeTime(endTime) : new Date(); // Build request const request: any = { name: `projects/${projectId}`, filter, interval: { startTime: { seconds: Math.floor(start.getTime() / 1000), nanos: 0 }, endTime: { seconds: Math.floor(end.getTime() / 1000), nanos: 0 } } }; // Add alignment if specified if (alignmentPeriod) { // Parse alignment period (e.g., "60s" -> 60 seconds) const match = alignmentPeriod.match(/^(\d+)([smhd])$/); if (!match) { throw new GcpMcpError( 'Invalid alignment period format. Use format like "60s", "5m", "1h".', 'INVALID_ARGUMENT', 400 ); } const value = parseInt(match[1]); const unit = match[2]; let seconds = value; switch (unit) { case 'm': // minutes seconds = value * 60; break; case 'h': // hours seconds = value * 60 * 60; break; case 'd': // days seconds = value * 60 * 60 * 24; break; } request.aggregation = { alignmentPeriod: { seconds: seconds }, perSeriesAligner: 'ALIGN_MEAN' }; } const [timeSeries] = await client.listTimeSeries(request); if (!timeSeries || timeSeries.length === 0) { return { content: [{ type: 'text', text: `# Metric Query Results\n\nProject: ${projectId}\nFilter: ${filter}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\n\nNo metrics found matching the filter.` }] }; } const formattedData = formatTimeSeriesData(timeSeries as unknown as TimeSeriesData[]); return { content: [{ type: 'text', text: `# Metric Query Results\n\nProject: ${projectId}\nFilter: ${filter}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\n${alignmentPeriod ? `\nAlignment: ${alignmentPeriod}` : ''}\n\n${formattedData}` }] }; } catch (error: any) { throw new GcpMcpError( `Failed to query metrics: ${error.message}`, error.code || 'UNKNOWN', error.statusCode || 500 ); } } );
- Zod schema defining inputs for query-metrics tool: filter (required), startTime (required), endTime (optional), alignmentPeriod (optional).{ filter: z.string().describe('The filter to apply to metrics'), startTime: z.string().describe('Start time in ISO format or relative time (e.g., "1h", "2d")'), endTime: z.string().optional().describe('End time in ISO format (defaults to now)'), alignmentPeriod: z.string().optional().describe('Alignment period (e.g., "60s", "300s")') },
- src/services/monitoring/tools.ts:26-121 (registration)Registers the 'query-metrics' tool with the MCP server inside registerMonitoringTools function.server.tool( 'query-metrics', { filter: z.string().describe('The filter to apply to metrics'), startTime: z.string().describe('Start time in ISO format or relative time (e.g., "1h", "2d")'), endTime: z.string().optional().describe('End time in ISO format (defaults to now)'), alignmentPeriod: z.string().optional().describe('Alignment period (e.g., "60s", "300s")') }, async ({ filter, startTime, endTime, alignmentPeriod }, context) => { try { const projectId = await getProjectId(); const client = getMonitoringClient(); const start = parseRelativeTime(startTime); const end = endTime ? parseRelativeTime(endTime) : new Date(); // Build request const request: any = { name: `projects/${projectId}`, filter, interval: { startTime: { seconds: Math.floor(start.getTime() / 1000), nanos: 0 }, endTime: { seconds: Math.floor(end.getTime() / 1000), nanos: 0 } } }; // Add alignment if specified if (alignmentPeriod) { // Parse alignment period (e.g., "60s" -> 60 seconds) const match = alignmentPeriod.match(/^(\d+)([smhd])$/); if (!match) { throw new GcpMcpError( 'Invalid alignment period format. Use format like "60s", "5m", "1h".', 'INVALID_ARGUMENT', 400 ); } const value = parseInt(match[1]); const unit = match[2]; let seconds = value; switch (unit) { case 'm': // minutes seconds = value * 60; break; case 'h': // hours seconds = value * 60 * 60; break; case 'd': // days seconds = value * 60 * 60 * 24; break; } request.aggregation = { alignmentPeriod: { seconds: seconds }, perSeriesAligner: 'ALIGN_MEAN' }; } const [timeSeries] = await client.listTimeSeries(request); if (!timeSeries || timeSeries.length === 0) { return { content: [{ type: 'text', text: `# Metric Query Results\n\nProject: ${projectId}\nFilter: ${filter}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\n\nNo metrics found matching the filter.` }] }; } const formattedData = formatTimeSeriesData(timeSeries as unknown as TimeSeriesData[]); return { content: [{ type: 'text', text: `# Metric Query Results\n\nProject: ${projectId}\nFilter: ${filter}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\n${alignmentPeriod ? `\nAlignment: ${alignmentPeriod}` : ''}\n\n${formattedData}` }] }; } catch (error: any) { throw new GcpMcpError( `Failed to query metrics: ${error.message}`, error.code || 'UNKNOWN', error.statusCode || 500 ); } } );