mcp-prometheus
# @mgcrea/mcp-prometheus
A [Model Context Protocol](https://modelcontextprotocol.io) server for the
[Prometheus HTTP API](https://prometheus.io/docs/prometheus/latest/querying/api/): PromQL
queries, metric and label discovery, alerts, rules, scrape health and cardinality, shaped to fit
a context window.
The server is **read-only by default**. The admin tools are not merely refused when writes are
off — they are never registered, so an agent cannot call them at all.
## Why another one
Prometheus answers are built for dashboards, not for models. Measured on a
kube-prometheus-stack install (~800k head series, 268 targets):
| Endpoint | Raw | What this server returns |
| ---------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- |
| `/api/v1/targets` | 1.5 MB | totals, unhealthy pools, and the unhealthy targets with `lastError` (~2 KB) |
| `/api/v1/query` (`up`, 124 series) | 9 labels repeated per series | shared labels once in `common_labels`, numeric values |
| `/api/v1/query_range` | `[ts, "0.005732144858390525"]` per point | one array per series on the step grid, rounded, plus min/max/avg/last |
| `/api/v1/rules` | 200 KB | flat list, filterable by health/state/type/name |
| `/api/v1/metadata` | 420 KB | searched by name _and_ help text, cached |
It also does the things a model gets wrong on its own: relative times (`now-1h`), a round
step picked for ~60 points, label lookups limited to series that exist _now_ rather than over
the whole retention, and PromQL errors returned verbatim with what to do about them.
## Tools
| Tool | What it does | Writes |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------- | ------- |
| `prometheus_auth_status` | Which Prometheus answered (version, hostname, external labels, retention, admin API), or what to configure | no |
| `prometheus_query` | Instant PromQL query | no |
| `prometheus_query_range` | Range query on a step grid, with per-series stats; `view=stats`, `sort=max` to survey | no |
| `prometheus_list_metrics` | Find metrics by name or help text, with type and help | no |
| `prometheus_describe_metric` | Type, help, series count, and every label with its cardinality and examples | no |
| `prometheus_list_labels` | Label names, optionally on a selector | no |
| `prometheus_list_label_values` | Values of one label, optionally on a selector | no |
| `prometheus_list_alerts` | Firing and pending alerts, most severe first, with counts | no |
| `prometheus_list_rules` | Alerting and recording rules; `health=err` finds broken ones | no |
| `prometheus_list_targets` | Scrape health: totals, unhealthy pools and targets with their errors | no |
| `prometheus_get_tsdb_status` | Head-block cardinality: top metrics, labels, label pairs | no |
| `prometheus_request` | GET any `/api/v1/*` endpoint, cut at `max_chars` | no |
| `prometheus_delete_series` | Delete series (counts first, refuses past `max_series`, needs `confirm`) | **yes** |
| `prometheus_clean_tombstones` | Purge deleted data from disk (needs `confirm`) | **yes** |
| `prometheus_reload_config` | `POST /-/reload` | **yes** |
There is deliberately no `/-/quit` and no TSDB snapshot tool (a snapshot copies the whole TSDB
onto a volume sized for one).
## Configure
| Variable | Required | Description |
| --------------------------------------------- | -------- | -------------------------------------------------------------------------------------- |
| `PROMETHEUS_URL` | yes | Base URL, including any path prefix (`https://host/prometheus`). No credentials in it. |
| `PROMETHEUS_USERNAME` / `PROMETHEUS_PASSWORD` | no | Basic auth for a proxy in front of Prometheus. |
| `PROMETHEUS_TOKEN` | no | Bearer token instead of basic auth. |
| `PROMETHEUS_HEADERS` | no | Extra headers as JSON, e.g. `{"X-Scope-OrgID":"tenant-1"}` for Mimir. |
| `PROMETHEUS_ALLOW_WRITES` | no | `1` to register the admin tools. |
| `PROMETHEUS_TIMEOUT` | no | Query timeout in seconds (default 30). |
| `PROMETHEUS_MAX_RETRIES` | no | Retries for 429 and proxy 5xx (default 2). Query timeouts are never retried. |
| `PROMETHEUS_UI_LINKS` | no | `0` to drop the Prometheus UI link from query results. |
A self-signed certificate needs `NODE_EXTRA_CA_CERTS=/path/to/ca.pem`.
A configuration mistake — a malformed URL, a token _and_ basic auth, invalid
`PROMETHEUS_HEADERS` — never stops the server from starting. It starts with only
`prometheus_auth_status`, which says what to fix.
### Which Prometheus is this?
kube-prometheus-stack names everything identically on every cluster, so a `.mcp.json` copied
between projects answers every query with plausible numbers from the wrong one. The server
prints the instance it reached on startup (stderr), and `prometheus_auth_status` returns the
URL, version, hostname and external labels. If you run several clusters, set a distinguishing
external label (`prometheus.prometheusSpec.externalLabels.cluster` in kube-prometheus-stack).
## Quick start
```bash
pnpm install
pnpm build
```
Then copy `.mcp.json.example` to `.mcp.json` and fill it in.
### Inspect the tools
```bash
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| PROMETHEUS_URL=http://localhost:9090 node dist/cli.js 2>/dev/null | jq -r '.result.tools[]?.name'
```
## Develop
```bash
pnpm dev # tsdown --watch
pnpm test # vitest
pnpm typecheck
pnpm lint
pnpm format
```
### Publish
```bash
pnpm dlx release-it # bump, commit, tag
git push --follow-tags # CI publishes to npm + GHCR from the tag
```
## License
MIT
TDQS
Scored across 12 tools
Each tool targets a distinct resource or action: instant vs. range queries are explicitly contrasted, and list_labels / list_label_values / describe_metric are differentiated with cross-references (e.g. 'describe_metric is more useful' for one metric's labels). The only potential overlap, prometheus_request, is explicitly framed as a read-only escape hatch with guidance to prefer the named tools.
All names share the prometheus_ snake_case prefix and are mostly verb_noun (query, query_range, list_metrics, describe_metric, get_tsdb_status). Minor deviations are auth_status (noun phrase) and request (bare verb), but the set remains predictable and readable.
Twelve tools is well within a healthy range and each earns its place: query, discovery, alerts/rules, target health, TSDB cardinality, and a raw escape hatch. No tool appears redundant with another.
The read-only Prometheus surface is broadly covered: queries, metric/label discovery, alerts, rules, targets, TSDB status, and auth/version checks. A dedicated series-lookup tool (/api/v1/series) and explicit config/flags endpoints are absent, but the request escape hatch lets agents work around these gaps.