grafana-unified-mcp
grafana-unified-mcp
Один MCP-сервер перед множеством экземпляров Grafana. Все инструменты стандартного
MCP-сервера Grafana, плюс один дополнительный аргумент — instance — который указывает,
против какой Grafana его выполнять.
query_prometheus(instance="tenant-a", expr="up", datasourceUid="...")
search_dashboards(instance="tenant-b", query="login latency")Зачем это нужно
Оригинальный grafana/mcp-grafana привязывает
GRAFANA_URL один раз, при запуске процесса. Он читает
X-Grafana-Service-Account-Token для каждого запроса, но URL фиксирован — а
заголовок, который раньше использовался для его переопределения, теперь явно неактивен. Из оригинального
validate_url.go:
Устарело: X-Grafana-URL больше не настраивает клиент Grafana. Это промежуточное ПО временно сохранено для обработки некорректных заголовков.
Таким образом, один процесс mcp-grafana может общаться только с одной Grafana. Десять Grafana
означают десять серверов, десять записей в каждом клиентском конфиге и десять наборов
инструментов с одинаковыми именами, которые модель должна различать.
Этот сервер решает проблему, запуская один дочерний процесс оригинального сервера на каждый экземпляр и
направляя каждый вызов к нужному на основе аргумента instance. Инструменты
обнаруживаются из реального бинарного файла во время выполнения, поэтому вы получаете все, что
предоставляет оригинальный сервер — на данный момент 65 инструментов — без написания кода для каждого инструмента
здесь и без необходимости обновления, когда оригинальный сервер добавляет новые.
Related MCP server: mcphub
Как это работает
┌──────────────────────────────────┐
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 …GrafanaДочерние процессы запускаются при первом использовании, остаются активными, завершаются при простое
(--idle-timeout, по умолчанию 15 мин) и прозрачно перезапускаются, если они умирают.
Недоступная Grafana ухудшает работу только своего собственного экземпляра.
Установка
Две части: оригинальный бинарный файл и этот пакет.
# 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] @ .'Если у вас уже есть бинарный файл, укажите на него с помощью MCP_GRAFANA_BINARY=/path/to/mcp-grafana
или --mcp-grafana-binary.
Настройка
Конечные точки
Именно то, что вы ожидаете — имя экземпляра для переменных окружения оригинального сервера:
{
"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"
}
}Необязательные ключи для каждого экземпляра: GRAFANA_ORG_ID, GRAFANA_USERNAME /
GRAFANA_PASSWORD, description, extra_env, extra_args. Чтобы хранить секреты
вне самого документа, используйте GRAFANA_SERVICE_ACCOUNT_TOKEN_ENV (читается из
окружения этого процесса) или GRAFANA_SERVICE_ACCOUNT_TOKEN_FILE (путь, который читает
дочерний процесс).
Аутентификация
{
"clients": [
{
"name": "claude-routines",
"token_sha256": "3f786850e387550fdab836ed7e6dc881de23001b…",
"instances": ["tenant-a", "tenant-b"],
"scope": "read-only"
},
{
"name": "platform-oncall",
"token_sha256": "…",
"instances": ["*"],
"scope": "read-write"
}
]
}Создайте токен и его хеш:
grafana-unified-mcp --hash-token # generates one
grafana-unified-mcp --hash-token 'my-existing-token'Передайте token клиенту; поместите token_sha256 в документ. Токены
сравниваются по дайджесту с помощью hmac.compare_digest, и каждый клиент проверяется при
каждой попытке, чтобы позиция совпадения не утекала через время.
Для каждого вызывающего применяются два ограничения:
instances— перечислениеinstance, которое видит вызывающий, сужается до его разрешения, и вызов, указывающий экземпляр вне его разрешения, отклоняется с тем же сообщением, что и для несуществующего, поэтому токен не может перечислить то, к чему у него нет доступа.scope— вызывающие сread-onlyдаже не видят изменяющие инструменты. Разделение основано на собственной аннотацииreadOnlyHintоригинального сервера (49 из 65 инструментов сегодня доступны только для чтения), а не на списке, поддерживаемом здесь, поэтому инструменты, добавленные оригинальным сервером, классифицируются без изменения кода. Все, что не аннотировано, считается не доступным только для чтения.
Для дополнительной безопасности добавьте --child-arg=--disable-write, чтобы удалить инструменты записи
на источнике для всех вызывающих.
Запуск без аутентификации
--auth-mode none обслуживает каждого вызывающего, который может достичь порта, только для чтения.
Нет личности для ограничения экземпляров, поэтому все настроенные экземпляры остаются
читаемыми — но ничего не доступно для записи, потому что открытый порт не должен иметь возможности
переписать дашборд или удалить снимок. Это обеспечивается на трех уровнях:
опубликованный каталог опускает все изменяющие инструменты;
проверка авторизации отклоняет их, даже если клиент называет один напрямую;
дочерние процессы запускаются с
--disable-write, поэтому оригинальный сервер также отклоняет их.
Третий уровень — это то, что делает это больше, чем просто фильтр. Оригинальный сервер
заменяет grafana_api_request на отдельную регистрацию только для GET — без параметра
body, method сужен до GET, не-GET отклоняется во время выполнения — поэтому даже
ошибка в уровнях 1 и 2 не может привести к записи.
stdio отличается: локальный вызывающий уже имеет документ с конечными точками и каждый токен в нем, поэтому их ограничение было бы театром. stdio получает полный доступ.
Если вам нужны записи через HTTP, используйте токены Bearer с клиентом read-write
вместо открытого порта.
Откуда берется конфигурация
Любой из этих источников, как для --endpoints, так и для --auth:
Источник | Пример |
Файл |
|
Встроенная переменная окружения |
|
AWS Secrets Manager |
|
AWS SSM Parameter Store |
|
Оба документа перечитываются каждые --config-refresh-seconds (по умолчанию 300). Неудачное
обновление логируется и сохраняет последнее хорошее значение, поэтому временная ошибка AWS или
наполовину записанный файл не могут вывести сервер из строя. Добавление экземпляра не требует
перезапуска; удаление останавливает его дочерний процесс.
Проверьте перед запуском:
grafana-unified-mcp --endpoints … --auth … --check-configЗапуск
# 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-urlимеет значение. SDK применяет защиту от DNS-ребендинга на основе заголовкаHost. За прокси, перенаправляющим публичное имя хоста, этот хост должен быть разрешен, иначе каждый запрос будет отклонен.--public-urlразрешает его (и используется для метаданных ресурсов RFC 9728);--allowed-hostдобавляет больше.
GET /healthz сообщает о состоянии процесса, активных дочерних процессах и состоянии каталога без
обращения к Grafana.
Подключение клиента
.mcp.json, для локального использования stdio:
{
"mcpServers": {
"grafana": {
"command": "/opt/grafana-unified-mcp/.venv/bin/grafana-unified-mcp",
"args": ["--endpoints", "/etc/grafana-unified-mcp/endpoints.json"]
}
}
}Для развернутого сервера — включая процедуры Claude Code и облачные сессии, для которых и существуют токены Bearer:
{
"mcpServers": {
"grafana": {
"type": "http",
"url": "https://grafana-mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer ${GRAFANA_UNIFIED_MCP_TOKEN}"
}
}
}
}Установите GRAFANA_UNIFIED_MCP_TOKEN в окружении, в котором выполняется сессия — для
Claude Code в вебе это переменные окружения, поэтому запланированные
процедуры и облачные сессии подхватывают его без хранения секрета в репозитории.
Дайте процедурам клиент read-only; оставьте read-write для людей.
Развертывание как systemd-сервис
Смотрите deploy/. Вкратце:
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 | jqМодуль запускается как выделенный непривилегированный пользователь с ProtectSystem=strict,
PrivateTmp и NoNewPrivileges. TLS завершается на nginx или ALB перед
сервером — смотрите deploy/nginx.conf.example, который отключает буферизацию ответов (требуется для
потоковой передачи SSE).
Использование
Сначала укажите модели на list_grafana_instances:
list_grafana_instances()
→ { "instances": [ {"name": "tenant-a", "url": "…", "connection": "live"}, … ],
"routing_argument": "instance",
"access": { "client": "claude-routines", "scope": "read-only" } }Затем каждый другой инструмент принимает это имя:
search_dashboards(instance="tenant-a", query="latency")Передайте check_health=true, чтобы также проверить каждую Grafana — медленнее, так как открывает
соединение с каждым экземпляром.
Одна особенность именования
У оригинального grafana_api_request уже есть обязательный параметр с именем
endpoint (путь API). Внедрение аргумента маршрутизации с таким именем
молча затенило бы его, поэтому аргумент маршрутизации по умолчанию называется instance. Если вы
переименуете его с помощью --routing-param endpoint, собственный параметр этого инструмента
автоматически переиздается как api_path и отображается обратно на пути — ни один
инструмент никогда не ломается из-за коллизии, независимо от вашего выбора.
Разработка
uv venv && uv pip install -e '.[dev,aws]'
uv run pytest # unit + integrationИнтеграционные тесты запускают реальный дочерний процесс mcp-grafana против недоступной
Grafana: достаточно, чтобы доказать обнаружение каталога, внедрение и удаление instance,
маршрутизацию и фильтрацию аутентификации, без необходимости в живых учетных данных. Установите
MCP_GRAFANA_BINARY, чтобы указать на бинарный файл, иначе они пропускаются.
План развития
OAuth 2.1 — слой аутентификации уже является интерфейсом, и SDK уже принимает OAuth-провайдера вместе с верификатором токенов. Заполнение
OAuth2Provider.verify_token— это вся работа;auth/oauth.pyдокументирует три шага. Сопоставьте группы IdP с существующими областямиgrafana:read/grafana:write/instance:<name>, и каждая проверка авторизации будет работать без изменений.Fan-out —
instance: "*"для выполнения одного запроса только для чтения по всем экземплярам и объединения результатов. Полезно для "какой из них сигнализирует?"; пока не реализовано, потому что объединение результатов заслуживает собственного проектирования.
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-