grafana-unified-mcp
grafana-unified-mcp
Ein MCP-Server vor vielen Grafana-Instanzen. Jedes Tool des standardmäßigen
Grafana-MCP-Servers, plus ein zusätzliches Argument — instance — das angibt,
gegen welche Grafana-Instanz es ausgeführt werden soll.
query_prometheus(instance="tenant-a", expr="up", datasourceUid="...")
search_dashboards(instance="tenant-b", query="login latency")Warum es das gibt
Das vorgelagerte grafana/mcp-grafana bindet
GRAFANA_URL einmalig, beim Prozessstart. Es liest
X-Grafana-Service-Account-Token pro Anfrage, aber die URL ist festgelegt — und der
Header, der früher zum Überschreiben verwendet wurde, ist jetzt explizit wirkungslos. Aus dem vorgelagerten
validate_url.go:
Deprecated: X-Grafana-URL no longer configures the Grafana client. This middleware is retained temporarily to preserve malformed-header handling.
Ein mcp-grafana-Prozess kann also immer nur mit einer einzigen Grafana-Instanz sprechen. Zehn Grafana-Instanzen
bedeuten zehn Server, zehn Einträge in jeder Client-Konfiguration und zehn Sätze von Tools
mit identischen Namen, die das Modell auseinanderhalten muss.
Dieser Server behebt das, indem er einen vorgelagerten Kindprozess pro Instanz ausführt und
jeden Aufruf basierend auf dem instance-Argument an den richtigen weiterleitet. Tools werden
zur Laufzeit aus der echten Binärdatei entdeckt, sodass Sie erhalten, was auch immer der vorgelagerte Server
bereitstellt — derzeit 65 Tools — ohne pro-Tool-Code hier und nichts, was aktualisiert werden muss,
wenn der vorgelagerte Server weitere hinzufügt.
Related MCP server: mcphub
Wie es funktioniert
┌──────────────────────────────────┐
Claude Code / routines / │ grafana-unified-mcp │
cloud sessions │ │
│ │ ┌────────────────────────────┐ │
│ streamable-HTTP │ │ bearer auth │ │
│ Authorization: Bearer … │ │ → Principal(instances, │ │
├──────────────────────────────►│ │ read-only|read-write) │ │
│ │ └────────────┬───────────────┘ │
│ │ │ │
│ │ ┌────────────▼───────────────┐ │
│ │ │ catalog: inject `instance` │ │
│ │ │ filter by caller's grant │ │
│ │ └────────────┬───────────────┘ │
│ │ │ route on │
│ │ │ instance=… │
│ │ ┌────────────▼───────────────┐ │
│ │ │ child pool (lazy, reaped) │ │
│ │ └──┬──────────┬──────────┬───┘ │
└───────────────────────────────┴─────┼──────────┼──────────┼──────┘
│ stdio │ stdio │ stdio
┌─────▼────┐ ┌───▼──────┐ ┌▼─────────┐
│mcp-grafana│ │mcp-grafana│ │mcp-grafana│
│ tenant-a │ │ tenant-b │ │ … │
└─────┬────┘ └───┬──────┘ └┬─────────┘
▼ ▼ ▼
tenant-a tenant-b …GrafanaKinder starten bei der ersten Verwendung, bleiben warm, werden bei Inaktivität beendet
(--idle-timeout, Standard 15 Min.) und werden transparent neu gestartet, wenn sie sterben.
Eine unerreichbare Grafana-Instanz beeinträchtigt nur ihre eigene Instanz.
Installation
Zwei Teile: die vorgelagerte Binärdatei und dieses Paket.
# 1. the upstream mcp-grafana binary (needs Go 1.26+; GOTOOLCHAIN=auto fetches it)
deploy/install-mcp-grafana.sh /usr/local/bin
# 2. this server
python3 -m venv /opt/grafana-unified-mcp/.venv
/opt/grafana-unified-mcp/.venv/bin/pip install 'grafana-unified-mcp[aws] @ .'Wenn Sie die Binärdatei bereits haben, verweisen Sie mit MCP_GRAFANA_BINARY=/pfad/zu/mcp-grafana
oder --mcp-grafana-binary darauf.
Konfiguration
Endpunkte
Genau die Form, die Sie erwarten würden — Instanzname zu den vorgelagerten Umgebungsvariablen:
{
"tenant-a": {
"GRAFANA_URL": "https://tenant-a.example.cloud/grafana",
"GRAFANA_SERVICE_ACCOUNT_TOKEN": "glsa_…"
},
"tenant-b": {
"GRAFANA_URL": "https://tenant-b.example.cloud/grafana",
"GRAFANA_SERVICE_ACCOUNT_TOKEN": "glsa_…",
"description": "Tenant B production"
}
}Optionale pro-Instanz-Schlüssel: GRAFANA_ORG_ID, GRAFANA_USERNAME /
GRAFANA_PASSWORD, description, extra_env, extra_args. Um Geheimnisse
außerhalb des Dokuments selbst zu halten, verwenden Sie GRAFANA_SERVICE_ACCOUNT_TOKEN_ENV (aus der Umgebung dieses Prozesses gelesen) oder GRAFANA_SERVICE_ACCOUNT_TOKEN_FILE (ein Pfad, den das Kind liest).
Authentifizierung
{
"clients": [
{
"name": "claude-routines",
"token_sha256": "3f786850e387550fdab836ed7e6dc881de23001b…",
"instances": ["tenant-a", "tenant-b"],
"scope": "read-only"
},
{
"name": "platform-oncall",
"token_sha256": "…",
"instances": ["*"],
"scope": "read-write"
}
]
}Erstellen Sie ein Token und seinen Hash:
grafana-unified-mcp --hash-token # generates one
grafana-unified-mcp --hash-token 'my-existing-token'Geben Sie token an den Client; setzen Sie token_sha256 in das Dokument. Tokens werden
durch den Digest unter hmac.compare_digest verglichen, und jeder Client wird bei
jedem Versuch überprüft, sodass die Übereinstimmungsposition nicht durch Timing preisgegeben wird.
Pro Aufrufer werden zwei Dinge erzwungen:
instances— dieinstance-Aufzählung, die ein Aufrufer sieht, wird auf seine Berechtigung eingeschränkt, und ein Aufruf, der eine Instanz außerhalb dieser benennt, wird mit derselben Nachricht abgelehnt wie eine nicht existierende, sodass ein Token nicht aufzählen kann, was es nicht erreichen kann.scope—read-only-Aufrufer sehen mutierende Tools nicht einmal. Die Aufteilung stammt aus der eigenenreadOnlyHint-Annotation des vorgelagerten Servers (49 von 65 Tools sind heute schreibgeschützt), nicht aus einer hier gepflegten Liste, sodass neu hinzugefügte Tools ohne Codeänderung klassifiziert werden. Alles, was nicht annotiert ist, wird als nicht schreibgeschützt behandelt.
Für doppelte Sicherheit fügen Sie --child-arg=--disable-write hinzu, um Schreib-Tools an der
Quelle für jeden Aufrufer zu entfernen.
Ausführung ohne Authentifizierung
--auth-mode none bedient jeden Aufrufer, der den Port erreichen kann, schreibgeschützt.
Es gibt keine Identität, um Instanzen danach abzugrenzen, daher bleiben alle konfigurierten Instanzen
lesbar — aber nichts ist beschreibbar, da ein offener Port kein Dashboard umschreiben
oder einen Snapshot löschen können sollte. Das wird auf drei Ebenen erzwungen:
Der veröffentlichte Katalog lässt jedes mutierende Tool aus;
Die Autorisierungsprüfung verweigert sie, selbst wenn ein Client eines direkt benennt;
Kinder werden mit
--disable-writegestartet, sodass der vorgelagerte Server sie ebenfalls ablehnt.
Die dritte Ebene ist es, die es mehr als einen Filter macht. Der vorgelagerte Server tauscht
grafana_api_request gegen eine separate GET-only-Registrierung aus — kein body-Parameter,
method auf GET eingeschränkt, Nicht-GET zur Laufzeit abgelehnt — sodass selbst ein
Fehler in den Ebenen 1 und 2 nicht zu einem Schreibzugriff werden könnte.
stdio ist anders: Der lokale Aufrufer hat bereits das Endpunkte-Dokument und jedes Token darin, daher wäre eine Einschränkung für ihn Theater. stdio erhält vollen Zugriff.
Wenn Sie Schreibzugriffe über HTTP benötigen, verwenden Sie Bearer-Tokens mit einem read-write-Client
anstelle eines offenen Ports.
Woher die Konfiguration kommt
Jede dieser Quellen, sowohl für --endpoints als auch für --auth:
Quelle | Beispiel |
Datei |
|
Inline-Umgebungsvariable |
|
AWS Secrets Manager |
|
AWS SSM Parameter Store |
|
Beide Dokumente werden alle --config-refresh-seconds (Standard 300) neu gelesen. Ein
fehlgeschlagenes Aktualisieren protokolliert den Fehler und behält den letzten gültigen Wert, sodass ein vorübergehender AWS-Fehler oder eine
halb geschriebene Datei den Server nicht lahmlegen kann. Das Hinzufügen einer Instanz benötigt keinen Neustart;
das Entfernen einer Instanz stoppt ihr Kind.
Vor dem Start validieren:
grafana-unified-mcp --endpoints … --auth … --check-configAusführen
# local, over stdio (no auth — the local caller already holds the config)
grafana-unified-mcp --endpoints ./examples/endpoints.json
# deployed, over streamable-HTTP behind a reverse proxy
grafana-unified-mcp \
--transport streamable-http \
--address 127.0.0.1:8900 \
--endpoints aws-secrets:prod/grafana/endpoints?region=us-west-2 \
--auth aws-secrets:prod/grafana/mcp-auth?region=us-west-2 \
--public-url https://grafana-mcp.example.com
--public-urlist wichtig. Das SDK wendet DNS-Rebinding-Schutz basierend auf demHost-Header an. Hinter einem Proxy, der einen öffentlichen Hostnamen weiterleitet, muss dieser Host erlaubt sein, oder jede Anfrage wird abgelehnt.--public-urlerlaubt ihn (und wird für RFC 9728-Ressourcenmetadaten verwendet);--allowed-hostfügt weitere hinzu.
GET /healthz meldet die Prozessgesundheit, aktive Kinder und den Katalogstatus, ohne
Grafana zu berühren.
Einen Client verbinden
.mcp.json, für die lokale stdio-Nutzung:
{
"mcpServers": {
"grafana": {
"command": "/opt/grafana-unified-mcp/.venv/bin/grafana-unified-mcp",
"args": ["--endpoints", "/etc/grafana-unified-mcp/endpoints.json"]
}
}
}Für den bereitgestellten Server — einschließlich Claude Code-Routinen und Cloud-Sitzungen, wofür die Bearer-Tokens existieren:
{
"mcpServers": {
"grafana": {
"type": "http",
"url": "https://grafana-mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer ${GRAFANA_UNIFIED_MCP_TOKEN}"
}
}
}
}Setzen Sie GRAFANA_UNIFIED_MCP_TOKEN in der Umgebung, in der die Sitzung läuft — für
Claude Code im Web sind das die Umgebungsvariablen, sodass geplante
Routinen und Cloud-Sitzungen es aufnehmen, ohne dass das Geheimnis im Repository lebt.
Geben Sie Routinen einen read-only-Client; behalten Sie read-write für Menschen.
Als systemd-Dienst bereitstellen
Siehe deploy/. Kurz gesagt:
sudo deploy/install.sh # user, dirs, venv, unit file
sudo systemctl edit grafana-unified-mcp # set the source URIs / region
sudo systemctl enable --now grafana-unified-mcp
curl -s localhost:8900/healthz | jqDie Unit läuft als dedizierter, unprivilegierter Benutzer mit ProtectSystem=strict,
PrivateTmp und NoNewPrivileges. TLS terminiert an nginx oder einem ALB davor —
siehe deploy/nginx.conf.example, das die Antwortpufferung deaktiviert (erforderlich für
SSE-Streaming).
Verwendung
Weisen Sie das Modell zuerst auf list_grafana_instances:
list_grafana_instances()
→ { "instances": [ {"name": "tenant-a", "url": "…", "connection": "live"}, … ],
"routing_argument": "instance",
"access": { "client": "claude-routines", "scope": "read-only" } }Dann akzeptiert jedes andere Tool diesen Namen:
search_dashboards(instance="tenant-a", query="latency")Übergeben Sie check_health=true, um auch jede Grafana-Instanz zu prüfen — langsamer, da es eine Verbindung zu
jeder Instanz öffnet.
Eine Namensbesonderheit
Das vorgelagerte grafana_api_request hat bereits einen erforderlichen Parameter namens
endpoint (der API-Pfad). Das Injizieren eines Routing-Arguments mit diesem Namen würde
es stillschweigend überschatten, weshalb das Routing-Argument standardmäßig instance ist.
Wenn Sie es mit --routing-param endpoint umbenennen, wird der eigene Parameter dieses Tools
automatisch als api_path neu veröffentlicht und auf dem Rückweg zurückgemappt — kein
Tool wird jemals durch die Kollision beschädigt, egal was Sie wählen.
Entwicklung
uv venv && uv pip install -e '.[dev,aws]'
uv run pytest # unit + integrationDie Integrationstests treiben ein echtes mcp-grafana-Kind gegen eine unerreichbare
Grafana-Instanz an: genug, um die Katalogerkennung, das Injizieren und Entfernen von instance,
das Routing und die Authentifizierungsfilterung zu beweisen, ohne dass Live-Anmeldeinformationen erforderlich sind. Setzen Sie
MCP_GRAFANA_BINARY, um auf die Binärdatei zu verweisen, oder sie werden übersprungen.
Fahrplan
OAuth 2.1 — die Authentifizierungsschicht ist bereits ein Interface, und das SDK akzeptiert bereits einen OAuth-Anbieter neben dem Token-Verifizierer. Das Ausfüllen von
OAuth2Provider.verify_tokenist die gesamte Aufgabe;auth/oauth.pydokumentiert die drei Schritte. Ordnen Sie IdP-Gruppen den bestehendengrafana:read/grafana:write/instance:<name>-Bereichen zu, und jede Autorisierungsprüfung funktioniert unverändert weiter.Fan-out —
instance: "*", um eine einzige schreibgeschützte Abfrage über alle Instanzen hinweg auszuführen und die Ergebnisse zusammenzuführen. Nützlich für "Welche von diesen alarmiert?"; vorerst weggelassen, weil die Zusammenführung von Ergebnissen ein eigenes Design verdient.
This server cannot be deployed
Maintenance
Related MCP Connectors
An MCP server giving access to Grafana dashboards, data and more.
Stateless MCP gateway and OTel span-streaming bridge for hosted MCP servers.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
MCP-first control plane for ProAgentStore agents and private instances.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProxy that aggregates multiple MCP servers and presents them as a unified interface, allowing clients to access resources from multiple servers transparently.4-
- AlicenseNot gradedqualityAmaintenanceA unified hub for centrally managing and dynamically orchestrating multiple MCP servers/APIs into separate endpoints with flexible routing strategies.846 npm2,488Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables MCP-compatible agents to interact with Grafana instances for searching, creating, and updating dashboards, exploring logs via Loki, querying datasources, managing alerts, incidents, and on-call shifts, and accessing observability data.8Apache 2.0
- FlicenseNot gradedqualityDmaintenanceEnables interaction with multiple Jenkins instances from a single MCP server, using header-based authentication for multi-tenancy.1-