Claude Media
ArgoCD MCPサーバーでGitOpsデプロイをClaudeに任せる

ArgoCD MCPサーバーでGitOpsデプロイをClaudeに任せる

argoproj-labs公式のArgoCD MCPサーバーで、アプリケーションの同期やリソースのトラブルシュートを自然言語から行う手順をまとめます。

ArgoCD MCPサーバー(argoproj-labs/mcp-for-argocd)は、アプリケーションの同期・リソースツリーの確認・ワークロードのログ取得をClaude Codeから自然言語で操作するMCPサーバーです。Akuityの開発者が作って寄贈したOSSで、Anthropicの製品ではありません。

この記事の中心は、導入手順より「何を任せて、何を任せないか」の線引きです。権限を絞る3つの層と、v0.9.0でネットワーク公開まわりの既定値が変わった点を軸に進めます。

前提とClaude Codeへの登録

GitOpsの運用では、Gitにpushしたマニフェストがクラスタにいつ・どう反映されたかを追う作業が日常的に出ます。このサーバーはその確認をAIエージェント経由に置き換えるもので、stdioとHTTP streamの両方に対応します。個人のローカル利用ならstdio、チーム共有ならHTTPという住み分けです。Applicationのマニフェストの書き方はClaude CodeでArgoCD Applicationマニフェストを書く手順で扱っています。

必要なものはNode.js(v18以上を推奨)と、APIにアクセスできるArgoCDインスタンスです。登録に使うのはArgoCDのベースURLとAPIトークンの2つです。

claude mcp add-json argocd-mcp \
  '{"command":"npx","args":["argocd-mcp@latest","stdio"],"env":{"ARGOCD_BASE_URL":"<argocd_url>","ARGOCD_API_TOKEN":"<argocd_token>"}}' \
  -s user

claude mcp add-json の構文やスコープの使い分けはClaude Code MCP設定ガイドにあります。npmのargocd-mcpはv0.8.0が2026年6月11日、v0.9.0が2026年8月11日の公開で、@latest はv0.9.0以降を取ってきます。

自己署名証明書のArgoCDには、環境変数 NODE_TLS_REJECT_UNAUTHORIZED を 0 にして繋げます。READMEが警告するとおりTLS検証そのものを切る設定なので、開発環境に限った回避策です。

APIトークンはツール呼び出しの引数としては受け付けず、HTTPヘッダーか環境変数からだけ読み込みます。トークンがプロンプトやモデルのコンテキストに入らないようにする設計です。

Cursor・VS Code・Claude Desktopでの書き方の違い

設定の中身は同じで、置き場所と最上位のキーがクライアントごとに違います。

クライアント置き場所最上位のキー
Cursor置き場所プロジェクトの .cursor/mcp.json最上位のキーmcpServers
VS Code置き場所プロジェクトの .vscode/mcp.json最上位のキーservers("type": "stdio" を明示)
Claude Desktop置き場所claude_desktop_config.json最上位のキーmcpServers

Cursorの例です。VS Codeはキーを servers に変え、サーバー定義に "type": "stdio" を足します。

{
  "mcpServers": {
    "argocd-mcp": {
      "command": "npx",
      "args": ["argocd-mcp@latest", "stdio"],
      "env": {
        "ARGOCD_BASE_URL": "<argocd_url>",
        "ARGOCD_API_TOKEN": "<argocd_token>"
      }
    }
  }
}

VS Code向けのワンクリックインストールのリンクもREADMEで配布されています。どのクライアントでも、トークンをファイルに直書きしない運用にしておくと、設定ファイルを誤ってコミットする事故を避けられます。Claude Code自体の全体像はClaude Codeの全体像から追えます。

提供ツールと、READMEとの食い違い

ツールはクラスタ・プロジェクト・アプリケーション・リソースの4系統です。READMEの一覧は次のとおりです。

系統READMEに載るツールできること
クラスタREADMEに載るツールlist_clustersできること登録済みクラスタの一覧
プロジェクトREADMEに載るツールget_appprojectできることAppProjectの詳細取得
アプリケーションREADMEに載るツールlist_applications / get_application / create_application / update_application / delete_application / sync_applicationできること作成・更新・同期・削除
リソースREADMEに載るツールget_application_resource_tree / get_application_managed_resources / get_application_workload_logs / get_resource_events / get_resource_actions / run_resource_actionできること状態確認とアクション実行

READMEと実際に公開されるツールにはずれがあります。v0.8.0に対するIssue #138では、実際の tools/list が15ツールで、get_appproject は含まれず、READMEに載らない get_application_events と get_resources が含まれると報告されています。この2ツールはv0.9.0の tools/list にもあり、READMEの表には載っていません。get_appproject を足すPR #133は2026年7月9日にマージされ、v0.9.0のリリースノートに入りました。v0.8.0を固定している環境には、プロジェクトの取得ツールがありません。

障害調査では、get_application_workload_logs でPodやDeploymentのログを取り、get_resource_events で時系列を追います。get_resource_actions はリソースに対して実行できるアクションの一覧を返すので、run_resource_action の前に呼ぶと、何が実行できるかを先に見られます。

大きな環境ではlist_applicationsが重い

数百のアプリケーションを抱えるArgoCDでは、一覧系のツールがコンテキストを食います。PR #147(2026年8月5日作成)の報告では、引数なしの list_applications はv0.8.0で約300万文字(約77万トークン)を返しました。search 引数はArgoCD側のクエリに存在せず、無視されて全件が返るとも書かれています。

このPRは未マージです。大規模環境では、指示の側でアプリケーション名を具体的に与え、一覧を丸ごと取らせない使い方が現実的です。

何を任せるか:権限を絞る3つの層

このサーバーは同期や削除まで呼べます。権限は次の3つの層で別々に絞れて、層ごとに守るものが違います。

層

権限を絞る3つの層

  • ArgoCD側:Roleのトークン

    AppProjectにRoleを定義し、そのRole専用のJWTを argocd proj role create-token で発行します。公式ドキュメントには、CI向けに「1つのアプリへの同期だけ」を許す使い方が例として載っています。MCPサーバーの設定が破られても、ArgoCDがRoleを超える操作を断ります。

  • サーバー側:MCP_READ_ONLY

    MCP_READ_ONLY を true にすると、create_application・update_application・delete_application・sync_application・run_resource_action が無効になり、閲覧系だけが残ります。既定では全ツールが有効です。

  • クライアント側:Claude Codeの拒否ルール

    Claude Codeの権限設定で、特定のツールを拒否できます。サーバー側の設定を間違えても、クライアントの線は残ります。

3層目が要る理由は、Issue #137にあります。v0.8.0の15ツールには、MCPの destructiveHint や readOnlyHint といったannotationsが付いていないと報告されています。v0.9.0のソースにも指定はありません。クライアントは list_applications と delete_application をメタデータ上で区別できません。区別は設定側で補う形になります。

Claude Codeの設定ファイルでは、mcp__<サーバー名>__<ツール名> の形で拒否ルールを書けます。サーバー名は claude mcp add-json で付けた argocd-mcp です。

{
  "permissions": {
    "deny": [
      "mcp__argocd-mcp__delete_application",
      "mcp__argocd-mcp__create_application"
    ]
  }
}

管理者トークンをそのまま渡すと、他の層が破られた時点で全操作が通ります。同じ発想は、HCP Terraformのワークスペースを触るTerraform MCPサーバーの運用にも当てはまります。

ネットワークに公開するとき(v0.9.0で既定値が変わった点)

http と sse のトランスポートはネットワークのリスナーを開き、create_application や delete_application を含む全ツールに到達できるようにします。v0.9.0のリリースノートでは、これがセキュリティ修正(GHSA-rp45-5x3v-48mr)として扱われています。以前はすべてのインターフェースにバインドしていた既定が、v0.9.0で 127.0.0.1 に変わりました。

READMEの設定一覧は次のとおりです。

設定フラグ / 環境変数既定役割
バインドアドレスフラグ / 環境変数--bind-address / MCP_BIND_ADDRESS既定127.0.0.1役割接続を受けるアドレス
受信トークンフラグ / 環境変数MCP_AUTH_TOKEN既定未設定役割設定すると全リクエストに Authorization: Bearer が必須
許可するHostフラグ / 環境変数--allowed-host-header既定ループバック名役割追加で受け入れるホスト名
許可するOriginフラグ / 環境変数--allowed-origin既定ループバックのorigin役割追加で受け入れるブラウザのorigin
外部認証フラグ / 環境変数--allow-unauthenticated既定false役割プロキシ側で認証済みのとき、トークン無しの公開を許す
ポートフラグ / 環境変数--port既定3000役割待ち受けポート

ARGOCD_API_TOKEN は呼び出し元の認証には使われません。ArgoCDに対してこのサーバーを認証するだけです。READMEには「--bind-address は誰が接続できるかを決め、--allowed-host-header は接続済みのクライアントが名乗る内容を見るだけ」とあり、後者はファイアウォールの代わりになりません。バインドを 0.0.0.0 のように広げるときは、MCP_AUTH_TOKEN か --allow-unauthenticated のどちらかが必須で、どちらも無いと理由をログに出して非ゼロ終了します。

export MCP_AUTH_TOKEN=<inbound_token>
node dist/index.js http --bind-address 0.0.0.0 \
  --allowed-host-header mcp.internal.example.com

Origin は常に検査され、悪意あるWebページやDNSリバインディングを止める役目です。GET /healthz は検査の対象外で、kubeletの生存確認が通ります。同じPod内のサイドカーなど、ループバックを共有する呼び出し元は、設定を変えなくても届きます。

複数のArgoCDインスタンスを扱う

ステージングと本番でインスタンスが分かれるときは、ARGOCD_TOKEN_REGISTRY_PATH でトークンレジストリのJSONを指します。トークンは環境変数でなくファイルに置き、プロセスの環境やクラッシュダンプ、子プロセスへの継承に漏れないようにする設計です。

[
  { "baseUrl": "https://argo-staging.example.com", "token": "<token-staging>" },
  { "baseUrl": "https://argo-prod.example.com", "token": "<token-prod>" }
]

ファイルは chmod 400 などで絞り、Kubernetes Secretのマウントなどの仕組みを使うよう、READMEが勧めています。

各ツールは argocdBaseUrl 引数でホストを指定できます。トークンは次の順で決まります。

手順

呼び出しごとのトークンの決まり方

  1. 1

    既定のベースURLを指す呼び出し

    環境変数やヘッダーで渡した既定トークンを使います。既定トークンが無いセッションなら、レジストリにあるそのURLのトークンへ切り替わります。

  2. 2

    それ以外のベースURLを指す呼び出し

    レジストリのトークンだけを使います。既定トークンは、既定のベースURL以外のホストには送られません。

  3. 3

    どちらにも当たらない呼び出し

    「Missing required ArgoCD API token」のエラーを返し、そのホストへはリクエストを送りません。

2番目の制約は、argocdBaseUrl がツール呼び出しから来る値であることに由来します。プロンプトインジェクションで任意のホストを指されても、既定トークンがそこへ漏れません。Issue #139(クローズ済み)は、呼び出しごとのベースURL上書きが、間違ったインスタンスへ黙ってトークンを送りうるという報告です。現在のREADMEの仕様はこの種の事故を避ける形です。レジストリのファイルが読めない場合も、既定トークンへ黙って切り替わらず、起動時にエラーで止まります。

HTTPを複数レプリカで動かす:ステートレスモード

HTTPトランスポートは既定でセッションIDをメモリに持ちます。複数のPodをセッションアフィニティ無しで動かすと、別のPodへ回った要求がセッションを見つけられず 400 になります。--stateless を付けると、Mcp-Session-Id を返さず要求しなくなり、どのレプリカでも処理できます。

ステートレスでは、ArgoCDの認証情報を毎回の要求で渡します。環境変数か、x-argocd-base-url と x-argocd-api-token ヘッダーを使います。GET /mcp と DELETE /mcp は 405 を返し、セッション単位のSSEと終了はサポートされません。READMEは、Horizontal Pod Autoscalingのようにスティッキーセッションが使えない構成に向くと説明しています。

Issue #152(2026年9月7日)の報告です。ステートレスの常時SSE応答が、Python製MCP SDKのv2系クライアントで「SSE stream ended without a response」になります。JSON応答を選ぶ --json-response を足すPR #153は未マージです。

デプロイを任せる指示の例

接続後は「production環境のAppを同期して」のような指示で sync_application が呼ばれます。失敗したときは「なぜ同期に失敗したか調べて」と続けます。get_application_resource_tree や get_resource_events が呼び出しの候補になり、Claude Codeが原因を追います。run_resource_action を使えば、失敗したリソースにアクションを実行させることもできます。ここは書き込み系なので、前節の読み取り専用モードや拒否ルールの対象です。

Podの中身まで追うなら、Kubernetes MCPサーバーの併用という選択肢があります。ArgoCDのツールでも get_application_workload_logs でログは取れますが、kubectl 相当の操作はKubernetes側のサーバーの領分です。両方を読み取り専用に寄せる構成と、専用のServiceAccountの作り方はKubernetes MCPサーバーの権限設計で扱っています。

構成の選び方

構成向くケース理由
stdio + 単一トークン向くケース個人のローカル開発理由設定が最小で、プロセス起動と同時に使える
HTTP + MCP_AUTH_TOKEN向くケースチームで1つのインスタンスを共有理由呼び出し元を認証しつつ複数人で使える
HTTP + トークンレジストリ向くケースステージング・本番を横断理由ホストごとにトークンを分離して切り替えられる
HTTP + --stateless向くケース複数レプリカ・HPA構成理由セッションアフィニティが要らない
MCP_READ_ONLY=true向くケース障害調査専任のBotやダッシュボード理由同期・削除が走る心配がない

よくあるつまずき

  • 「Missing required ArgoCD API token」と出る: argocdBaseUrl に既定とは別のホストを指定しています。そのホストをトークンレジストリに登録します
  • 複数レプリカで 400 が出る: セッションがPodごとのメモリにあるためです。ステートレスモードか、ロードバランサ側のセッションアフィニティで解消します
  • Dockerで http --stateless を渡すと「Cannot find module '/app/http'」になる: 原因はv0.8.0以前のイメージです。起動コマンドが CMD なので、引数が全体を置き換えます(Issue #128)。v0.9.0以降は ENTRYPOINT に変わりました。READMEの docker run <image> http --stateless がそのまま動きます。古いイメージのときだけ node dist/index.js http --stateless まで渡します
  • バインドを広げたら起動しない: MCP_AUTH_TOKEN も --allow-unauthenticated も無いためです。前者を設定するか、認証済みのプロキシ配下なら後者を使います

まとめ

本番に繋ぐ前に決めるのは、任せる範囲です。個人のローカル利用なら、stdioと MCP_READ_ONLY=true の組み合わせで閲覧だけを任せるところから始められます。書き込みが要る段階で、Roleのトークンと拒否ルールを足していく順番です。HTTPで公開する場合は、先にバージョンを確かめます。

この記事を共有:XはてブLinkedIn