Skip to main content
Glama
robert-sinclair

grafana-unified-mcp

grafana-unified-mcp

複数の Grafana インスタンスを束ねる単一の MCP サーバー。標準の Grafana MCP サーバーにあるすべてのツールに加え、どの Grafana に対して実行するかを指定する instance という追加の引数が1つあります。

query_prometheus(instance="appstate", expr="up", datasourceUid="...")
search_dashboards(instance="uoregon", query="login latency")

存在理由

上流の grafana/mcp-grafanaGRAFANA_URLプロセス起動時に1回だけバインドします。X-Grafana-Service-Account-Token はリクエストごとに読み取りますが、URL は固定されており — かつてこれを上書きしていたヘッダーは、現在では明示的に無効化されています。上流の validate_url.go より:

非推奨: X-Grafana-URL は Grafana クライアントを設定しなくなりました。このミドルウェアは、不正なヘッダー処理を維持するために一時的に保持されています。

つまり、1つの mcp-grafana プロセスは常に1つの Grafana としか通信できません。10個の Grafana があれば、10個のサーバー、すべてのクライアント設定に10個のエントリ、そしてモデルが区別するための同一名を持つ10セットのツールが必要になります。

このサーバーは、インスタンスごとに1つの上流子プロセスを実行し、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 は、そのインスタンスのみに影響を与えます。

インストール

上流のバイナリとこのパッケージの2つが必要です。

# 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_IDGRAFANA_USERNAME / GRAFANA_PASSWORDdescriptionextra_envextra_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 でダイジェストを比較され、すべてのクライアントが毎回チェックされるため、タイミングによってマッチ位置が漏洩することはありません。

呼び出し元ごとに2つのことが強制されます:

  • instances — 呼び出し元が見る instance 列挙型はその許可範囲に絞り込まれ、許可範囲外のインスタンスを指定した呼び出しは、存在しないインスタンスと同じメッセージで拒否されるため、トークンは到達不能なインスタンスを列挙できません。

  • scoperead-only の呼び出し元は、変更を伴うツールを決して見ることはできません。この分割は、ここで管理されるリストではなく、上流自身の readOnlyHint アノテーション(現在65個のツールのうち49個が読み取り専用)に基づいており、上流でツールが追加されてもコード変更なしで分類されます。アノテーションがないものは、読み取り専用ではないものとして扱われます。

念には念を入れて、--child-arg=--disable-write を追加すると、すべての呼び出し元に対して、ソースで書き込みツールを削除できます。

認証なしでの実行

--auth-mode none は、ポートに到達できるすべての呼び出し元を提供しますが、読み取り専用です。インスタンスをスコープするための ID がないため、設定されたすべてのインスタンスは読み取り可能のままですが、何も書き込みできません。これは、開かれたポートでダッシュボードを書き換えたり、スナップショットを削除したりできないようにするためです。これは3つのレイヤーで強制されます:

  1. 公開されたカタログは、変更を伴うすべてのツールを省略します。

  2. 認証チェックは、クライアントが直接ツールを指定した場合でも拒否します。

  3. 子プロセスは --disable-write で起動されるため、上流もそれらを拒否します。

3番目のレイヤーが、これを単なるフィルター以上のものにしています。上流は grafana_api_request を別の GET 専用の登録に置き換えます — body パラメータなし、methodGET に限定され、非 GET は実行時に拒否されるため — レイヤー1や2にバグがあっても書き込みにはつながらないようになっています。

stdio は異なります:ローカルの呼び出し元はすでにエンドポイントドキュメントとその中のすべてのトークンを保持しているため、それらを制限することは茶番になります。stdio はフルアクセスを取得します。

HTTP 経由で書き込みが必要な場合は、開かれたポートではなく、read-write クライアントのベアラートークンを使用してください。

設定の取得元

--endpoints--auth の両方で、以下のいずれかを使用できます:

ソース

ファイル

/etc/grafana-unified-mcp/endpoints.json

インライン環境変数

env:GRAFANA_ENDPOINTS_JSON

AWS Secrets Manager

aws-secrets:prod/grafana/endpoints?region=us-west-2

AWS SSM パラメータストア

aws-ssm:/prod/grafana/endpoints?region=us-west-2

両方のドキュメントは、--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 にアクセスすることなく、プロセスヘルス、実行中の子プロセス、カタログ状態を報告します。

クライアントの接続

ローカルの stdio 使用のための .mcp.json

{
  "mcpServers": {
    "grafana": {
      "command": "/opt/grafana-unified-mcp/.venv/bin/grafana-unified-mcp",
      "args": ["--endpoints", "/etc/grafana-unified-mcp/endpoints.json"]
    }
  }
}

デプロイされたサーバー用 — Claude Code ルーチンやクラウドセッションを含みます。これはベアラートークンが存在する理由です:

{
  "mcpServers": {
    "grafana": {
      "type": "http",
      "url": "https://grafana-mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${GRAFANA_UNIFIED_MCP_TOKEN}"
      }
    }
  }
}

GRAFANA_UNIFIED_MCP_TOKEN をセッションが実行される環境に設定します — Claude Code on the web の場合、それは環境の変数であるため、スケジュールされたルーチンやクラウドセッションは、リポジトリに機密情報を保存することなく、それを取得します。ルーチンには 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=strictPrivateTmpNoNewPrivileges を使用します。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 もプローブします — すべてのインスタンスに接続を開くため、遅くなります。

命名に関する1つの注意点

上流の 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 に3つの手順が文書化されています。IdP グループを既存の grafana:read / grafana:write / instance:<name> スコープにマッピングすれば、すべての認証チェックは変更なしで機能し続けます。

  • ファンアウトinstance: "*" を使用して、すべてのインスタンスに対して1つの読み取り専用クエリを実行し、結果をマージします。「このうちどれがアラート中か?」に便利です。結果のマージには独自の設計が必要なため、今のところは省略されています。

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all MCP Connectors

Latest Blog Posts

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