├── .gitignore
├── LICENSE
├── README.md
├── demo.png
├── package-lock.json
├── package.json
├── src
└── index.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | *.log
4 | .env*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2024 LogLM
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mcp-server-prometheus
2 |
3 | MCP server for interacting with Prometheus metrics and data.
4 |
5 | This is a TypeScript-based MCP server that implements a Prometheus API interface. It provides a bridge between Claude and your Prometheus server through the Model Context Protocol (MCP).
6 |
7 |
8 |
9 | ## Demo
10 |
11 | 
12 |
13 | ## Features
14 |
15 | ### Resources
16 |
17 | - List and access Prometheus metric schema
18 | - Each metric resource provides:
19 | - Metric name and description
20 | - Detailed metadata from Prometheus
21 | - Statistical information (count, min, max)
22 | - JSON mime type for structured data access
23 |
24 | ### Current Capabilities
25 |
26 | - List all available Prometheus metrics with descriptions
27 | - Read detailed metric information including:
28 | - Metadata and help text
29 | - Current statistical data (count, min, max values)
30 | - Basic authentication support for secured Prometheus instances
31 |
32 | ## Configuration
33 |
34 | The server requires the following environment variable:
35 |
36 | - `PROMETHEUS_URL`: The base URL of your Prometheus instance
37 |
38 | Optional authentication configuration:
39 |
40 | - `PROMETHEUS_USERNAME`: Username for basic auth (if required)
41 | - `PROMETHEUS_PASSWORD`: Password for basic auth (if required)
42 |
43 | ## Development
44 |
45 | Install dependencies:
46 |
47 | ```bash
48 | npm install
49 | ```
50 |
51 | Build the server:
52 |
53 | ```bash
54 | npm run build
55 | ```
56 |
57 | For development with auto-rebuild:
58 |
59 | ```bash
60 | npm run watch
61 | ```
62 |
63 | ## Installation
64 |
65 | To use with Claude Desktop, add the server config:
66 |
67 | On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
68 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
69 |
70 | ```json
71 | {
72 | "mcpServers": {
73 | "mcp-server-prometheus": {
74 | "command": "/path/to/mcp-server-prometheus/build/index.js",
75 | "env": {
76 | "PROMETHEUS_URL": "http://your-prometheus-instance:9090"
77 | }
78 | }
79 | }
80 | }
81 | ```
82 |
83 | ### Debugging
84 |
85 | Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
86 |
87 | ```bash
88 | npm run inspector
89 | ```
90 |
91 | The Inspector will provide a URL to access debugging tools in your browser.
92 |
93 | ## API Structure
94 |
95 | The server exposes Prometheus metrics through the following URI structure:
96 |
97 | - Base URI: `http://your-prometheus-instance:9090`
98 | - Metric URIs: `http://your-prometheus-instance:9090/metrics/{metric_name}`
99 |
100 | Each metric resource returns JSON data containing:
101 |
102 | - Metric name
103 | - Metadata (help text, type)
104 | - Current statistics (count, min, max)
105 |
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loglmhq/mcp-server-prometheus/6350a33d5236117c4e6346baf4fafbf6e452087d/demo.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@loglm/mcp-server-prometheus",
3 | "version": "0.1.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "@loglm/mcp-server-prometheus",
9 | "version": "0.1.0",
10 | "dependencies": {
11 | "@modelcontextprotocol/sdk": "0.6.0"
12 | },
13 | "bin": {
14 | "mcp-server-prometheus": "build/index.js"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.11.24",
18 | "typescript": "^5.3.3"
19 | }
20 | },
21 | "node_modules/@modelcontextprotocol/sdk": {
22 | "version": "0.6.0",
23 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz",
24 | "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==",
25 | "license": "MIT",
26 | "dependencies": {
27 | "content-type": "^1.0.5",
28 | "raw-body": "^3.0.0",
29 | "zod": "^3.23.8"
30 | }
31 | },
32 | "node_modules/@types/node": {
33 | "version": "20.17.9",
34 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz",
35 | "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==",
36 | "dev": true,
37 | "license": "MIT",
38 | "dependencies": {
39 | "undici-types": "~6.19.2"
40 | }
41 | },
42 | "node_modules/bytes": {
43 | "version": "3.1.2",
44 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
45 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
46 | "license": "MIT",
47 | "engines": {
48 | "node": ">= 0.8"
49 | }
50 | },
51 | "node_modules/content-type": {
52 | "version": "1.0.5",
53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
54 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
55 | "license": "MIT",
56 | "engines": {
57 | "node": ">= 0.6"
58 | }
59 | },
60 | "node_modules/depd": {
61 | "version": "2.0.0",
62 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
63 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
64 | "license": "MIT",
65 | "engines": {
66 | "node": ">= 0.8"
67 | }
68 | },
69 | "node_modules/http-errors": {
70 | "version": "2.0.0",
71 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
72 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
73 | "license": "MIT",
74 | "dependencies": {
75 | "depd": "2.0.0",
76 | "inherits": "2.0.4",
77 | "setprototypeof": "1.2.0",
78 | "statuses": "2.0.1",
79 | "toidentifier": "1.0.1"
80 | },
81 | "engines": {
82 | "node": ">= 0.8"
83 | }
84 | },
85 | "node_modules/iconv-lite": {
86 | "version": "0.6.3",
87 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
88 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
89 | "license": "MIT",
90 | "dependencies": {
91 | "safer-buffer": ">= 2.1.2 < 3.0.0"
92 | },
93 | "engines": {
94 | "node": ">=0.10.0"
95 | }
96 | },
97 | "node_modules/inherits": {
98 | "version": "2.0.4",
99 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
100 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
101 | "license": "ISC"
102 | },
103 | "node_modules/raw-body": {
104 | "version": "3.0.0",
105 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
106 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
107 | "license": "MIT",
108 | "dependencies": {
109 | "bytes": "3.1.2",
110 | "http-errors": "2.0.0",
111 | "iconv-lite": "0.6.3",
112 | "unpipe": "1.0.0"
113 | },
114 | "engines": {
115 | "node": ">= 0.8"
116 | }
117 | },
118 | "node_modules/safer-buffer": {
119 | "version": "2.1.2",
120 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
121 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
122 | "license": "MIT"
123 | },
124 | "node_modules/setprototypeof": {
125 | "version": "1.2.0",
126 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
127 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
128 | "license": "ISC"
129 | },
130 | "node_modules/statuses": {
131 | "version": "2.0.1",
132 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
133 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
134 | "license": "MIT",
135 | "engines": {
136 | "node": ">= 0.8"
137 | }
138 | },
139 | "node_modules/toidentifier": {
140 | "version": "1.0.1",
141 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
142 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
143 | "license": "MIT",
144 | "engines": {
145 | "node": ">=0.6"
146 | }
147 | },
148 | "node_modules/typescript": {
149 | "version": "5.7.2",
150 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
151 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
152 | "dev": true,
153 | "license": "Apache-2.0",
154 | "bin": {
155 | "tsc": "bin/tsc",
156 | "tsserver": "bin/tsserver"
157 | },
158 | "engines": {
159 | "node": ">=14.17"
160 | }
161 | },
162 | "node_modules/undici-types": {
163 | "version": "6.19.8",
164 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
165 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
166 | "dev": true,
167 | "license": "MIT"
168 | },
169 | "node_modules/unpipe": {
170 | "version": "1.0.0",
171 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
172 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
173 | "license": "MIT",
174 | "engines": {
175 | "node": ">= 0.8"
176 | }
177 | },
178 | "node_modules/zod": {
179 | "version": "3.23.8",
180 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
181 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
182 | "license": "MIT",
183 | "funding": {
184 | "url": "https://github.com/sponsors/colinhacks"
185 | }
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@loglm/mcp-server-prometheus",
3 | "version": "0.1.0",
4 | "description": "MCP server for interacting with Prometheus",
5 | "private": true,
6 | "type": "module",
7 | "bin": {
8 | "mcp-server-prometheus": "./build/index.js"
9 | },
10 | "files": [
11 | "build"
12 | ],
13 | "scripts": {
14 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 | "prepare": "npm run build",
16 | "watch": "tsc --watch",
17 | "inspector": "npx @modelcontextprotocol/inspector node build/index.js"
18 | },
19 | "dependencies": {
20 | "@modelcontextprotocol/sdk": "0.6.0"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^20.11.24",
24 | "typescript": "^5.3.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5 | import {
6 | ListResourcesRequestSchema,
7 | ListToolsRequestSchema,
8 | ReadResourceRequestSchema,
9 | } from "@modelcontextprotocol/sdk/types.js";
10 |
11 | export type PrometheusServerParams = {
12 | prometheusUrl: string;
13 | prometheusUsername?: string;
14 | prometheusPassword?: string;
15 | };
16 |
17 | /**
18 | * Creates and configures a Prometheus MCP server
19 | */
20 | export function createPrometheusServer(params: PrometheusServerParams) {
21 | const server = new Server(
22 | {
23 | name: "@loglm/mcp-server-prometheus",
24 | version: "0.1.0",
25 | },
26 | {
27 | capabilities: {
28 | resources: {},
29 | tools: {},
30 | },
31 | }
32 | );
33 |
34 | if (!params.prometheusUrl) {
35 | throw new Error("prometheusUrl is required");
36 | }
37 |
38 | const prometheusUrl = new URL(params.prometheusUrl);
39 | const resourceBaseUrl = new URL(prometheusUrl);
40 |
41 | // Helper functions for fetching Prometheus data
42 | async function fetchMetricMetadata(): Promise {
43 | const url = new URL("/api/v1/metadata", prometheusUrl);
44 | const response = await fetch(url.toString(), {
45 | headers: {
46 | Accept: "application/json",
47 | ...(params.prometheusUsername && params.prometheusPassword
48 | ? {
49 | Authorization: `Basic ${btoa(
50 | `${params.prometheusUsername}:${params.prometheusPassword}`
51 | )}`,
52 | }
53 | : {}),
54 | },
55 | });
56 |
57 | if (!response.ok) {
58 | throw new Error(`Prometheus API error: ${response.statusText}`);
59 | }
60 |
61 | return response.json();
62 | }
63 |
64 | async function fetchMetricDetails(metricName: string): Promise {
65 | const metadataUrl = new URL("/api/v1/metadata", prometheusUrl);
66 | const metadataResponse = await fetch(
67 | `${metadataUrl}?metric=${metricName}`,
68 | {
69 | headers: {
70 | Accept: "application/json",
71 | ...(params.prometheusUsername && params.prometheusPassword
72 | ? {
73 | Authorization: `Basic ${btoa(
74 | `${params.prometheusUsername}:${params.prometheusPassword}`
75 | )}`,
76 | }
77 | : {}),
78 | },
79 | }
80 | );
81 |
82 | if (!metadataResponse.ok) {
83 | throw new Error(`Prometheus API error: ${metadataResponse.statusText}`);
84 | }
85 |
86 | const queryUrl = new URL("/api/v1/query", prometheusUrl);
87 | const queries = [
88 | `count(${metricName})`,
89 | `min(${metricName})`,
90 | `max(${metricName})`,
91 | ];
92 |
93 | const queryPromises = queries.map((query) =>
94 | fetch(`${queryUrl}?query=${encodeURIComponent(query)}`, {
95 | headers: {
96 | Accept: "application/json",
97 | ...(params.prometheusUsername && params.prometheusPassword
98 | ? {
99 | Authorization: `Basic ${btoa(
100 | `${params.prometheusUsername}:${params.prometheusPassword}`
101 | )}`,
102 | }
103 | : {}),
104 | },
105 | }).then((res) => {
106 | if (!res.ok) {
107 | throw new Error(`Prometheus API error: ${res.statusText}`);
108 | }
109 | return res.json();
110 | })
111 | );
112 |
113 | const [metadata, countData, minData, maxData] = await Promise.all([
114 | metadataResponse.json(),
115 | ...queryPromises,
116 | ]);
117 |
118 | return {
119 | metadata,
120 | statistics: {
121 | count: countData.data.result[0]?.value[1] || 0,
122 | min: minData.data.result[0]?.value[1] || 0,
123 | max: maxData.data.result[0]?.value[1] || 0,
124 | },
125 | };
126 | }
127 |
128 | // Handler implementations
129 | server.setRequestHandler(ListResourcesRequestSchema, async () => {
130 | const data = await fetchMetricMetadata();
131 |
132 | if (data.status !== "success") {
133 | throw new Error("Failed to fetch metrics metadata");
134 | }
135 |
136 | return {
137 | resources: Object.entries(data.data).map(
138 | ([metricName, metadata]: [string, any]) => ({
139 | uri: new URL(`metrics/${metricName}`, resourceBaseUrl).href,
140 | mimeType: "application/json",
141 | name: metricName,
142 | description: metadata[0]?.help || "No description available",
143 | })
144 | ),
145 | };
146 | });
147 |
148 | server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
149 | const resourceUrl = new URL(request.params.uri);
150 | const pathComponents = resourceUrl.pathname.split("/");
151 | const metricName = pathComponents[pathComponents.length - 1];
152 |
153 | const metricData = await fetchMetricDetails(metricName);
154 |
155 | const content = {
156 | name: metricName,
157 | metadata: metricData.metadata.data[metricName]?.[0] || {},
158 | statistics: metricData.statistics,
159 | };
160 |
161 | return {
162 | contents: [
163 | {
164 | uri: request.params.uri,
165 | mimeType: "application/json",
166 | text: JSON.stringify(content, null, 2),
167 | },
168 | ],
169 | };
170 | });
171 |
172 | server.setRequestHandler(ListToolsRequestSchema, async () => {
173 | return {
174 | tools: [],
175 | };
176 | });
177 |
178 | return server;
179 | }
180 |
181 | /**
182 | * Main function to run the server
183 | */
184 | async function main() {
185 | const PROMETHEUS_URL = process.env.PROMETHEUS_URL;
186 |
187 | if (!PROMETHEUS_URL) {
188 | console.error("PROMETHEUS_URL environment variable is not set");
189 | process.exit(1);
190 | }
191 |
192 | const PROMETHEUS_USERNAME = process.env.PROMETHEUS_USERNAME;
193 | const PROMETHEUS_PASSWORD = process.env.PROMETHEUS_PASSWORD;
194 |
195 | const transport = new StdioServerTransport();
196 | const server = createPrometheusServer({
197 | prometheusUrl: PROMETHEUS_URL,
198 | prometheusUsername: PROMETHEUS_USERNAME,
199 | prometheusPassword: PROMETHEUS_PASSWORD,
200 | });
201 |
202 | await server.connect(transport);
203 | console.error("Prometheus MCP Server running on stdio");
204 | }
205 |
206 | // Run the server
207 | main().catch((error) => {
208 | console.error("Server error:", error);
209 | process.exit(1);
210 | });
211 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "Node16",
6 | "outDir": "./build",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules"]
15 | }
16 |
--------------------------------------------------------------------------------