Skip to main content
Glama
common-schemas.ts6.82 kB
import { z } from 'zod'; /** * Common reusable Zod schemas for better type safety and consistency */ // Basic identifier schemas export const UidSchema = z.string().min(1, 'UID cannot be empty'); export const IdSchema = z.number().int().positive(); // Branded types for better type safety export type DashboardUID = string & { readonly __brand: 'DashboardUID' }; export type DataSourceUID = string & { readonly __brand: 'DataSourceUID' }; export type AlertRuleUID = string & { readonly __brand: 'AlertRuleUID' }; export type FolderUID = string & { readonly __brand: 'FolderUID' }; // Schema factories for branded types export const createBrandedUidSchema = <T extends string>(brand: T) => z.string().min(1, `${brand} UID cannot be empty`) as unknown as z.ZodType<string & { readonly __brand: T }>; export const DashboardUIDSchema = createBrandedUidSchema('DashboardUID'); export const DataSourceUIDSchema = createBrandedUidSchema('DataSourceUID'); export const AlertRuleUIDSchema = createBrandedUidSchema('AlertRuleUID'); export const FolderUIDSchema = createBrandedUidSchema('FolderUID'); // Time range schemas export const TimeRangeSchema = z.object({ from: z.string().describe("Start time (RFC3339 or relative like '5m')"), to: z.string().describe("End time (RFC3339 or relative like 'now')"), }); export const OptionalTimeRangeSchema = z.object({ start: z.string().describe('Start time (RFC3339 or Unix timestamp)').optional(), end: z.string().describe('End time (RFC3339 or Unix timestamp)').optional(), }); // Pagination schemas export const PaginationSchema = z.object({ page: z.number().int().positive().default(1).describe('Page number (1-based)'), limit: z.number().int().positive().max(1000).default(100).describe('Items per page (max 1000)'), }); export const PerPageSchema = z.object({ page: z.number().int().positive().default(1), perpage: z.number().int().positive().max(1000).default(1000), }); // Common field schemas export const TagsSchema = z.array(z.string()).optional().describe('Tags for filtering'); export const FolderSchema = z.string().optional().describe('Folder name or UID'); // Query schemas export const PromQLQuerySchema = z.string().min(1, 'PromQL query cannot be empty'); export const LogQLQuerySchema = z.string().min(1, 'LogQL query cannot be empty'); // Extended query schemas with validation export const PrometheusQueryParamsSchema = z.object({ query: PromQLQuerySchema, datasourceUid: DataSourceUIDSchema, start: z.string().optional(), end: z.string().optional(), step: z.string().optional().describe('Query resolution step width'), }); export const LokiQueryParamsSchema = z.object({ query: LogQLQuerySchema, datasourceUid: DataSourceUIDSchema, limit: z.number().int().positive().max(5000).default(1000), direction: z.enum(['forward', 'backward']).default('backward'), ...OptionalTimeRangeSchema.shape, }); // Search schemas export const SearchSchema = z.object({ query: z.string().optional().describe('Search query string'), tags: TagsSchema, folder: FolderSchema, ...PaginationSchema.shape, }); // Labels and series schemas export const LabelMatchersSchema = z.array(z.string()).describe( 'Label matchers (e.g., ["{job=\\"prometheus\\"}"])', ); export const LabelsSchema = z.record(z.string()).describe( 'Labels as key-value pairs (e.g., {"job": "nginx", "level": "error"})', ); // Advanced query building schemas export const MetricFiltersSchema = z.record(z.string()).optional().describe( 'Label filters as key-value pairs', ); export const PrometheusBuilderSchema = z.object({ metric: z.string().min(1, 'Base metric name required'), filters: MetricFiltersSchema, function: z.string().optional().describe('Prometheus function (rate, sum, avg, etc.)'), timeWindow: z.string().optional().describe('Time window for functions (e.g., "5m")'), }); export const LokiBuilderSchema = z.object({ labels: LabelsSchema, filter: z.string().optional().describe('Log line filter pattern'), operation: z.enum(['rate', 'count_over_time', 'sum', 'avg', 'min', 'max']).optional(), timeWindow: z.string().optional().describe('Time window for operations (e.g., "5m")'), filterType: z.enum(['contains', 'regex', 'exact']).default('contains').optional(), }); // Configuration schemas export const DatasourceTestSchema = z.object({ uid: DataSourceUIDSchema, }); export const DashboardUpdateSchema = z.object({ dashboard: z.record(z.any()).describe('Full dashboard JSON configuration'), folderId: z.number().int().optional().describe('Folder ID (0 for General)'), folderUid: FolderUIDSchema.optional(), overwrite: z.boolean().default(false).describe('Overwrite existing dashboard'), message: z.string().optional().describe('Commit message'), }); // Alert schemas export const AlertRuleSchema = z.object({ uid: AlertRuleUIDSchema.optional(), title: z.string().min(1, 'Alert rule title required'), condition: z.string().min(1, 'Alert condition required'), data: z.array(z.record(z.any())), intervalSeconds: z.number().int().positive().default(60), noDataState: z.enum(['NoData', 'Alerting', 'OK']).default('NoData'), execErrState: z.enum(['Alerting', 'OK']).default('Alerting'), for: z.string().default('5m').describe('Duration before alerting'), annotations: z.record(z.string()).optional(), labels: z.record(z.string()).optional(), }); // Validation helpers export const createValidationError = (field: string, message: string) => ({ field, message, code: 'VALIDATION_ERROR' as const, }); export const validateTimeRange = (start?: string, end?: string) => { const errors: ReturnType<typeof createValidationError>[] = []; if (start && end) { const startTime = new Date(start); const endTime = new Date(end); if (isNaN(startTime.getTime())) { errors.push(createValidationError('start', 'Invalid start time format')); } if (isNaN(endTime.getTime())) { errors.push(createValidationError('end', 'Invalid end time format')); } if (startTime > endTime) { errors.push(createValidationError('timeRange', 'Start time must be before end time')); } } return errors; }; // Export commonly used combined schemas export const CommonSchemas = { uid: UidSchema, id: IdSchema, dashboardUid: DashboardUIDSchema, datasourceUid: DataSourceUIDSchema, timeRange: TimeRangeSchema, optionalTimeRange: OptionalTimeRangeSchema, pagination: PaginationSchema, perPage: PerPageSchema, tags: TagsSchema, folder: FolderSchema, search: SearchSchema, prometheusQuery: PrometheusQueryParamsSchema, lokiQuery: LokiQueryParamsSchema, prometheusBuilder: PrometheusBuilderSchema, lokiBuilder: LokiBuilderSchema, dashboardUpdate: DashboardUpdateSchema, alertRule: AlertRuleSchema, } as const;

Latest Blog Posts

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/quanticsoul4772/grafana-mcp'

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