home-manager には Claude Code のサポートが入っており、これを使うと MCP server の設定を宣言的に管理することができます。例えば以下のように書くと GitHub MCP Server が使えるようになります[1]:
programs.claude-code = {
enable = true;
mcpServers = {
github = {
type = "http";
url = "https://api.githubcopilot.com/mcp/";
};
};
};
設定に認証情報を含める必要が無いものはこれでよいのですが、context7 などを使おうとすると、
mcpServers の attrset の中に直接 API key を含める必要があります。/nix/store は誰でも読み取りできるため、一般に configuration.nix に直接シークレットを含めるのは良くありません。
このようなときに便利なのが agenix です。agenix を使うと、あらかじめ指定した公開鍵を用いて暗号化したファイルを /nix/store に置き、対応する秘密鍵が存在するマシン上でのみそのファイルを復号できます。復号されたファイルはパーミッションが 600 になるため所有者しかアクセスできず、セキュリティが担保されます。
agenix を使って mcpServers の中身を暗号化し、API key が直接露出しないようにしてみます。
agenix のインストールやファイルの配置は agenix の README に詳しいので省略し、
Claude Code でそのファイルをどのように使うかについて以下では詳述します。
agenix を使う場合、復号結果の平文を /nix/store に置かないように注意しなければなりません。
agenix の README に従うと config.age.secrets.context7-api-key.path のような式で context7 の API key が復号されたファイルのパスを取得できるようになります。これを builtins.readFile して programs.claude-code.mcpServers に入力したくなりますが、これをやってしまうと /nix/store に平文が載ってしまいます。代わりに claude コマンドをラップし、コマンドが実行される直前に平文を読みに行くようにします:
programs = {
claude-code = {
enable = true;
package = let
# pkgsUnstable の正体は https://blog.anqou.net/2026/04/nixos-use-unstable-pkg-with-stable-channel/ を参照。
package = pkgsUnstable.claude-code;
# mcp server の設定をコマンド実行の直前に注入する。
wrapped-claude = pkgs.writeScriptBin "claude" ''
#!${pkgs.bash}/bin/bash
${package}/bin/claude --mcp-config <(cat <<EOS
{
"mcpServers": {
"context7": {
"headers": {
"CONTEXT7_API_KEY": "$(cat ${config.age.secrets.context7-api-key.path})"
},
"type": "http",
"url": "https://mcp.context7.com/mcp"
}
}
}
EOS
) "$@"
'';
in
pkgs.symlinkJoin {
name = "claude-code";
paths = [wrapped-claude];
inherit (package) meta;
};
};
};
もう少し簡単な Nix コードでどうにかなる気もしますが、とりあえずこれで動いているので良しとしています。
注釈
-
なお後述の理由により自分は
programs.claude-code.mcpServersを直接は使っていません。このコード自体は home-manager の例から引っ張ってきたものであり、動作は未確認です。 ↩