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 • 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-mcp takes a different approach, inspired by Cloudflare's MCP server which covers 2500+ endpoints with only 2 tools. It reads ArgoCD's OpenAPI spec at startup and exposes every endpoint through just 2 tools: search and execute. New ArgoCD version? Restart the server. Done.
- 103+ endpoints from ArgoCD's OpenAPI spec, zero hardcoded handlers
- Two tool modes:
search(2 meta-tools) orgenerated(1 typed tool per endpoint) - Works with Claude Desktop, Claude Code, Cursor, or any MCP client
- No code per endpoint — the OpenAPI spec is the source of truth
- Two auth modes: static token or OAuth via ArgoCD Dex (per-user RBAC)
- Read-only mode — disable all write operations with a single flag
- Resource scoping — restrict which ArgoCD resources are exposed with
ALLOWED_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 search via 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:
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 calls argocd_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]
Which mode to choose?
| Search | Generated | |
|---|---|---|
| Tools registered | 2 | 103+ |
| LLM round-trips | 2 (search → execute) | 1 (direct call) |
| Parameter typing | Raw JSON strings | Typed individual params |
| Context usage | Low (~200 tokens) | Higher (mitigated by client deferred loading) |
| Best for | Lightweight clients, constrained context | Claude Code, Claude Desktop, Cursor |
Clients like Claude Code and Claude Desktop support deferred 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-tokenSee all configuration options in charts/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:latestAdd 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 Dex id_token is forwarded to ArgoCD, which applies its RBAC policies per user.
Step 1: Start the server
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:latestStep 2: Connect your MCP client
claude mcp add --transport http --callback-port 9382 argocd http://localhost:8080/mcpThen run /mcp inside Claude Code to authenticate via the browser.
Claude Desktop requires a publicly accessible URL (the OAuth redirect goes through claude.ai). Expose the server via a reverse proxy or ngrok, then set SERVER_BASE_URL accordingly.
Add the public URL as a remote MCP server in Settings > Connectors (e.g. https://mcp.example.com/mcp). Claude Desktop handles the OAuth flow automatically.
Required: ArgoCD Dex configuration
The argo-cd-cli Dex client needs the callback URLs for your MCP clients registered as redirect URIs. Add a staticClients override in your ArgoCD dex.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| Redirect URI | Used by |
|---|---|
http://localhost |
ArgoCD CLI (argocd login --sso) |
http://localhost:8085/auth/callback |
ArgoCD CLI (legacy) |
http://localhost:9382/callback |
Claude Code (--callback-port 9382) |
https://claude.ai/api/mcp/auth_callback |
Claude Desktop |
ArgoCD auto-registers argo-cd-cli at 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: The
argo-cd-cliclient is public (no secret), so this override is safe — unlike overridingargo-cdwhich has an internal secret (ref).
How it works under the hood:
- The MCP server acts as an OAuth proxy to ArgoCD's Dex
- Uses the
argo-cd-clipublic client (no secret needed) - The Dex
id_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 applies per-user RBAC
- Each user only sees the applications and resources they have access to
If your ArgoCD instance sits behind Google Cloud Identity-Aware Proxy, every request from the MCP server to ArgoCD is intercepted by IAP and rejected with 401 Invalid IAP credentials: empty token unless it carries a Google-signed OIDC token. This is separate from the ArgoCD/Dex auth — IAP guards the network edge, ArgoCD guards the API.
Set IAP_AUDIENCE to enable IAP support. The server then signs every server-to-server call (spec fetch, Dex token exchange in OAuth mode, and all ArgoCD API calls) with a Google OIDC token in the Proxy-Authorization header. IAP validates and strips that header, and forwards the Authorization header (your ArgoCD token or Dex id_token) to ArgoCD untouched.
docker run -d \
-e ARGOCD_BASE_URL=https://argocd.example.com \
-e ARGOCD_TOKEN=xxx \
-e IAP_AUDIENCE=1234567890-abc.apps.googleusercontent.com \
-e GOOGLE_APPLICATION_CREDENTIALS=/creds/sa.json \
-v /path/to/sa.json:/creds/sa.json:ro \
...IAP_AUDIENCE— the value IAP expects in the token'saudclaim. Usually the IAP OAuth 2.0 client ID (ends in.apps.googleusercontent.com), or the IAP-secured resource URL, depending on your IAP configuration.- Credentials — resolved via Application Default Credentials: a service-account key (
GOOGLE_APPLICATION_CREDENTIALS), GKE workload identity, or the GCE metadata server. The service account needs the IAP-secured Web App User role on the resource. - Works with both
AUTH_MODE=tokenandAUTH_MODE=oauth. LeaveIAP_AUDIENCEunset to disable (default).
Enable Ollama-powered vector search for better results on natural language queries:
docker compose up --build -d # Starts Ollama + argocd-mcp with embeddingsSet EMBEDDINGS_ENABLED=true, OLLAMA_URL, and EMBEDDINGS_MODEL (defaults to nomic-embed-text).
Set DISABLE_WRITE=true to prevent any disruptive action on your cluster. When enabled:
- Write endpoints are hidden —
POST,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 an
execute_operationrequest with a write method, it is rejected. - Read operations work normally —
GET,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:latestSet ALLOWED_RESOURCES to restrict which ArgoCD resource types the LLM can discover and call. This filters both search results and blocks execution of out-of-scope endpoints.
# Only expose application and version endpoints
ALLOWED_RESOURCES=ApplicationService,VersionServiceComposes with DISABLE_WRITE:
# Read-only access to applications only
DISABLE_WRITE=true
ALLOWED_RESOURCES=ApplicationServiceAvailable resource tags (from ArgoCD's OpenAPI spec):
| Tag | Endpoints |
|---|---|
AccountService |
6 |
ApplicationService |
31 |
ApplicationSetService |
6 |
CertificateService |
3 |
ClusterService |
7 |
GPGKeyService |
4 |
NotificationService |
3 |
ProjectService |
12 |
RepoCredsService |
8 |
RepositoryService |
17 |
SessionService |
3 |
SettingsService |
2 |
VersionService |
1 |
Matching is case-insensitive (applicationservice works).
Set TOOL_MODE=generated to 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:latestHow generated tools work
Each endpoint's operationId is converted to a snake_case tool name with argocd_ prefix:
| operationId | Tool name |
|---|---|
ApplicationService_Sync |
argocd_application_sync |
ClusterService_Get |
argocd_cluster_get |
ApplicationSetService_List |
argocd_application_set_list |
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_WRITE and ALLOWED_RESOURCES are enforced at startup — forbidden tools are simply not generated. The LLM cannot even see them.
Protect ArgoCD from excessive API calls by setting RATE_LIMIT. Only execute_operation is 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 20How rate limiting works
Rate limiting uses a token bucket per user. Each user gets a bucket that refills at RATE_LIMIT tokens per second, with a maximum of RATE_LIMIT_BURST tokens. When the bucket is empty, requests are rejected until tokens refill.
| Auth mode | Bucket key | Behavior |
|---|---|---|
| OAuth | User email from JWT | Each user has an independent limit |
| Static token | Shared "static-token" key |
All clients share one bucket |
Note: In static token mode, an aggressive LLM can starve other clients. Prefer OAuth mode in multi-user production setups.
When a request is rate limited:
- The call never reaches ArgoCD — rejected before the proxy
- An audit log entry is emitted with
blocked: true - The LLM receives a clear error:
"rate limit exceeded: too many requests, please slow down"
If RATE_LIMIT_BURST is not set, it defaults to the RATE_LIMIT value. Set RATE_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.
| Prompt | Description | Arguments |
|---|---|---|
unhealthy-apps |
Find all apps with degraded health or out-of-sync status | — |
sync-status |
Dashboard-style overview of all apps | — |
app-diff |
Show what would change on sync | appName (required) |
rollback |
Show history and rollback to a previous revision | appName (required) |
app-logs |
Fetch and analyze container logs | appName (required), container (optional) |
Each prompt guides the LLM through a step-by-step workflow using search_operations and execute_operation. No additional tools are needed.
Audit logging is enabled by default. Every search_operations and execute_operation call 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"}Each entry includes:
- tool —
search_operationsorexecute_operation - user — email from the OAuth token (empty in static token mode)
- method / path — the ArgoCD API call (execute) or query (search)
- status_code — upstream HTTP response code
- blocked —
trueif the call was rejected byDISABLE_WRITEorALLOWED_RESOURCES - duration_ms — round-trip time in milliseconds
- error — error message (logged at ERROR level when present)
Set AUDIT_LOG=false to disable.
| Variable | Required | Default | Description |
|---|---|---|---|
ARGOCD_BASE_URL |
Yes | ArgoCD server URL | |
ARGOCD_TOKEN |
When AUTH_MODE=token |
ArgoCD API token | |
AUTH_MODE |
No | token |
token (static) or oauth (Dex SSO) |
DEX_CLIENT_ID |
When AUTH_MODE=oauth |
argo-cd-cli |
Dex client ID |
SERVER_BASE_URL |
When AUTH_MODE=oauth |
http://localhost:8080 |
Public URL of this server |
ARGOCD_SPEC_URL |
No | {base}/swagger.json |
Override spec URL |
MCP_TRANSPORT |
No | stdio |
stdio or http |
MCP_ADDR |
No | :8080 |
HTTP listen address |
ARGOCD_TLS_INSECURE |
No | false |
Skip TLS certificate verification (set true for self-signed certs) |
IAP_AUDIENCE |
No | Google Cloud IAP audience. Set when ArgoCD is behind IAP (see below) | |
GOOGLE_APPLICATION_CREDENTIALS |
No | Service-account key path for IAP; falls back to workload identity / metadata server | |
TOOL_MODE |
No | search |
search (2 meta-tools) or generated (1 tool per endpoint) |
DISABLE_WRITE |
No | false |
Block all write operations (POST, PUT, PATCH, DELETE) |
ALLOWED_RESOURCES |
No | Comma-separated list of resource tags to expose (e.g. ApplicationService,VersionService) |
|
RATE_LIMIT |
No | 0 (disabled) |
Max execute_operation requests per second per user |
RATE_LIMIT_BURST |
No | same as RATE_LIMIT |
Max burst size before throttling |
AUDIT_LOG |
No | true |
Structured JSON audit log for every tool call |
EMBEDDINGS_ENABLED |
No | false |
Enable Ollama vector search |
OLLAMA_URL |
No | http://localhost:11434/api |
Ollama API URL |
EMBEDDINGS_MODEL |
No | nomic-embed-text |
Ollama embedding model |
make build
ARGOCD_BASE_URL=https://argocd.example.com ARGOCD_TOKEN=xxx ./bin/argocd-mcpMIT
