list-metric-types
List available metric types in Google Cloud Monitoring using filters, set page size, and define request timeout for efficient data retrieval.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filter | No | Simple search term (e.g., "spanner") or full filter expression (e.g., "metric.type = starts_with(\"spanner\")") | |
| pageSize | No | Maximum number of metric types to return | |
| timeout | No | Timeout in seconds for the request |
Implementation Reference
- src/services/monitoring/tools.ts:124-255 (handler)The complete handler function for the 'list-metric-types' tool. It registers the tool with Zod input schema, fetches metric descriptors from Google Cloud Monitoring API using listMetricDescriptors, handles timeouts, client-side filtering for simple terms, and formats results as a markdown table with columns for type, display name, kind, value type, and description.server.tool( 'list-metric-types', { filter: z.string().optional().describe('Simple search term (e.g., "spanner") or full filter expression (e.g., "metric.type = starts_with(\\"spanner\\")")'), pageSize: z.number().min(1).max(100).default(20).describe('Maximum number of metric types to return'), timeout: z.number().min(5).max(60).default(30).describe('Timeout in seconds for the request') }, async ({ filter, pageSize, timeout }, context) => { try { const projectId = await getProjectId(); const client = getMonitoringClient(); // Format the filter if it's a simple string without operators let formattedFilter = filter; let useClientSideFiltering = false; if (filter && !filter.includes('=') && !filter.includes('>') && !filter.includes('<')) { // If it's just a simple term, we'll use client-side filtering // We don't set a filter for the API call to avoid syntax errors formattedFilter = undefined; useClientSideFiltering = true; } // Create a promise that rejects after the timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Request timed out after ${timeout} seconds`)); }, timeout * 1000); }); // Create the actual request promise const requestPromise = (async () => { const request: any = { name: `projects/${projectId}`, pageSize }; if (formattedFilter) { request.filter = formattedFilter; } return await client.listMetricDescriptors(request); })(); // Race the timeout against the actual request const [metricDescriptors] = await Promise.race([requestPromise, timeoutPromise]) as [any]; // Apply client-side filtering if needed let filteredDescriptors = metricDescriptors; if (useClientSideFiltering && filter) { const searchTerm = filter.toLowerCase(); filteredDescriptors = metricDescriptors.filter((descriptor: any) => { // Search in the type name if (descriptor.type && descriptor.type.toLowerCase().includes(searchTerm)) { return true; } // Search in the display name if (descriptor.displayName && descriptor.displayName.toLowerCase().includes(searchTerm)) { return true; } // Search in the description if (descriptor.description && descriptor.description.toLowerCase().includes(searchTerm)) { return true; } return false; }); } if (!filteredDescriptors || filteredDescriptors.length === 0) { return { content: [{ type: 'text', text: `# Available Metric Types\n\nProject: ${projectId}\n${filter ? `\nSearch term: "${filter}"` : ''}\n\nNo metric types found matching your search term. Try a different search term or increase the timeout.` }] }; } let markdown = `# Available Metric Types\n\nProject: ${projectId}\n${filter ? `\nSearch term: "${filter}"` : ''}\n${useClientSideFiltering ? `\n*Note: Filtering was performed client-side by searching for "${filter}" in metric type, display name, and description.*` : ''}\n\nFound ${filteredDescriptors.length} metric types${metricDescriptors.length !== filteredDescriptors.length ? ` (filtered from ${metricDescriptors.length} total)` : ''}.\n\n`; // Table header markdown += '| Metric Type | Display Name | Kind | Value Type | Description |\n'; markdown += '|-------------|--------------|------|------------|-------------|\n'; // Table rows - limit to first 50 to avoid excessive output const limitedDescriptors = filteredDescriptors.slice(0, 50); for (const descriptor of limitedDescriptors) { const description = (descriptor.description || '').replace(/\n/g, ' ').substring(0, 100); markdown += `| ${descriptor.type || ''} | ${descriptor.displayName || ''} | ${descriptor.metricKind || ''} | ${descriptor.valueType || ''} | ${description} |\n`; } if (filteredDescriptors.length > 50) { markdown += `\n*Note: Showing first 50 of ${filteredDescriptors.length} metric types. Use a more specific search term to narrow down results.*`; } return { content: [{ type: 'text', text: markdown }] }; } catch (error: unknown) { // Extract error message safely let errorMessage = 'Unknown error'; if (error instanceof Error) { errorMessage = error.message; } else if (typeof error === 'object' && error !== null) { errorMessage = String((error as any).message || JSON.stringify(error)); } else if (typeof error === 'string') { errorMessage = error; } // Check for timeout errors if (errorMessage.includes('timed out')) { throw new GcpMcpError( `Request timed out. Try using a filter to narrow down results or increase the timeout parameter.`, 'DEADLINE_EXCEEDED', 504 ); } // Handle other errors const errorCode = (error as any)?.code || 'UNKNOWN'; const statusCode = (error as any)?.statusCode || 500; throw new GcpMcpError( `Failed to list metric types: ${errorMessage}`, errorCode, statusCode ); } } );