Skip to main content
Glama

Grafana MCP Server

by levalhq
prometheus.tsโ€ข9.29 kB
import { z } from 'zod'; import { ToolDefinition, ToolContext, createToolResult, createErrorResult } from '../server/mcp-server'; import { PrometheusClient } from '../clients/prometheus-client'; const QueryPrometheusSchema = z.object({ datasourceUid: z.string().describe('The UID of the datasource to query'), expr: z.string().describe('The PromQL expression to query'), queryType: z.enum(['range', 'instant']).describe('The type of query to use'), startTime: z.string().describe('The start time (RFC3339 or relative like "now-1h")'), endTime: z.string().optional().describe('The end time for range queries'), stepSeconds: z.number().optional().describe('The time series step size in seconds for range queries'), }); const ListPrometheusMetricNamesSchema = z.object({ datasourceUid: z.string().describe('The UID of the datasource to query'), regex: z.string().optional().describe('The regex to match against the metric names'), limit: z.number().optional().describe('The maximum number of results to return'), page: z.number().optional().describe('The page number to return'), }); const ListPrometheusLabelNamesSchema = z.object({ datasourceUid: z.string().describe('The UID of the datasource to query'), matches: z.array(z.object({ filters: z.array(z.object({ name: z.string().describe('The name of the label to match against'), value: z.string().describe('The value to match against'), type: z.enum(['=', '!=', '=~', '!~']).describe('The match operator'), })), })).optional().describe('Label matchers to filter the results'), startRfc3339: z.string().optional().describe('The start time of the time range'), endRfc3339: z.string().optional().describe('The end time of the time range'), limit: z.number().optional().describe('Maximum number of results'), }); const ListPrometheusLabelValuesSchema = z.object({ datasourceUid: z.string().describe('The UID of the datasource to query'), labelName: z.string().describe('The name of the label to query'), matches: z.array(z.object({ filters: z.array(z.object({ name: z.string().describe('The name of the label to match against'), value: z.string().describe('The value to match against'), type: z.enum(['=', '!=', '=~', '!~']).describe('The match operator'), })), })).optional().describe('Selectors to filter the results'), startRfc3339: z.string().optional().describe('The start time of the query'), endRfc3339: z.string().optional().describe('The end time of the query'), limit: z.number().optional().describe('Maximum number of results'), }); const ListPrometheusMetricMetadataSchema = z.object({ datasourceUid: z.string().describe('The UID of the datasource to query'), metric: z.string().optional().describe('The metric to query'), limit: z.number().optional().describe('The maximum number of metrics to return'), limitPerMetric: z.number().optional().describe('The maximum number of metrics to return per metric'), }); // Helper function to convert relative time to Unix timestamp function parseTime(time: string): string { if (time === 'now') { return Math.floor(Date.now() / 1000).toString(); } const relativeMatch = time.match(/^now-(\d+)([smhd])$/); if (relativeMatch) { const value = parseInt(relativeMatch[1]); const unit = relativeMatch[2]; let seconds = 0; switch (unit) { case 's': seconds = value; break; case 'm': seconds = value * 60; break; case 'h': seconds = value * 3600; break; case 'd': seconds = value * 86400; break; } return Math.floor((Date.now() / 1000) - seconds).toString(); } // Assume it's already a Unix timestamp or RFC3339 return time; } // Helper to build Prometheus selector from filters function buildSelector(filters: any[]): string { if (!filters || filters.length === 0) return '{}'; const parts = filters.map(f => { switch (f.type) { case '=': return `${f.name}="${f.value}"`; case '!=': return `${f.name}!="${f.value}"`; case '=~': return `${f.name}=~"${f.value}"`; case '!~': return `${f.name}!~"${f.value}"`; default: return ''; } }).filter(p => p); return `{${parts.join(',')}}`; } export const queryPrometheus: ToolDefinition = { name: 'query_prometheus', description: 'Query Prometheus using a PromQL expression. Supports both instant and range queries.', inputSchema: QueryPrometheusSchema, handler: async (params, context: ToolContext) => { try { const client = new PrometheusClient(context.config.grafanaConfig, params.datasourceUid); let result; if (params.queryType === 'instant') { result = await client.query(params.expr, parseTime(params.startTime)); } else { const start = parseTime(params.startTime); const end = params.endTime ? parseTime(params.endTime) : 'now'; const step = params.stepSeconds ? `${params.stepSeconds}s` : '60s'; result = await client.queryRange(params.expr, start, end, step); } return createToolResult(result); } catch (error: any) { return createErrorResult(error.message); } }, }; export const listPrometheusMetricNames: ToolDefinition = { name: 'list_prometheus_metric_names', description: 'List metric names in a Prometheus datasource. Retrieves all metric names and filters them using regex.', inputSchema: ListPrometheusMetricNamesSchema, handler: async (params, context: ToolContext) => { try { const client = new PrometheusClient(context.config.grafanaConfig, params.datasourceUid); // Get all series to extract metric names const series = await client.getSeries(['__name__=~".+"']); const metricNames = new Set<string>(); for (const s of series) { if (s.__name__) { metricNames.add(s.__name__); } } let names = Array.from(metricNames).sort(); // Apply regex filter if provided if (params.regex) { const regex = new RegExp(params.regex); names = names.filter(name => regex.test(name)); } // Apply pagination if (params.limit || params.page) { const limit = params.limit || 100; const page = params.page || 1; const start = (page - 1) * limit; names = names.slice(start, start + limit); } return createToolResult(names); } catch (error: any) { return createErrorResult(error.message); } }, }; export const listPrometheusLabelNames: ToolDefinition = { name: 'list_prometheus_label_names', description: 'List label names in a Prometheus datasource. Allows filtering by series selectors and time range.', inputSchema: ListPrometheusLabelNamesSchema, handler: async (params, context: ToolContext) => { try { const client = new PrometheusClient(context.config.grafanaConfig, params.datasourceUid); const match = params.matches?.map((m: any) => buildSelector(m.filters)) || []; const labels = await client.getLabelNames( match.length > 0 ? match : undefined, params.startRfc3339, params.endRfc3339 ); const limited = params.limit ? labels.slice(0, params.limit) : labels; return createToolResult(limited); } catch (error: any) { return createErrorResult(error.message); } }, }; export const listPrometheusLabelValues: ToolDefinition = { name: 'list_prometheus_label_values', description: 'Get the values for a specific label name in Prometheus. Allows filtering by series selectors and time range.', inputSchema: ListPrometheusLabelValuesSchema, handler: async (params, context: ToolContext) => { try { const client = new PrometheusClient(context.config.grafanaConfig, params.datasourceUid); const match = params.matches?.map((m: any) => buildSelector(m.filters)) || []; const values = await client.getLabelValues( params.labelName, match.length > 0 ? match : undefined, params.startRfc3339, params.endRfc3339 ); const limited = params.limit ? values.slice(0, params.limit) : values; return createToolResult(limited); } catch (error: any) { return createErrorResult(error.message); } }, }; export const listPrometheusMetricMetadata: ToolDefinition = { name: 'list_prometheus_metric_metadata', description: 'List Prometheus metric metadata. Returns metadata about metrics currently scraped from targets.', inputSchema: ListPrometheusMetricMetadataSchema, handler: async (params, context: ToolContext) => { try { const client = new PrometheusClient(context.config.grafanaConfig, params.datasourceUid); const metadata = await client.getMetricMetadata(params.metric, params.limit); return createToolResult(metadata); } catch (error: any) { return createErrorResult(error.message); } }, }; export function registerPrometheusTools(server: any) { server.registerTool(queryPrometheus); server.registerTool(listPrometheusMetricNames); server.registerTool(listPrometheusLabelNames); server.registerTool(listPrometheusLabelValues); server.registerTool(listPrometheusMetricMetadata); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/levalhq/mcp-grafana'

If you have feedback or need assistance with the MCP directory API, please join our Discord server