UNPKG

openclaw-grafana-lens

Version:

OpenClaw plugin that gives AI agents full Grafana access — 18 composable tools for PromQL/LogQL/TraceQL queries, dashboard creation, alerting, SRE investigation, security monitoring, data collection pipeline management via Grafana Alloy (29 recipes), and

475 lines (474 loc) 14.7 kB
/** * Grafana HTTP client — self-contained, no external dependencies. * * Wraps Grafana's REST API for all operations grafana-lens needs: * - Dashboards: create, search, get, render, snapshot * - Queries: PromQL instant/range via datasource proxy * - Alerting: create/list/delete alert rules (Unified Alerting) * - Annotations: create/query event markers * - Datasources: list, discover metrics/labels * - Folders: create/list for organization */ export type GrafanaClientOptions = { url: string; apiKey: string; orgId?: number; }; export type DashboardCreateRequest = { dashboard: Record<string, unknown>; folderUid?: string; message?: string; overwrite?: boolean; }; export type DashboardCreateResponse = { id: number; uid: string; url: string; status: string; version: number; }; export type DashboardSearchResult = { id: number; uid: string; title: string; url: string; type: string; tags: string[]; folderUid?: string; folderTitle?: string; folderUrl?: string; }; export type SnapshotCreateResponse = { key: string; deleteKey: string; url: string; deleteUrl: string; id: number; }; export type DatasourceListItem = { id: number; uid: string; name: string; type: string; url: string; isDefault: boolean; access: string; }; export type PrometheusInstantResult = { status: string; data: { resultType: string; result: Array<{ metric: Record<string, string>; value: [number, string]; }>; }; /** Non-fatal warnings from Prometheus (e.g., rate() applied to gauge). */ infos?: string[]; }; export type PrometheusRangeResult = { status: string; data: { resultType: string; result: Array<{ metric: Record<string, string>; values: Array<[number, string]>; }>; }; /** Non-fatal warnings from Prometheus (e.g., rate() applied to gauge). */ infos?: string[]; }; export type PrometheusLabelValuesResult = { status: string; data: string[]; }; export type MetricMetadataItem = { type: string; help: string; unit: string; }; export type PrometheusMetadataResult = { status: string; data: Record<string, MetricMetadataItem[]>; }; export type LokiStreamEntry = { stream: Record<string, string>; values: Array<[string, string]>; }; export type LokiQueryResult = { status: string; data: { resultType: "streams" | "matrix" | "vector"; result: LokiStreamEntry[] | Array<{ metric: Record<string, string>; values?: Array<[number, string]>; value?: [number, string]; }>; stats?: Record<string, unknown>; }; }; export type TempoAttribute = { key: string; value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean; arrayValue?: { values: Array<{ stringValue?: string; }>; }; }; }; export type TempoSearchTrace = { traceID: string; rootServiceName: string; rootTraceName: string; startTimeUnixNano: string; durationMs: number; spanSets?: Array<{ spans: Array<{ spanID: string; startTimeUnixNano: string; durationNanos: string; attributes?: TempoAttribute[]; }>; matched: number; }>; }; export type TempoSearchResult = { traces: TempoSearchTrace[]; }; export type TempoSpan = { traceId: string; spanId: string; parentSpanId?: string; operationName?: string; name?: string; startTimeUnixNano: string; endTimeUnixNano: string; status?: { code?: number | string; message?: string; }; attributes?: TempoAttribute[]; kind?: number | string; }; export type TempoScopeSpans = { scope?: { name?: string; version?: string; }; spans: TempoSpan[]; }; export type TempoResourceSpans = { resource?: { attributes?: TempoAttribute[]; }; scopeSpans: TempoScopeSpans[]; }; export type TempoTraceResult = { /** OTLP JSON format (some Tempo versions) */ resourceSpans?: TempoResourceSpans[]; /** Protobuf-JSON format (Tempo v2 default — uses base64 IDs, string kind/status) */ batches?: TempoResourceSpans[]; }; export type AnnotationCreateRequest = { dashboardUID?: string; panelId?: number; time: number; timeEnd?: number; tags?: string[]; text: string; }; export type AnnotationCreateResponse = { id: number; message: string; }; export type AnnotationQueryParams = { from?: number; to?: number; dashboardUID?: string; panelId?: number; tags?: string[]; limit?: number; }; export type Annotation = { id: number; dashboardUID: string; panelId: number; time: number; timeEnd: number; tags: string[]; text: string; created: number; updated: number; }; export type AlertQuery = { refId: string; datasourceUid: string; model: Record<string, unknown>; relativeTimeRange?: { from: number; to: number; }; queryType?: string; }; export type AlertRuleCreateRequest = { title: string; folderUID: string; ruleGroup: string; condition: string; data: AlertQuery[]; for?: string; noDataState?: "NoData" | "Alerting" | "OK"; execErrState?: "Alerting" | "OK" | "Error"; labels?: Record<string, string>; annotations?: Record<string, string>; }; export type AlertRule = { uid: string; title: string; folderUID: string; ruleGroup: string; condition: string; data: AlertQuery[]; for: string; noDataState: string; execErrState: string; labels: Record<string, string>; annotations: Record<string, string>; updated: string; provenance: string; }; /** Evaluation state for a single alert rule from Grafana's Prometheus-compatible endpoint. */ export type AlertRuleState = { uid: string; state: "inactive" | "firing" | "pending" | "nodata" | "error"; health: "ok" | "nodata" | "error" | "unknown"; lastEvaluation: string; evaluationTime: number; isPaused: boolean; }; export type FolderCreateRequest = { title: string; uid?: string; }; export type Folder = { id: number; uid: string; title: string; url: string; }; export type ContactPoint = { uid: string; name: string; type: string; settings: Record<string, unknown>; disableResolveMessage: boolean; }; export type ContactPointCreateRequest = { name: string; type: string; settings: Record<string, unknown>; disableResolveMessage?: boolean; }; export type NotificationPolicyTree = { receiver: string; group_by?: string[]; routes?: NotificationPolicyRoute[]; }; export type NotificationPolicyRoute = { receiver?: string; matchers?: Array<{ name: string; type: string; value: string; }>; continue?: boolean; group_by?: string[]; routes?: NotificationPolicyRoute[]; }; export declare class GrafanaClient { private baseUrl; private headers; private url; constructor(opts: GrafanaClientOptions); /** Public getter for the instance URL (used by GrafanaClientRegistry). */ getUrl(): string; private fetchWithTimeout; /** * Create or update a dashboard. * Maps to POST /api/dashboards/db — the same endpoint mcp-grafana's * update_dashboard tool calls under the hood. */ createDashboard(req: DashboardCreateRequest): Promise<DashboardCreateResponse>; /** * Search for dashboards by query string with optional filters. */ searchDashboards(query: string, opts?: { tags?: string[]; starred?: boolean; sort?: string; limit?: number; }): Promise<DashboardSearchResult[]>; /** * Get a dashboard by UID. */ getDashboard(uid: string): Promise<Record<string, unknown>>; /** * Render a panel as PNG image. * Requires the Grafana Image Renderer plugin to be installed. * * Uses GET /render/d/{uid}?viewPanel={panelId}&kiosk=true * (verified from mcp-grafana tools/rendering.go) */ renderPanel(dashboardUid: string, panelId: number, opts?: { width?: number; height?: number; from?: string; to?: string; theme?: "light" | "dark"; scale?: number; }): Promise<ArrayBuffer>; /** * Create a dashboard snapshot. * Snapshots freeze the dashboard state at a point in time and provide * a shareable URL that works without authentication. */ createSnapshot(dashboard: Record<string, unknown>, opts?: { name?: string; expires?: number; }): Promise<SnapshotCreateResponse>; /** Health check — validates the API key works. */ healthCheck(): Promise<boolean>; /** Returns the full URL for a dashboard by UID. */ dashboardUrl(uid: string): string; /** List all configured datasources. */ listDatasources(): Promise<DatasourceListItem[]>; /** Run a PromQL instant query against a Prometheus datasource. */ queryPrometheus(dsUid: string, expr: string, time?: string): Promise<PrometheusInstantResult>; /** Run a PromQL range query against a Prometheus datasource. */ queryPrometheusRange(dsUid: string, expr: string, start: string, end: string, step: string): Promise<PrometheusRangeResult>; /** List available metric names from a Prometheus datasource. */ listMetricNames(dsUid: string, opts?: { match?: string; }): Promise<string[]>; /** List values for a specific label from a Prometheus datasource. */ listLabelValues(dsUid: string, label: string): Promise<string[]>; /** Get metric metadata (type, help, unit) from a Prometheus datasource. */ getMetricMetadata(dsUid: string, opts?: { limit?: number; metric?: string; }): Promise<Record<string, MetricMetadataItem[]>>; /** Run a LogQL instant query against a Loki datasource. */ queryLoki(dsUid: string, expr: string, opts?: { time?: string; limit?: number; direction?: string; }): Promise<LokiQueryResult>; /** Run a LogQL range query against a Loki datasource. */ queryLokiRange(dsUid: string, expr: string, start: string, end: string, opts?: { step?: string; limit?: number; direction?: string; }): Promise<LokiQueryResult>; /** Search traces via TraceQL or basic query parameters. */ searchTraces(dsUid: string, query: string, opts?: { start?: string; end?: string; limit?: number; minDuration?: string; maxDuration?: string; spss?: number; }): Promise<TempoSearchResult>; /** Get a full trace by trace ID. */ getTrace(dsUid: string, traceId: string): Promise<TempoTraceResult>; /** Create an annotation on a dashboard or globally. */ createAnnotation(req: AnnotationCreateRequest): Promise<AnnotationCreateResponse>; /** Query annotations with optional filters. */ getAnnotations(params: AnnotationQueryParams): Promise<Annotation[]>; /** * Create an alert rule via Grafana's Unified Alerting provisioning API. * Sends X-Disable-Provenance so agent-created rules remain editable in UI. */ createAlertRule(req: AlertRuleCreateRequest): Promise<AlertRule>; /** List all alert rules. */ listAlertRules(): Promise<AlertRule[]>; /** Delete an alert rule by UID. */ deleteAlertRule(uid: string): Promise<void>; /** * Fetch evaluation state for all alert rules via Grafana's Prometheus-compatible endpoint. * Returns a map of rule UID → state info for efficient lookup. */ getAlertRuleStates(): Promise<Map<string, AlertRuleState>>; /** Create a folder for organizing dashboards and alert rules. */ createFolder(req: FolderCreateRequest): Promise<Folder>; /** List folders. */ listFolders(): Promise<Folder[]>; /** List configured alert contact points. */ listContactPoints(): Promise<ContactPoint[]>; /** Create a contact point (webhook, email, etc.). */ createContactPoint(req: ContactPointCreateRequest): Promise<ContactPoint>; /** Update an existing contact point by UID. */ updateContactPoint(uid: string, req: ContactPointCreateRequest): Promise<void>; /** Delete a contact point by UID. */ deleteContactPoint(uid: string): Promise<void>; /** Get the notification policy tree. */ getNotificationPolicies(): Promise<NotificationPolicyTree>; /** Update the full notification policy tree. */ updateNotificationPolicies(tree: NotificationPolicyTree): Promise<void>; /** Delete a dashboard by UID. */ deleteDashboard(uid: string): Promise<{ title: string; }>; /** Create a silence for matching alerts. */ createSilence(matchers: Array<{ name: string; value: string; isRegex: boolean; }>, duration: string, comment: string, createdBy?: string): Promise<{ silenceID: string; }>; /** List all silences. */ listSilences(): Promise<Array<{ id: string; status: { state: string; }; matchers: Array<{ name: string; value: string; isRegex: boolean; }>; comment: string; createdBy: string; endsAt: string; }>>; /** Delete (expire) a silence by ID. */ deleteSilence(silenceId: string): Promise<void>; /** Classify render-specific errors with actionable messages. */ private classifyRenderError; /** Classify general API errors with actionable messages. */ private classifyError; } /** * Convert Grafana date math to Unix nanoseconds (for Loki). * Accepted: "now", "now-1h", "now-30m", "now-7d", "now-2w", * RFC3339 ("2026-01-15T00:00:00Z"), Unix seconds, Unix nanoseconds. * Throws on unrecognized formats with recovery guidance. */ export declare function parseDateMathToNs(time: string): string; /** * Convert Grafana date math to Unix seconds (for Prometheus). * Same accepted formats as parseDateMathToNs(). */ export declare function parseDateMathToSeconds(time: string): string; /** * Convert Grafana date math to epoch milliseconds (for Annotations API). * Same accepted formats as parseDateMathToNs(). */ export declare function parseDateMathToMs(time: string): number; /** Escape special regex characters in user input for safe use in Prometheus match[] selectors. */ export declare function escapeRegex(s: string): string;