ArgoCD

by matthisholleville

Not rated
GitHub

About

Expose the entire ArgoCD API to LLMs via MCP using just 2 auto-generated tools powered by the OpenAPI spec.

Details

Author
matthisholleville
Categories
Cloud Service, Infrastructure, Other, API

Setup

Install ArgoCD in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/matthisholleville/argocd-mcp

Follow the installation instructions in the repository README, then restart your MCP client.

The entire ArgoCD API, exposed to LLMs via MCP.
103+ endpoints. Zero hardcoded handlers. Two modes: search or generated tools.

Quick Start•How It Works•OAuth•OIDC•Configuration

Most ArgoCD MCP servers hardcode a few operations: list apps, sync, get status. When ArgoCD adds a new feature, you wait for the maintainer to add it.

argocd-mcptakes a different approach, inspired byCloudflare's MCP serverwhich covers 2500+ endpoints with only 2 tools. It reads ArgoCD's OpenAPI spec at startup and exposes every endpoint through just 2 tools:searchandexecute. New ArgoCD version? Restart the server. Done.

- 103+ endpointsfrom ArgoCD's OpenAPI spec, zero hardcoded handlers
- Two tool modes:search(2 meta-tools) orgenerated(1 typed tool per endpoint)
- Works withClaude Desktop, Claude Code, Cursor, or any MCP client
- No code per endpoint— the OpenAPI spec is the source of truth
- Three auth modes: static token, OAuth via ArgoCD Dex, or OAuth via the external OIDC provider ArgoCD trusts (both give per-user RBAC)
- Read-only mode— disable all write operations with a single flag
- Resource scoping— restrict which ArgoCD resources are exposed withALLOWED_RESOURCES
- Rate limiting— per-user token bucket to protect ArgoCD from excessive calls
- Prompt templates— pre-packaged workflows for common operations (unhealthy apps, diff, rollback, logs)
- Audit logging— structured JSON logs for every tool call (user, method, path, status, duration)
- MCP annotations— tools are annotated as read-only, destructive, or idempotent for proper client categorization
- Optional semantic searchvia Ollama embeddings

At startup, the server fetches ArgoCD's Swagger spec and parses every endpoint. Then it exposes them to LLMs via one of two modes:

Search mode (default,TOOL_MODE=search)

Two meta-tools handle all 103+ endpoints. The LLM discovers endpoints by searching, then calls them via a generic executor.

graph TD A[ArgoCD /swagger.json] -->|Fetch at startup| B[Parse Swagger 2.0] B --> C[103+ Endpoints in memory] C --> D[search_operations] C --> E[execute_operation] D -->|LLM discovers endpoints| F[Returns method, path, summary, params] E -->|LLM calls API| G[Proxies to ArgoCD with user token]

One typed MCP tool per endpoint, generated dynamically at startup. The LLM callsargocd_application_sync(name, revision)directly — no search step, no path construction.

graph TD A[ArgoCD /swagger.json] -->|Fetch at startup| B[Parse Swagger 2.0] B --> C[103+ Endpoints] C -->|Generate per endpoint| D[argocd_application_list] C --> E[argocd_application_sync] C --> F[argocd_cluster_get] C --> G[... 100+ more tools] D & E & F & G -->|Typed params, 1 call| H[Proxies to ArgoCD]

Clients like Claude Code and Claude Desktop supportdeferred tool loading— they only load tool definitions into context when needed, so the 103+ tools don't consume context window upfront.

helm install argocd-mcp oci://ghcr.io/matthisholleville/charts/argocd-mcp \ --set argocd.baseURL=https://argocd.example.com \ --set argocd.token=your-token

See all configuration options incharts/argocd-mcp/values.yaml.

Best for local dev, CI/CD, or single-user setups. Uses a static ArgoCD API token.

claude mcp add argocd -s user -- \ docker run --rm -i \ -e ARGOCD_BASE_URL=https://argocd.example.com \ -e ARGOCD_TOKEN=your-token \ ghcr.io/matthisholleville/argocd-mcp:latest

Add to your Claude Desktop MCP config (claude_desktop_config.json):

{ "mcpServers": { "argocd": { "command": "docker", "args": ["run", "--rm", "-i", "-e", "ARGOCD_BASE_URL=https://argocd.example.com", "-e", "ARGOCD_TOKEN=your-token", "ghcr.io/matthisholleville/argocd-mcp:latest" ] } } }

Best for multi-user, production setups. Each user authenticates with their own identity via ArgoCD's built-in Dex.No static token needed— the user's Dexid_tokenis forwarded to ArgoCD, which applies its RBAC policies per user.

docker run -p 8080:8080 \ -e ARGOCD_BASE_URL=https://argocd.example.com \ -e MCP_TRANSPORT=http \ -e AUTH_MODE=oauth \ -e DEX_CLIENT_ID=argo-cd-cli \ -e SERVER_BASE_URL=http://localhost:8080 \ ghcr.io/matthisholleville/argocd-mcp:latest
claude mcp add --transport http --callback-port 9382 argocd http://localhost:8080/mcp

Then run/mcpinside Claude Code to authenticate via the browser.

Claude Desktop requires a publicly accessible URL (the OAuth redirect goes throughclaude.ai). Expose the server via a reverse proxy or ngrok, then setSERVER_BASE_URLaccordingly. Inoidcmode that callback is not a loopback URL, so it also has to be allowlisted:OIDC_ALLOWED_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback.

Add the public URL as a remote MCP server inSettings > Connectors(e.g.https://mcp.example.com/mcp). Claude Desktop handles the OAuth flow automatically.

Theargo-cd-cliDex client needs the callback URLs for your MCP clients registered as redirect URIs. Add astaticClientsoverride in your ArgoCDdex.config:

staticClients: - id: argo-cd-cli name: Argo CD CLI public: true redirectURIs: - http://localhost - http://localhost:8085/auth/callback - http://localhost:9382/callback - https://claude.ai/api/mcp/auth_callback

ArgoCD auto-registersargo-cd-cliat startup and prepends it to the client list. Dex uses the last definition when there are duplicate IDs, so our override wins safely (ref).

Note: Theargo-cd-cliclient is public (no secret), so this override is safe — unlike overridingargo-cdwhich has an internal secret (ref).

- The MCP server acts as an OAuth proxy to ArgoCD's Dex
- Uses theargo-cd-clipublic client (no secret needed)
- The Dexid_token(withaud: argo-cd-cli) is swapped into theaccess_tokenfield and forwarded as Bearer to ArgoCD
- ArgoCD validates the token against Dex's JWKS and appliesper-user RBAC
- Each user only sees the applications and resources they have access to

OAuth via an external OIDC provider (no Dex)

For the ArgoCD instances configured withoidc.configrather thandex.config: ArgoCD talks to the identity provider directly and runs no Dex, so/api/dex/*answers nothing andAUTH_MODE=oauthcannot complete a flow.AUTH_MODE=oidcproxies to that same provider instead, and per-user RBAC works exactly as in Dex mode.

The two settings are mutually exclusive in ArgoCD (oidc.configwins and Dex is never served), so pick the mode that matches your instance:

docker run -p 8080:8080 \ -e ARGOCD_BASE_URL=https://argocd.example.com \ -e MCP_TRANSPORT=http \ -e AUTH_MODE=oidc \ -e SERVER_BASE_URL=http://localhost:8080 \ ghcr.io/matthisholleville/argocd-mcp:latest

No issuer or client id is needed: the server reads ArgoCD's own/api/v1/settingsat startup and uses the provider it declares, preferringcliClientIDoverclientIDwhen both are set. Override withOIDC_ISSUER+OIDC_CLIENT_ID(both together) to point at something else. Client connection is identical to Dex mode.

The client id must be an audience ArgoCD accepts.ArgoCD validates theaudof the incomingid_tokenagainst itsoidc.configclientID/cliClientID. A token minted for any other client is rejected withfailed to verify the token, which is why the server defaults to the client ArgoCD itself advertises.

Register one redirect URIon that application:

MCP clients bind a loopback port that cannot be pre-registered. Dex accepts any loopback redirect for a public client (RFC 8252), which is whyoauthmode needs no such handling, but most providers require an exact match and would reject it.

So inoidcmode this server is the registered redirect target: it carries the client'sredirect_uriandstatethrough the upstreamstateparameter (HMAC-signed, so neither can be tampered with in the browser), then relays the code back to whichever loopback the client bound. Client callbacks need no registration at all.

Because the provider now only ever sees this server's callback, its own redirect-URI allowlist no longer constrains where an authorization code can end up, and this server has to do that itself. It accepts loopback redirects (RFC 8252, what MCP clients bind) and rejects everything else; a client that calls back to a fixed public URL, e.g.https://claude.ai/api/mcp/auth_callbackfor the hosted claude.ai integration, has to be listed inOIDC_ALLOWED_REDIRECT_URIS. Signing alone would not be enough: the signature proves this server minted the state, not that the target is safe.

SetOIDC_PROXY_CALLBACK=falseto pass the client'sredirect_uristraight through instead, for a provider that tolerates loopback redirects. Each client callback then has to be registered.

Confidential clients:if the application ArgoCD points at requires a client secret (most web-application types do), pass it asOIDC_CLIENT_SECRET. It is added server-side on the token exchange, so MCP clients still register as public and never see it. Public/PKCE clients (e.g. an application registered specifically for CLI use and referenced by ArgoCD ascliClientID) need no secret.

Groups for RBAC:ArgoCD requests the groups claim through its ownrequestedIDTokenClaims. Providers that only emit a claim on request need the same from this server, otherwise every user lands onpolicy.default:

-e OIDC_REQUESTED_ID_TOKEN_CLAIMS='{"id_token":{"groups":{"essential":true}}}'

Scopes are taken from ArgoCD's ownrequestedScopeswhen they are discovered, since providers disagree on which scopes even exist (neither Entra ID nor Google Workspace hasgroups). They fall back toopenid profile email groupswhen ArgoCD declares none, andOIDC_SCOPESoverrides both.openidis required either way: without it the provider mints noid_token, so startup rejects a scope set that omits it and the token exchange fails with502rather than handing the client an opaque token that would 401 on every ArgoCD call.

Note that nooffline_accessis requested, so there is no refresh token and clients re-authenticate when theid_tokenexpires. Add it throughOIDC_SCOPESfor a provider that accepts it.

- The MCP server acts as an OAuth proxy to the provider, discovered via{issuer}/.well-known/openid-configuration, whoseissuermust match the one requested
- /oauth/callbackis the provider's redirect target, and relays the code to the client's own callback, which is validated against loopback plusOIDC_ALLOWED_REDIRECT_URIS
- The provider'sid_tokenis swapped into theaccess_tokenfield and forwarded as Bearer to ArgoCD
- ArgoCD validates it against the provider's JWKS and appliesper-user RBAC
- Startup fails loudly when ArgoCD advertises a Dex configuration instead, pointing atAUTH_MODE=oauth
- The callback state is signed with a per-process key unlessOIDC_STATE_KEYis set, so running more than one replica requires that shared key

Enable Ollama-powered vector search for better results on natural language queries:

docker compose up --build -d # Starts Ollama + argocd-mcp with embeddings

SetEMBEDDINGS_ENABLED=true,OLLAMA_URL, andEMBEDDINGS_MODEL(defaults tonomic-embed-text).

SetDISABLE_WRITE=trueto prevent any disruptive action on your cluster. When enabled:

- Write endpoints are hiddenPOST,PUT,PATCH,DELETEoperations are filtered out from the search index, so the LLM never discovers them.
- Write execution is blocked— even if a caller manually crafts anexecute_operationrequest with a write method, it is rejected.
- Read operations work normallyGET,HEAD,OPTIONSare unaffected.

This is ideal for production environments, demos, or any setup where you want LLMs to observe but never modify your ArgoCD resources.

# Claude Code claude mcp add argocd -s user -- \ docker run --rm -i \ -e ARGOCD_BASE_URL=https://argocd.example.com \ -e ARGOCD_TOKEN=your-token \ -e DISABLE_WRITE=true \ ghcr.io/matthisholleville/argocd-mcp:latest

SetALLOWED_RESOURCESto restrict which ArgoCD resource types the LLM can discover and call. This filters both search resultsandblocks execution of out-of-scope endpoints.

# Only expose application and version endpoints ALLOWED_RESOURCES=ApplicationService,VersionService
# Read-only access to applications only DISABLE_WRITE=true ALLOWED_RESOURCES=ApplicationService

Available resource tags (from ArgoCD's OpenAPI spec):

Matching is case-insensitive (applicationserviceworks).

SetTOOL_MODE=generatedto create one MCP tool per ArgoCD endpoint at startup. Instead of searching then executing, the LLM calls typed tools directly:

# Claude Code claude mcp add argocd -s user -- \ docker run --rm -i \ -e ARGOCD_BASE_URL=https://argocd.example.com \ -e ARGOCD_TOKEN=your-token \ -e TOOL_MODE=generated \ ghcr.io/matthisholleville/argocd-mcp:latest

Each endpoint'soperationIdis converted to a snake_case tool name withargocd_prefix:

Parameters are typed individually — no raw JSON needed for common cases:

argocd_application_sync( name: "frontend" ← path param (required) revision: "HEAD" ← body param, flattened dryRun: true ← body param, flattened strategy: '{"apply":{}}' ← nested object stays JSON string )

Tools are annotated with MCP hints (readOnlyHint,destructiveHint,idempotentHint) so clients like Claude Desktop categorize them correctly (read vs write/delete).

DISABLE_WRITEandALLOWED_RESOURCESare enforced at startup — forbidden tools are simply not generated. The LLM cannot even see them.

Protect ArgoCD from excessive API calls by settingRATE_LIMIT. Onlyexecute_operationis rate limited — search is local and not affected.

RATE_LIMIT=10 # 10 requests/sec per user RATE_LIMIT_BURST=20 # allow short bursts up to 20

Rate limiting uses atoken bucket per user. Each user gets a bucket that refills atRATE_LIMITtokens per second, with a maximum ofRATE_LIMIT_BURSTtokens. When the bucket is empty, requests are rejected until tokens refill.

Note: In static token mode, an aggressive LLM can starve other clients. Prefer OAuth mode in multi-user production setups.

- The callnever reaches ArgoCD— rejected before the proxy
- An audit log entry is emitted withblocked: true
- The LLM receives a clear error:"rate limit exceeded: too many requests, please slow down"

IfRATE_LIMIT_BURSTis not set, it defaults to theRATE_LIMITvalue. SetRATE_LIMIT=0(or omit it) to disable rate limiting entirely.

Pre-packaged workflows for common ArgoCD operations. MCP clients (Claude Desktop, Cursor) show these as selectable prompts in their UI.

Each prompt guides the LLM through a step-by-step workflow usingsearch_operationsandexecute_operation. No additional tools are needed.

Audit logging isenabled by default. Everysearch_operationsandexecute_operationcall emits a structured JSON log entry to stderr:

{"time":"2026-03-22T10:00:00Z","level":"INFO","msg":"audit","tool":"execute_operation","method":"GET","path":"/api/v1/applications","blocked":false,"duration_ms":142,"status_code":200,"user":"alice@example.com"}

- toolsearch_operationsorexecute_operation
- user— email from the OAuth token (empty in static token mode)
- method / path— the ArgoCD API call (execute) orquery(search)
- status_code— upstream HTTP response code
- blockedtrueif the call was rejected byDISABLE_WRITEorALLOWED_RESOURCES
- duration_ms— round-trip time in milliseconds
- error— error message (logged at ERROR level when present)

Prometheus metrics are exposed atGET /metricswheneverMCP_TRANSPORT=http(there is no HTTP surface instdiomode, so nothing to scrape there). Unlike audit logging, metrics arealways onand cannot be disabled: they carry no user identity, only a boundedtool/statuslabel set, so there's no volume or privacy reason to turn them off.

toolissearch_operations/execute_operationin the default search mode, or the generated tool name (e.g.argocd_application_sync) inTOOL_MODE=generated.

curl http://localhost:8080/metrics | grep mcp_tool_
No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.