grafana-unified-mcp
grafana-unified-mcp
一个位于多个 Grafana 实例前的统一 MCP 服务器。包含标准 Grafana MCP 服务器的所有工具,外加一个额外参数——instance——用于指定针对哪个 Grafana 实例运行。
query_prometheus(instance="appstate", expr="up", datasourceUid="...")
search_dashboards(instance="uoregon", 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 个工具——无需在此处编写每个工具的代码,也无需在上游添加更多工具时进行更新。
工作原理
┌──────────────────────────────────┐
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│
│ appstate │ │ uoregon │ │ … │
└─────┬────┘ └───┬──────┘ └┬─────────┘
▼ ▼ ▼
appstate uoregon …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 指向它。
配置
端点
正是您所期望的形式——实例名称映射到上游环境变量:
{
"appstate": {
"GRAFANA_URL": "https://appstate.uw2.example.cloud/grafana",
"GRAFANA_SERVICE_ACCOUNT_TOKEN": "glsa_…"
},
"uoregon": {
"GRAFANA_URL": "https://uoregon.uw2.example.cloud/grafana",
"GRAFANA_SERVICE_ACCOUNT_TOKEN": "glsa_…",
"description": "University of Oregon 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": ["appstate", "uoregon"],
"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注解(目前 65 个工具中有 49 个是只读的),而不是在此处维护的列表,因此上游添加的工具无需更改代码即可分类。任何未注解的内容都被视为非只读。
为了双重保险,添加 --child-arg=--disable-write 以在源头上为所有调用者剥离写入工具。
在没有认证的情况下运行
--auth-mode none 为每个能够访问端口的调用者提供服务,只读。没有身份可以按实例划分范围,因此所有配置的实例保持可读——但没有任何内容可写,因为开放的端口不应能够重写仪表板或删除快照。这在三个层面强制执行:
发布的目录省略了所有会修改数据的工具;
即使客户端直接命名一个此类工具,授权检查也会拒绝它;
子进程以
--disable-write启动,因此上游也会拒绝它们。
第三层使其不仅仅是一个过滤器。上游将 grafana_api_request 替换为一个单独的仅 GET 注册——没有 body 参数,method 缩小为 GET,非 GET 请求在运行时被拒绝——因此即使第 1 层和第 2 层存在错误,也无法变成写入操作。
stdio 则不同:本地调用者已经持有端点文档及其中的所有令牌,因此限制它们将只是做做样子。stdio 获得完全访问权限。
如果您需要通过 HTTP 进行写入操作,请使用带有 read-write 客户端的 bearer 令牌,而不是开放端口。
配置来源
以下任何来源均可,适用于 --endpoints 和 --auth:
来源 | 示例 |
文件 |
|
内联环境变量 |
|
AWS Secrets Manager |
|
AWS SSM 参数存储 |
|
两个文档都会每隔 --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 会根据Host标头应用 DNS 重新绑定保护。在代理转发公共主机名的情况下,必须允许该主机,否则每个请求都将被拒绝。--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": "appstate", "url": "…", "connection": "live"}, … ],
"routing_argument": "instance",
"access": { "client": "claude-routines", "scope": "read-only" } }然后所有其他工具都使用该名称:
search_dashboards(instance="appstate", 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集成测试针对一个不可及的 Grafana 驱动一个真实的 mcp-grafana 子进程:足以证明目录发现、instance 注入和剥离、路由以及认证过滤,而无需实时凭证。设置 MCP_GRAFANA_BINARY 指向二进制文件,否则它们会跳过。
路线图
OAuth 2.1 —— 认证层已经是一个接口,SDK 已经可以接受 OAuth 提供程序和令牌验证器。填充
OAuth2Provider.verify_token是全部工作;auth/oauth.py记录了三个步骤。将 IdP 组映射到现有的grafana:read/grafana:write/instance:<name>作用域,所有授权检查将保持正常工作。扇出 ——
instance: "*"用于跨所有实例运行一个只读查询并合并结果。对于“哪个正在告警?”很有用;暂时省略,因为结果合并本身值得单独设计。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
An MCP server giving access to Grafana dashboards, data and more.
Remote MCP for GenAI span mapping, provider normalization, dashboard schemas, and receipts.
MCP server for interacting with the Supabase platform
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/robert-sinclair/grafana-unified-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server