diff --git a/README.md b/README.md index f26d4b9..9f2fba7 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,36 @@ home-manager build --flake . home-manager generations ``` +## Codex と Claude Code から Grafana MCP を使う + +`mcp-grafana` は Nix パッケージとしてインストールされ、Codex と Claude Code が同じ +起動設定を使います。起動時に Doppler から必要な環境変数を受け取り、読み取り専用 +モードで Grafana に接続します。あらかじめ Doppler CLI へログインしてください。 + +1. Grafana に最小限の RBAC 権限を持つ Service Account を作成し、トークンを発行します。 +2. Doppler の project `mcp`、config `prd` に `GRAFANA_URL` と `GRAFANA_SERVICE_ACCOUNT_TOKEN` の 2 変数だけを登録します。 +3. Home Manager の設定を反映して、各クライアントから MCP サーバーを確認します。 + +トークンの値を dotfiles に保存したり、Codex の親プロセスに設定したりしません。 + +```bash +home-manager switch --flake . +codex mcp list +claude mcp list +``` + +Codex と Claude Code を再起動した後、それぞれの `/mcp` でも接続状態を確認できます。 + +Grafana Cloud ではスタックの URL を `GRAFANA_URL` に設定します。ホスト上の Grafana +へ接続する場合は、`http://localhost:3000` のような URL を指定できます。 + +`--disable-write` は誤操作を防ぐための補助策であり、Grafana 側の RBAC に代わるものでは +ありません。MCP サーバーは必須扱いにしていないため、Doppler や Grafana への接続に +失敗しても Codex や Claude Code 全体の起動は止まりません。 + +`mcp-grafana` を更新するときは、`modules/grafana-mcp.nix` のバージョンと +hash を同時に更新してください。 + ## 開発環境テンプレート プロジェクトごとに独立した Nix 開発環境を提供するテンプレートを管理しています(`templates/` 参照)。 diff --git a/config/AGENTS.md b/config/AGENTS.md index 4dd67b8..8e6b276 100644 --- a/config/AGENTS.md +++ b/config/AGENTS.md @@ -79,6 +79,10 @@ - **汎用スクリプト実行**: 任意コードの実行につながる恐れがあるインタープリタ。 - 例: `python`, `python3` +Nix で宣言した固定の MCP 起動設定が子プロセスとして Doppler を起動する経路は、 +AI Agent がツール経由で任意の Doppler コマンドを実行する経路とは別に扱います。 +この区別は、AI Agent に Doppler の実行権限を与えるものではありません。 + ## 5. permission 設定の扱い - AI Agent のコマンド許可判断の原則はこの `config/AGENTS.md` に記述する diff --git a/home.nix b/home.nix index f7bf341..f605c81 100644 --- a/home.nix +++ b/home.nix @@ -8,6 +8,7 @@ imports = [ ./modules/packages.nix ./modules/llm-agents.nix + ./modules/grafana-mcp.nix ./modules/git.nix ./modules/gpg.nix ./modules/ssh.nix diff --git a/modules/claude.nix b/modules/claude.nix index 0cbfcea..fc0612a 100644 --- a/modules/claude.nix +++ b/modules/claude.nix @@ -1,11 +1,38 @@ -{ ... }: +{ + pkgs, + grafanaMcp, + ... +}: +let + jsonFormat = pkgs.formats.json { }; + pluginManifest = jsonFormat.generate "claude-grafana-mcp-plugin.json" { + name = "grafana-mcp"; + }; + mcpConfig = jsonFormat.generate "claude-grafana-mcp.json" { + mcpServers.grafana = { + inherit (grafanaMcp) command args; + type = "stdio"; + }; + }; + grafanaMcpPlugin = pkgs.runCommand "claude-grafana-mcp-plugin" { } '' + install -Dm644 ${pluginManifest} "$out/grafana-mcp/.claude-plugin/plugin.json" + install -Dm644 ${mcpConfig} "$out/grafana-mcp/.mcp.json" + ''; + claudeSkills = pkgs.symlinkJoin { + name = "claude-skills"; + paths = [ + ../config/agents/skills + grafanaMcpPlugin + ]; + }; +in { home.file.".claude/CLAUDE.md".source = ../config/claude/CLAUDE.md; home.file.".claude/settings.json".source = ../config/claude/settings.json; home.file.".claude/skills" = { - source = ../config/agents/skills; + source = claudeSkills; }; home.file.".claude/hooks/notify.sh" = { diff --git a/modules/codex.nix b/modules/codex.nix index 7c0a055..dd96a4d 100644 --- a/modules/codex.nix +++ b/modules/codex.nix @@ -2,6 +2,7 @@ pkgs, lib, homeDirectory, + grafanaMcp, ... }: let @@ -96,6 +97,11 @@ let plugins."google-calendar@openai-curated".enabled = true; plugins."github@openai-curated".enabled = true; + mcp_servers.grafana = { + inherit (grafanaMcp) command args; + startup_timeout_sec = 120; + }; + features.codex_git_commit = true; }; codexConfigFile = tomlFormat.generate "codex-config.toml" codexConfig; diff --git a/modules/grafana-mcp.nix b/modules/grafana-mcp.nix new file mode 100644 index 0000000..ab09b48 --- /dev/null +++ b/modules/grafana-mcp.nix @@ -0,0 +1,53 @@ +{ pkgs, ... }: +let + # renovate: datasource=github-releases depName=grafana/mcp-grafana + version = "0.14.0"; + package = pkgs.stdenvNoCC.mkDerivation { + pname = "mcp-grafana"; + inherit version; + + src = pkgs.fetchurl { + url = "https://github.com/grafana/mcp-grafana/releases/download/v${version}/mcp-grafana_Linux_x86_64.tar.gz"; + hash = "sha256-gcWrHESi580nBCCywjWHvrYqtZgvLd6LgAQFqp7G6Cw="; + }; + + sourceRoot = "."; + dontBuild = true; + + installPhase = '' + runHook preInstall + + install -Dm755 mcp-grafana "$out/bin/mcp-grafana" + + runHook postInstall + ''; + + meta = { + description = "MCP server for Grafana"; + homepage = "https://github.com/grafana/mcp-grafana"; + license = pkgs.lib.licenses.asl20; + mainProgram = "mcp-grafana"; + platforms = [ "x86_64-linux" ]; + sourceProvenance = [ pkgs.lib.sourceTypes.binaryNativeCode ]; + }; + }; +in +{ + _module.args.grafanaMcp = { + command = "${pkgs.doppler}/bin/doppler"; + args = [ + "run" + "--project" + "mcp" + "--config" + "prd" + "--" + "${package}/bin/mcp-grafana" + "-t" + "stdio" + "--disable-write" + ]; + }; + + home.packages = [ package ]; +}