From 436d1027ed1ec261e70f1920d609110b7dac97e1 Mon Sep 17 00:00:00 2001 From: rito528 <39003544+rito528@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:04:43 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(codex):=20Grafana=20MCP=20=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit シークレットを Codex の親環境へ常駐させず、Doppler から固定された MCP 起動プロセスだけへ渡すため。 Co-authored-by: Codex --- README.md | 31 +++++++++++++++++++++++++++++++ config/AGENTS.md | 4 ++++ modules/codex.nix | 25 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/README.md b/README.md index f26d4b9..9048bdd 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,37 @@ home-manager build --flake . home-manager generations ``` +## Codex から Grafana MCP を使う + +`modules/codex.nix` は、Doppler から必要な環境変数を受け取り、Docker で +`grafana/mcp-grafana` を読み取り専用モードで起動します。あらかじめ Doppler CLI +へのログインを済ませ、Docker デーモンを利用できる状態にしておいてください。 + +1. Grafana に最小限の RBAC 権限を持つ Service Account を作成し、トークンを発行します。 +2. Doppler の project `codex-mcp`、config `prd` に `GRAFANA_URL` と `GRAFANA_SERVICE_ACCOUNT_TOKEN` の 2 変数だけを登録します。 +3. Home Manager の設定を反映して、MCP サーバーを確認します。 + +トークンの値を dotfiles に保存したり、Codex の親プロセスに設定したりしません。 + +```bash +home-manager switch --flake . +codex mcp list +``` + +Codex を再起動した後、`/mcp` でも接続状態を確認できます。 + +Grafana Cloud ではスタックの URL を `GRAFANA_URL` に設定します。ホスト上の Grafana +へ Docker コンテナから接続する場合、コンテナ内の `localhost` はホストを指しません。 +Docker Desktop などで利用できる環境では、`http://host.docker.internal:3000` のような +URL を指定してください。 + +`--disable-write` は誤操作を防ぐための補助策であり、Grafana 側の RBAC に代わるものでは +ありません。MCP サーバーは必須扱いにしていないため、Doppler や Docker、Grafana への +接続に失敗しても Codex 全体の起動は止まりません。 + +コンテナイメージを更新するときは、再現性を保つため、`modules/codex.nix` のタグと +digest を同時に更新してください。 + ## 開発環境テンプレート プロジェクトごとに独立した 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/modules/codex.nix b/modules/codex.nix index 7c0a055..f245c91 100644 --- a/modules/codex.nix +++ b/modules/codex.nix @@ -96,6 +96,31 @@ let plugins."google-calendar@openai-curated".enabled = true; plugins."github@openai-curated".enabled = true; + mcp_servers.grafana = { + command = "${pkgs.doppler}/bin/doppler"; + args = [ + "run" + "--project" + "codex-mcp" + "--config" + "prd" + "--" + "${pkgs.docker}/bin/docker" + "run" + "--rm" + "-i" + "-e" + "GRAFANA_URL" + "-e" + "GRAFANA_SERVICE_ACCOUNT_TOKEN" + "grafana/mcp-grafana:0.14.0@sha256:42f541f2206359ce7a40c8e19d96253cef4771bf00e707a760d3a7035c40e8f8" + "-t" + "stdio" + "--disable-write" + ]; + startup_timeout_sec = 120; + }; + features.codex_git_commit = true; }; codexConfigFile = tomlFormat.generate "codex-config.toml" codexConfig; From 2a19bb2da2bda9ab8e18a27799c2561f395ae52b Mon Sep 17 00:00:00 2001 From: rito528 <39003544+rito528@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:46:16 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor(mcp):=20Grafana=20MCP=20=E3=82=92?= =?UTF-8?q?=20Nix=20=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8=E3=81=A7?= =?UTF-8?q?=E5=85=B1=E9=80=9A=E5=8C=96=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker への依存を外し、Codex と Claude Code が同じ Doppler 設定を使えるようにする。 Co-authored-by: Codex --- README.md | 27 +++++++++---------- modules/claude.nix | 28 ++++++++++++++++---- modules/codex.nix | 23 ++-------------- modules/lib/grafana-mcp.nix | 19 ++++++++++++++ modules/llm-agents.nix | 1 - modules/packages.nix | 45 ++++++++++++++++++-------------- modules/packages/mcp-grafana.nix | 34 ++++++++++++++++++++++++ 7 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 modules/lib/grafana-mcp.nix create mode 100644 modules/packages/mcp-grafana.nix diff --git a/README.md b/README.md index 9048bdd..bacde72 100644 --- a/README.md +++ b/README.md @@ -96,36 +96,35 @@ home-manager build --flake . home-manager generations ``` -## Codex から Grafana MCP を使う +## Codex と Claude Code から Grafana MCP を使う -`modules/codex.nix` は、Doppler から必要な環境変数を受け取り、Docker で -`grafana/mcp-grafana` を読み取り専用モードで起動します。あらかじめ Doppler CLI -へのログインを済ませ、Docker デーモンを利用できる状態にしておいてください。 +`mcp-grafana` は Nix パッケージとしてインストールされ、Codex と Claude Code が同じ +起動設定を使います。起動時に Doppler から必要な環境変数を受け取り、読み取り専用 +モードで Grafana に接続します。あらかじめ Doppler CLI へログインしてください。 1. Grafana に最小限の RBAC 権限を持つ Service Account を作成し、トークンを発行します。 -2. Doppler の project `codex-mcp`、config `prd` に `GRAFANA_URL` と `GRAFANA_SERVICE_ACCOUNT_TOKEN` の 2 変数だけを登録します。 -3. Home Manager の設定を反映して、MCP サーバーを確認します。 +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 を再起動した後、`/mcp` でも接続状態を確認できます。 +Codex と Claude Code を再起動した後、それぞれの `/mcp` でも接続状態を確認できます。 Grafana Cloud ではスタックの URL を `GRAFANA_URL` に設定します。ホスト上の Grafana -へ Docker コンテナから接続する場合、コンテナ内の `localhost` はホストを指しません。 -Docker Desktop などで利用できる環境では、`http://host.docker.internal:3000` のような -URL を指定してください。 +へ接続する場合は、`http://localhost:3000` のような URL を指定できます。 `--disable-write` は誤操作を防ぐための補助策であり、Grafana 側の RBAC に代わるものでは -ありません。MCP サーバーは必須扱いにしていないため、Doppler や Docker、Grafana への -接続に失敗しても Codex 全体の起動は止まりません。 +ありません。MCP サーバーは必須扱いにしていないため、Doppler や Grafana への接続に +失敗しても Codex や Claude Code 全体の起動は止まりません。 -コンテナイメージを更新するときは、再現性を保つため、`modules/codex.nix` のタグと -digest を同時に更新してください。 +`mcp-grafana` を更新するときは、`modules/packages/mcp-grafana.nix` のバージョンと +hash を同時に更新してください。 ## 開発環境テンプレート diff --git a/modules/claude.nix b/modules/claude.nix index 0cbfcea..29c3654 100644 --- a/modules/claude.nix +++ b/modules/claude.nix @@ -1,13 +1,31 @@ -{ ... }: +{ pkgs, ... }: +let + grafanaMcp = import ./lib/grafana-mcp.nix { inherit pkgs; }; + skillEntries = builtins.readDir ../config/agents/skills; + skillNames = builtins.filter (name: skillEntries.${name} == "directory") ( + builtins.attrNames skillEntries + ); + claudeSkills = builtins.listToAttrs ( + map (name: { + inherit name; + value = ../config/agents/skills + "/${name}"; + }) skillNames + ); +in { + programs.claude-code = { + enable = true; + package = pkgs.llm-agents.claude-code; + skills = claudeSkills; + mcpServers.grafana = { + inherit (grafanaMcp) command args; + }; + }; + 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; - }; - home.file.".claude/hooks/notify.sh" = { text = builtins.readFile ../config/claude/hooks/notify.sh; executable = true; diff --git a/modules/codex.nix b/modules/codex.nix index f245c91..294d24f 100644 --- a/modules/codex.nix +++ b/modules/codex.nix @@ -6,6 +6,7 @@ }: let tomlFormat = pkgs.formats.toml { }; + grafanaMcp = import ./lib/grafana-mcp.nix { inherit pkgs; }; shellUtilityPrefixes = [ "rg" "grep" @@ -97,27 +98,7 @@ let plugins."github@openai-curated".enabled = true; mcp_servers.grafana = { - command = "${pkgs.doppler}/bin/doppler"; - args = [ - "run" - "--project" - "codex-mcp" - "--config" - "prd" - "--" - "${pkgs.docker}/bin/docker" - "run" - "--rm" - "-i" - "-e" - "GRAFANA_URL" - "-e" - "GRAFANA_SERVICE_ACCOUNT_TOKEN" - "grafana/mcp-grafana:0.14.0@sha256:42f541f2206359ce7a40c8e19d96253cef4771bf00e707a760d3a7035c40e8f8" - "-t" - "stdio" - "--disable-write" - ]; + inherit (grafanaMcp) command args; startup_timeout_sec = 120; }; diff --git a/modules/lib/grafana-mcp.nix b/modules/lib/grafana-mcp.nix new file mode 100644 index 0000000..0558402 --- /dev/null +++ b/modules/lib/grafana-mcp.nix @@ -0,0 +1,19 @@ +{ pkgs }: +let + mcpGrafana = import ../packages/mcp-grafana.nix { inherit pkgs; }; +in +{ + command = "${pkgs.doppler}/bin/doppler"; + args = [ + "run" + "--project" + "mcp" + "--config" + "prd" + "--" + "${mcpGrafana}/bin/mcp-grafana" + "-t" + "stdio" + "--disable-write" + ]; +} diff --git a/modules/llm-agents.nix b/modules/llm-agents.nix index 2385a74..abe55e3 100644 --- a/modules/llm-agents.nix +++ b/modules/llm-agents.nix @@ -2,7 +2,6 @@ { home.packages = [ pkgs.llm-agents.ccusage - pkgs.llm-agents.claude-code pkgs.llm-agents.copilot-cli pkgs.llm-agents.codex pkgs.llm-agents.antigravity-cli diff --git a/modules/packages.nix b/modules/packages.nix index 04a7fd2..e369243 100644 --- a/modules/packages.nix +++ b/modules/packages.nix @@ -1,23 +1,28 @@ { pkgs, ... }: +let + mcpGrafana = import ./packages/mcp-grafana.nix { inherit pkgs; }; +in { - home.packages = with pkgs; [ - git - jq - direnv - starship - nodejs_24 - nixfmt - doppler - gh - ripgrep - shellcheck - actionlint - ghq - fzf - editorconfig-checker - bubblewrap - socat - yazi - python3 - ]; + home.packages = + (with pkgs; [ + git + jq + direnv + starship + nodejs_24 + nixfmt + doppler + gh + ripgrep + shellcheck + actionlint + ghq + fzf + editorconfig-checker + bubblewrap + socat + yazi + python3 + ]) + ++ [ mcpGrafana ]; } diff --git a/modules/packages/mcp-grafana.nix b/modules/packages/mcp-grafana.nix new file mode 100644 index 0000000..cf194be --- /dev/null +++ b/modules/packages/mcp-grafana.nix @@ -0,0 +1,34 @@ +{ pkgs }: +let + # renovate: datasource=github-releases depName=grafana/mcp-grafana + version = "0.14.0"; +in +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 ]; + }; +} From a225c0624a95208818f57a0f407e0e80f3e912bb Mon Sep 17 00:00:00 2001 From: rito528 <39003544+rito528@users.noreply.github.com> Date: Sun, 19 Jul 2026 19:49:34 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(mcp):=20=E6=97=A2=E5=AD=98=E3=81=AE=20C?= =?UTF-8?q?laude=20Code=20=E7=AE=A1=E7=90=86=E6=96=B9=E6=B3=95=E3=82=92?= =?UTF-8?q?=E7=B6=AD=E6=8C=81=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MCP の追加によって Claude Code 本体や skills の配布責務まで移さないようにし、mcp-grafana の定義とインストールを専用 module にまとめる。 Co-authored-by: Codex --- README.md | 2 +- home.nix | 1 + modules/claude.nix | 47 ++++++++++++++++------------ modules/codex.nix | 2 +- modules/grafana-mcp.nix | 53 ++++++++++++++++++++++++++++++++ modules/lib/grafana-mcp.nix | 19 ------------ modules/llm-agents.nix | 1 + modules/packages.nix | 45 ++++++++++++--------------- modules/packages/mcp-grafana.nix | 34 -------------------- 9 files changed, 105 insertions(+), 99 deletions(-) create mode 100644 modules/grafana-mcp.nix delete mode 100644 modules/lib/grafana-mcp.nix delete mode 100644 modules/packages/mcp-grafana.nix diff --git a/README.md b/README.md index bacde72..9f2fba7 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ Grafana Cloud ではスタックの URL を `GRAFANA_URL` に設定します。 ありません。MCP サーバーは必須扱いにしていないため、Doppler や Grafana への接続に 失敗しても Codex や Claude Code 全体の起動は止まりません。 -`mcp-grafana` を更新するときは、`modules/packages/mcp-grafana.nix` のバージョンと +`mcp-grafana` を更新するときは、`modules/grafana-mcp.nix` のバージョンと hash を同時に更新してください。 ## 開発環境テンプレート 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 29c3654..fc0612a 100644 --- a/modules/claude.nix +++ b/modules/claude.nix @@ -1,31 +1,40 @@ -{ pkgs, ... }: -let - grafanaMcp = import ./lib/grafana-mcp.nix { inherit pkgs; }; - skillEntries = builtins.readDir ../config/agents/skills; - skillNames = builtins.filter (name: skillEntries.${name} == "directory") ( - builtins.attrNames skillEntries - ); - claudeSkills = builtins.listToAttrs ( - map (name: { - inherit name; - value = ../config/agents/skills + "/${name}"; - }) skillNames - ); -in { - programs.claude-code = { - enable = true; - package = pkgs.llm-agents.claude-code; - skills = claudeSkills; + 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 = claudeSkills; + }; + home.file.".claude/hooks/notify.sh" = { text = builtins.readFile ../config/claude/hooks/notify.sh; executable = true; diff --git a/modules/codex.nix b/modules/codex.nix index 294d24f..dd96a4d 100644 --- a/modules/codex.nix +++ b/modules/codex.nix @@ -2,11 +2,11 @@ pkgs, lib, homeDirectory, + grafanaMcp, ... }: let tomlFormat = pkgs.formats.toml { }; - grafanaMcp = import ./lib/grafana-mcp.nix { inherit pkgs; }; shellUtilityPrefixes = [ "rg" "grep" 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 ]; +} diff --git a/modules/lib/grafana-mcp.nix b/modules/lib/grafana-mcp.nix deleted file mode 100644 index 0558402..0000000 --- a/modules/lib/grafana-mcp.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ pkgs }: -let - mcpGrafana = import ../packages/mcp-grafana.nix { inherit pkgs; }; -in -{ - command = "${pkgs.doppler}/bin/doppler"; - args = [ - "run" - "--project" - "mcp" - "--config" - "prd" - "--" - "${mcpGrafana}/bin/mcp-grafana" - "-t" - "stdio" - "--disable-write" - ]; -} diff --git a/modules/llm-agents.nix b/modules/llm-agents.nix index abe55e3..2385a74 100644 --- a/modules/llm-agents.nix +++ b/modules/llm-agents.nix @@ -2,6 +2,7 @@ { home.packages = [ pkgs.llm-agents.ccusage + pkgs.llm-agents.claude-code pkgs.llm-agents.copilot-cli pkgs.llm-agents.codex pkgs.llm-agents.antigravity-cli diff --git a/modules/packages.nix b/modules/packages.nix index e369243..04a7fd2 100644 --- a/modules/packages.nix +++ b/modules/packages.nix @@ -1,28 +1,23 @@ { pkgs, ... }: -let - mcpGrafana = import ./packages/mcp-grafana.nix { inherit pkgs; }; -in { - home.packages = - (with pkgs; [ - git - jq - direnv - starship - nodejs_24 - nixfmt - doppler - gh - ripgrep - shellcheck - actionlint - ghq - fzf - editorconfig-checker - bubblewrap - socat - yazi - python3 - ]) - ++ [ mcpGrafana ]; + home.packages = with pkgs; [ + git + jq + direnv + starship + nodejs_24 + nixfmt + doppler + gh + ripgrep + shellcheck + actionlint + ghq + fzf + editorconfig-checker + bubblewrap + socat + yazi + python3 + ]; } diff --git a/modules/packages/mcp-grafana.nix b/modules/packages/mcp-grafana.nix deleted file mode 100644 index cf194be..0000000 --- a/modules/packages/mcp-grafana.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ pkgs }: -let - # renovate: datasource=github-releases depName=grafana/mcp-grafana - version = "0.14.0"; -in -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 ]; - }; -}