{"id":"73a31523-2e4b-48e2-86d2-630094526a92","slug":"hakkiburakalks-pmm-mcp","kind":"mcp","name":"pmm-mcp","summary":"MCP server for Percona Monitoring and Management (PMM 2 & 3): PromQL metrics, Query Analytics, alerts and dashboards for Claude and other MCP clients. Read-only by default.","owner":"hakkiburakalks","repoUrl":"https://github.com/hakkiburakalks/pmm-mcp","license":"MIT","licenseTier":"permissive","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-10-25T03:00:49.000Z","addedAt":"2026-10-29T00:52:16.771Z","logoUrl":"https://avatars.githubusercontent.com/u/69517719?u=e5176ce115717091eefe1526da62b6adea93549d&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","databases","data-analytics"],"tags":["claude","mongodb","monitoring","mysql","percona","pmm","postgresql","promql"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-29T00:55:28.883Z","sample":false,"url":"https://conduits.dev/tools/hakkiburakalks-pmm-mcp","language":"Python","transports":["stdio"],"install":{"pypi":"pmm-mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# PMM MCP Server\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)\n[![MCP](https://img.shields.io/badge/MCP-stdio-green.svg)](https://modelcontextprotocol.io/)\n\nA community-maintained [Model Context Protocol](https://modelcontextprotocol.io/) server for [Percona Monitoring and Management](https://www.percona.com/software/database-tools/percona-monitoring-and-management) (PMM 2.x and 3.x). It gives Claude and other MCP clients access to MySQL, MongoDB and PostgreSQL metrics (PromQL), Query Analytics (slowest queries, query details, ClickHouse SQL), alerts and PMM dashboards. It is **read-only by default**; a few write tools (silences, annotations) can be enabled explicitly, see [Read-only or write?](#read-only-or-write).\n\n> This is not an official Percona or Anthropic product.\n\n## ✨ Features\n\n- **Read-only by default**: works with a Grafana *Viewer* token. Optional write tools (silences, annotations) are off unless `PMM_ALLOW_WRITE=1` and always dry-run first\n- **Metrics**: instant and range PromQL queries plus label discovery\n- **Query Analytics (QAN)**: top queries, per-service load, single-query details and distribution over time\n- **Safe SQL**: free-form `SELECT` against QAN's ClickHouse, strictly validated and capped\n- **TLS verified by default**, with custom CA bundle support\n- **Cross-platform**: Linux, macOS and Windows, with one-command installers\n\n## 🚀 Installation\n\n**Requirements**\n\n- Python 3.11+ and [`uv`](https://docs.astral.sh/uv/)\n- Network access to your PMM server (VPN if needed)\n- A PMM Grafana token: *Administration → Service accounts → role Viewer → Add token*\n\n**With `uvx` (no clone needed)**\n\n```bash\nuvx pmm-mcp\n```\n\nPut `PMM_URL` and `PMM_TOKEN` in the environment (or an env file, see [Configuration](#-configuration)). To register it with Claude Code:\n\n```bash\nclaude mcp add pmm -- uvx --env-file ~/.config/mcp/pmm.env pmm-mcp\n```\n\n**Linux / macOS (installer)**\n\n```bash\ngit clone https://github.com/hakkiburakalks/pmm-mcp && cd pmm-mcp && ./install.sh\n```\n\n**Windows (PowerShell installer)**\n\n```powershell\ngit clone https://github.com/hakkiburakalks/pmm-mcp; cd pmm-mcp; powershell -ExecutionPolicy Bypass -File .\\install.ps1\n```\n\nThe installer asks for your PMM URL and token, stores them in `~/.config/mcp/pmm.env` (owner-only access) and registers the server with Claude Code under the name `pmm`. If `claude` is not installed, it prints the JSON snippet for Claude Desktop instead.\n\n## 🎯 Quick Start\n\n### Claude Desktop / any MCP client\n\nThe server is a plain stdio MCP server. Run it with:\n\n```bash\nuv run --env-file /path/to/pmm.env --directory /path/to/pmm-mcp pmm-mcp\n```\n\n(`server.py` still works as an alias for existing setups) and register that command in your client, for example in `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"pmm\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"run\", \"--quiet\",\n        \"--env-file\", \"C:\\\\Users\\\\you\\\\.config\\\\mcp\\\\pmm.env\",\n        \"--directory\", \"C:\\\\path\\\\to\\\\pmm-mcp\",\n        \"server.py\"\n      ]\n    }\n  }\n}\n```\n\nUse the full path to `uv` as `command` if your client does not inherit your `PATH`.\n\nOn macOS the config file is `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"pmm\": {\n      \"command\": \"/opt/homebrew/bin/uv\",\n      \"args\": [\n        \"run\", \"--quiet\",\n        \"--env-file\", \"/Users/you/.config/mcp/pmm.env\",\n        \"--directory\", \"/Users/you/pmm-mcp\",\n        \"server.py\"\n      ]\n    }\n  }\n}\n```\n\nDesktop apps don't inherit your shell `PATH`, so give the full path to `uv` (check with `which uv`; typically `/opt/homebrew/bin/uv` on Apple Silicon, `/usr/local/bin/uv` on Intel, `~/.local/bin/uv` expanded to an absolute path if installed via the curl script). `install.sh` prints this snippet for you.\n\n### Claude Code\n\n```bash\nclaude mcp add pmm -- uv run --quiet --env-file ~/.config/mcp/pmm.env --directory /path/to/pmm-mcp server.py\n```\n\n## 🔧 Configuration\n\nCreate `~/.config/mcp/pmm.env` (the installers do this for you) with your own values; the ones below are fake:\n\n```bash\nPMM_URL=https://pmm.example.com\nPMM_TOKEN=glsa_EXAMPLEexampleEXAMPLEexample1234567890_abcdef12\nPMM_VERIFY=1\n# PMM_CA_BUNDLE=/etc/ssl/certs/my-company-ca.pem\n# PMM_ALLOW_WRITE=1   # off by default, see \"Read-only or write?\" below\n```\n\nSet these environment variables (see [`pmm.env.example`](pmm.env.example) for a commented template):\n\n| Variable | Required | Description |\n|---|---|---|\n| `PMM_URL` | ✅ | PMM server URL |\n| `PMM_TOKEN` | ✅ | Grafana service-account token. Viewer is enough for all read tools; the write tools need Editor or Admin |\n| `PMM_ALLOW_WRITE` | | Default `0`. Set `1` to register the opt-in write tools (see below) |\n| `PMM_VERIFY` | | TLS verification, default `1`. Use `0` only for self-signed certificates |\n| `PMM_CA_BUNDLE` | | Path to a custom CA bundle (preferred over disabling verification) |\n| `PMM_TIMEOUT` | | HTTP timeout in seconds, default `60` |\n| `PMM_METRICS_UID` | | Grafana metrics datasource UID, only if it differs from PMM's default |\n| `PMM_CLICKHOUSE_UID` | | Grafana ClickHouse datasource UID, only if it differs from PMM's default |\n\n## 🛠️ Available Tools\n\n### Inventory\n\n| Tool | Description |\n|---|---|\n| `pmm_list_services` | Monitored databases and their status |\n| `pmm_whoami` | Identity and role behind the configured token |\n\n### Alerting\n\n| Tool | Description |\n|---|---|\n| `pmm_list_alerts` | Currently firing alerts, optionally filtered by label |\n| `pmm_alert_rules` | Alert rules and their state |\n| `pmm_list_silences` | Active silences |\n\n### Query Analytics\n\n| Tool | Description |\n|---|---|\n| `pmm_qan_by_service` | How much query time each database spends |\n| `pmm_qan_top_queries` | Heaviest queries, filterable by service, database, node, user, client host or text |\n| `pmm_qan_query_detail` | A single query's example and its distribution over time |\n| `pmm_qan_schema` | Actual columns of `pmm.metrics` on your PMM version |\n| `pmm_qan_sql` | Free-form, validated `SELECT` against QAN ClickHouse |\n\n### Metrics\n\n| Tool | Description |\n|---|---|\n| `pmm_promql` | Instant PromQL query |\n| `pmm_promql_range` | Range PromQL query (point and series caps) |\n| `pmm_metric_names` | Find metric names by substring |\n| `pmm_metric_metadata` | Help text and type of a metric |\n| `pmm_label_names` / `pmm_label_values` | Discover labels and their values |\n\n### Dashboards and context\n\n| Tool | Description |\n|---|---|\n| `pmm_search_dashboards` | Find PMM dashboards |\n| `pmm_dashboard_queries` | The PromQL/SQL behind each dashboard panel |\n| `pmm_annotations` | Deploy/maintenance markers to correlate with changes |\n| `pmm_deeplink` | PMM URL for QAN or a dashboard |\n\n### Read-only or write?\n\n**Read-only is the default and the recommended setup.** With just `PMM_URL` and a Viewer `PMM_TOKEN` the model can look at metrics, queries, alerts and dashboards but cannot change anything. To let it silence an alert during maintenance or add a deploy marker, you need all three of: an Editor/Admin token, `PMM_ALLOW_WRITE=1`, and your approval of each call. Creating alert rules is not supported.\n\n### Write tools (opt-in)\n\nRegistered only when `PMM_ALLOW_WRITE=1` **and** the token has Editor/Admin rights. Without the flag the model does not even see them.\n\n| Tool | Description |\n|---|---|\n| `pmm_create_silence` | Silence alerts matching exact labels, max 72 h, comment required |\n| `pmm_delete_silence` | Expire a silence |\n| `pmm_add_annotation` | Add a deploy/maintenance marker |\n\nEvery write tool is a **dry run by default**: it returns exactly what it would do and changes nothing until it is called again with `confirm=true`. The tools are also annotated as non-read-only, so MCP clients ask for approval. The model is instructed to confirm with you first, so keep your client's per-tool approval on for these.\n\n## 💬 Example Queries\n\n**Overview**\n- \"Which databases does PMM monitor, and are they all healthy?\"\n- \"Which service spends the most time running queries?\"\n\n**Query Analytics**\n- \"Show the 10 slowest queries on the production MySQL service\"\n- \"Give me the details and time distribution of this query ID\"\n\n**Metrics**\n- \"What is the connection count on `db-prod-1` over the last 6 hours?\"\n- \"Compare InnoDB buffer pool hit ratio across all MySQL nodes\"\n\n## 🔒 Security\n\n- Use a **Viewer** token unless you need the write tools; by default the server never writes to PMM. Writes require `PMM_ALLOW_WRITE=1`, an Editor/Admin token and `confirm=true` on each call.\n- TLS certificates are verified by default.\n- `pmm_qan_sql` accepts only a single plain `SELECT`/`WITH`. Comments, quoted identifiers, `SETTINGS`/`FORMAT`/`INTO`, table functions (`url`, `file`, `remote`, ...) and system tables are rejected. Row counts and time windows are capped.\n- Tool inputs (labels, service types, limits, time windows) are validated before being used in a query.\n- The token lives in `~/.config/mcp/pmm.env`, created with owner-only permissions. Never commit it.\n\nSee [SECURITY.md](SECURITY.md) to report a vulnerability.\n\n## 🐛 Troubleshooting\n\n| Symptom | What to check |\n|---|---|\n| Server not listed in the client | Use the full path to `uv` as `command`; GUI clients often don't inherit `PATH` |\n| `401` / `403` errors | Token is invalid, expired, or not a Grafana service-account token |\n| TLS / certificate errors | Set `PMM_CA_BUNDLE` to your CA file. As a last resort, `PMM_VERIFY=0` |\n| Timeouts | Check VPN/network access, or raise `PMM_TIMEOUT` |\n| Datasource not found | Set `PMM_METRICS_UID` / `PMM_CLICKHOUSE_UID` if your PMM uses non-default UIDs |\n\n## 🔍 Debugging\n\nLaunch the server in the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):\n\n```bash\nnpx @modelcontextprotocol/inspector uv run --env-file /path/to/pmm.env --directory /path/to/pmm-mcp server.py\n```\n\n## 🧪 Development\n\n```bash\nuv sync --extra dev\nuv run pytest\n```\n\n## 📝 Notes\n\n- Tested against PMM 2 (Grafana 9.2) and PMM 3 (Grafana 12.4) with the default datasource UIDs, using Viewer and Admin tokens, and QAN on MySQL, PostgreSQL (`pg_stat_statements`) and MongoDB (profiler). Not covered: alerts in firing state, ProxySQL/HAProxy.\n- PMM's `/v1` API rejects Viewer tokens (observed on PMM 2; PMM 3 was not checked for this), so everything goes through the Grafana datasource proxy.\n- The same queryid can appear in several services; group by service when aggregating.\n- In MySQL QAN the `max_ms`/`p99` fields are 0 (a perfschema limitation).\n\n## 📄 License\n\n[MIT](LICENSE): free for anyone to use, modify and distribute, including commercially.\n"}