
otelHeadersHelper を使って Claude Code の OTLP 認証トークンを Keychain に保管する
はじめに
Claude Code の利用状況を OpenTelemetry (OTel) で可視化している人は多いと思います。私も Grafana Cloud に送っていますが、認証トークンの置き場所が以前から気になっていました。
テレメトリの送信先である Grafana Cloud OTLP エンドポイントには認証ヘッダーが必要で、Basic 認証 Authorization ヘッダーに入れます。Claude Code でこのヘッダーを設定するなら、settings.json の env に OTEL_EXPORTER_OTLP_HEADERS として書くのが一般的です。
これで動くには動くのですが、トークンが settings.json に平文で保存されてしまいます。プロジェクトスコープの場合は settings.json を Git で管理したりすることもあるので、平文のトークンはなるべく置きたくありません。
そこで使えるのが otelHeadersHelper です。OTLP に付けるヘッダーの生成を外部スクリプトに任せられる Claude Code 標準の設定で、トークンを settings.json から切り離すことができます。
otelHeadersHelper にヘッダー生成を任せる
otelHeadersHelper は、OTLP に付ける HTTP ヘッダーを外部スクリプトに生成させる設定です。settings.json にスクリプトのパスを書いておくと、Claude Code が起動時にそのスクリプトを実行し、標準出力に返ってきた JSON をヘッダーとして使います。
ヘルパースクリプトは起動時に一度実行され、その後も定期的に再実行されます。デフォルトの間隔は 29 分で、CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS 環境変数で調整できます。
設定してみた
私の環境は macOS なので、トークンの保管先には Keychain を使いました。まずは Grafana Cloud で発行した Base64 済みトークンを Keychain に登録します。
security add-generic-password -U -a "$USER" -s grafana-otlp-token -w '<base64-token>'
次に、Keychain からトークンを取り出して JSON を出力するスクリプトを用意します。
#!/usr/bin/env bash
# Grafana Cloud OTLP の認証ヘッダーを Keychain から動的取得して JSON で出力する
set -euo pipefail
TOKEN="$(security find-generic-password -a "$USER" -s grafana-otlp-token -w 2>/dev/null || true)"
if [ -z "${TOKEN}" ]; then
echo '{"error":"grafana-otlp-token not found in keychain"}' >&2
exit 1
fi
printf '{"Authorization":"Basic %s"}\n' "${TOKEN}"
スクリプトの返却値ですが、otelHeadersHelper に渡すのは JSON オブジェクトで、キーがヘッダー名、値がヘッダー値という形になります。環境変数の OTEL_EXPORTER_OTLP_HEADERS で設定するように Authorization=Basic ... を返すようにすると動作しません。
最後に settings.json を書き換えます。テレメトリ関連の env から OTEL_EXPORTER_OTLP_HEADERS を消し、代わりに otelHeadersHelper を追加します。
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://otlp-gateway-xxxx.grafana.net/otlp"
},
"otelHeadersHelper": "/Users/<you>/.claude/scripts/otel-headers.sh"
}
これで認証トークンは settings.json から削除でき、実体は Keychain の中だけになりました。トークンをローテーションするときも、Keychain のエントリを差し替えるだけで済みます。
確認してみた
設定したら、実際にデータが届いているかを確認します。テレメトリのエクスポート間隔はデフォルトでメトリクスが 60 秒、ログが 5 秒です。動作確認のときは一時的に短くしておくと様子を見やすくなります。
export OTEL_METRIC_EXPORT_INTERVAL=10000
export OTEL_LOGS_EXPORT_INTERVAL=5000
Claude Code を claude --debug で起動すると、ヘルパーの実行結果やエクスポートの動きがログに出力されます。なお、エラー発生時は /doctor の出力や --debug 実行時のデバッグログに以下のようなログを出力します。
2026-06-18T13:24:15.770Z [ERROR] Error getting OpenTelemetry headers from otelHeadersHelper (in settings): exited 1: {"error":"grafana-otlp-token not found in keychain"}
あとは Grafana 側でメトリクスやログが入ってきているかを確認しましょう。
おわりに
今回の方法は macOS の Keychain を前提としています。Linux や Windows ではそのまま使えないので、pass や OS 標準の資格情報ストア、あるいはクラウドのシークレットマネージャーから取得するなど、環境に合わせて変更しましょう。スクリプトが JSON を返しさえすれば中身は自由なので、取得元は差し替えやすいはずです。
ヘルパーは 29 分ごとに再実行されます。毎回ネットワーク越しに重い処理を走らせるようなスクリプトだと、その負荷が定期的にかかります。Keychain からの読み出し程度なら気になりませんが、外部 API を叩く場合は軽い処理になるよう実装を工夫させるのが無難だと思います。
otelHeadersHelper を使ってもトークンの実体は Keychain という形でローカルに残ります。その意味で銀の弾丸にはならないのですが、それでも認証トークンを settings.json から切り離せるというのは精神衛生上安心です。設定ファイルをチームで共有したり Git に乗せたりする運用なら、検討する価値は十分にあると思います。







