Skip to main content
Glama
server.ts4.16 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { PrometheusClient } from "../prometheus/client"; import { logger } from "../logging/logging"; import { type ToolAny, tools } from "./tools"; import packageJSON from "../../package.json"; const ServerConfigSchema = z .object({ prometheusUrl: z .string() .url("must be a valid url") .default("http://localhost:9090"), enableDiscoveryTools: z .boolean() .default(true) .describe("enable prometheus discovery tools"), enableInfoTools: z.boolean().default(true).describe("enable prometheus info tools"), enableQueryTools: z .boolean() .default(true) .describe("enable prometheus query tools"), }) .refine( (config) => config.enableQueryTools || config.enableDiscoveryTools || config.enableInfoTools, { message: "at least one tool category must be enabled (enableQueryTools, enableDiscoveryTools, or enableInfoTools)", }, ); /** * Maps tool capabilities to their corresponding configuration flags. * Used to filter tools based on enabled capabilities. */ const capabilityMap = { discovery: "enableDiscoveryTools", info: "enableInfoTools", query: "enableQueryTools", } as const; type ServerConfig = z.infer<typeof ServerConfigSchema>; export class Server extends McpServer { private readonly prometheusClient: PrometheusClient; constructor(config: ServerConfig) { const validatedConfig = ServerConfigSchema.parse(config); super({ name: packageJSON.name, version: packageJSON.version, }); this.prometheusClient = new PrometheusClient(validatedConfig.prometheusUrl); // Filter tools based on enabled capabilities const prometheusTools: ToolAny[] = tools.filter( (tool) => validatedConfig[capabilityMap[tool.capability]] ?? false, ); prometheusTools.forEach((tool) => this._registerTool(tool)); } /** * Registers a tool with the MCP server. * * Configures the tool with appropriate metadata, input schema validation, * and error handling. All tool executions are logged and wrapped in * try-catch blocks for robust error handling. * * @param tool - The tool to register * @private */ private _registerTool(tool: ToolAny) { this.registerTool( tool.name, { title: tool.title, description: tool.description, inputSchema: tool.inputSchema.shape, annotations: { title: tool.title, readOnlyHint: tool.type === "readonly", destructiveHint: tool.type === "destructive", openWorldHint: false, }, }, // This tool is already typed, but the type system is not able to infer the type of the args // eslint-disable-next-line @typescript-eslint/no-explicit-any async (args: any) => { logger.info(`executing tool: ${tool.name}`); try { const result = await tool.handle(this.prometheusClient, args); logger.info(`tool ${tool.name} executed successfully`); return { isError: false, content: [{ type: "text" as const, text: JSON.stringify(result) }], }; } catch (error) { logger.error(`tool ${tool.name} execution failed`); return { isError: true, content: [ { type: "text" as const, text: error instanceof Error ? error.message : String(error), }, ], }; } }, ); } } export const createServer = () => { const config: ServerConfig = { prometheusUrl: process.env.PROMETHEUS_URL || "http://localhost:9090", enableQueryTools: parseBoolean(process.env.ENABLE_QUERY_TOOLS), enableDiscoveryTools: parseBoolean(process.env.ENABLE_DISCOVERY_TOOLS), enableInfoTools: parseBoolean(process.env.ENABLE_INFO_TOOLS), }; return new Server(config); }; const parseBoolean = (value: string | undefined): boolean => { if (value === undefined) return true; return value.toLowerCase().trim() === "true"; };

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/idanfishman/prometheus-mcp'

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