Production Argo CD MCP Server Template (TypeScript)
Overview
This template gives you a production-ready Model Context Protocol (MCP) server that bridges AI agents โ such as Claude Desktop, Cursor, or Windsurf โ with your Argo CD GitOps platform. Instead of copy-pasting kubectl commands or clicking through the Argo CD UI, your AI agent can query application health, sync status, Kubernetes resources, and events directly through natural language. The server wraps the official Argo CD REST API and exposes a curated set of safe, read-first tools that make day-to-day GitOps workflows faster.
The template is intentionally conservative. Read operations are enabled by default. The one write operation included โ sync_application โ ships with explicit validation, a dry-run guard, and prominent warnings, because pushing a sync in production is a real-world consequence. Destructive operations such as delete_application are deliberately omitted. This posture makes the server suitable for team environments where not every AI assistant session should carry full Argo CD admin rights.
Who is this for? Platform engineers, SRE teams, and developers who manage Kubernetes workloads with Argo CD and want to give their AI coding assistants real, live context about deployment state โ without standing up a heavy integration or risking accidental changes to production clusters.
What You'll Learn
- How to scaffold a TypeScript MCP server using the official
@modelcontextprotocol/sdkpackage with Streamable HTTP transport - How to protect an MCP endpoint with API key authentication middleware in Express
- How to call an authenticated upstream REST API (Argo CD) from MCP tool handlers with proper token hygiene
- How to define and validate MCP tool input schemas using Zod and convert them to JSON Schema for the SDK
- How to implement structured JSON logging with Pino, including request correlation IDs and configurable log levels
- How to apply express-rate-limit to an MCP HTTP endpoint and return correct 429 responses
- How to write a
/healthendpoint that checks upstream API reachability and returns machine-readable JSON status - How to write Jest + Supertest integration tests that cover auth rejection, health checks, and tool execution
- How to build a multi-stage Docker image that produces a lean production container from a TypeScript source tree
- How to wire up a GitHub Actions CI pipeline with lint, type-check, test, and Docker build steps
- How to manage secrets safely: never logging tokens, never returning them in tool responses, using least-privilege Argo CD tokens
- How to configure MCP clients (Claude Desktop, Cursor, Windsurf, Continue) to talk to a self-hosted HTTP MCP server
Architecture
AI Agent (Claude / Cursor / Windsurf)
โ
โ HTTP POST /mcp (Bearer API Key)
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Express Application โ
โ โ
โ Rate Limiter โ Auth Middleware โ
โ โ โ
โ โผ โ
โ MCP Streamable HTTP Transport โ
โ โ โ
โ โผ โ
โ MCP Server (Tool Router) โ
โ โโโ list_applications โ
โ โโโ get_application โ
โ โโโ get_application_health โ
โ โโโ get_application_sync_status โ
โ โโโ list_application_resources โ
โ โโโ get_application_events โ
โ โโโ refresh_application โ
โ โโโ sync_application โ ๏ธ write โ
โ โ โ
โ Argo CD API Client โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HTTPS + Argo CD API Token
โผ
Argo CD Server (REST API)
โ
โผ
Kubernetes Cluster(s)
Project Structure
argo-cd-mcp-server/
โโโ .env.example
โโโ .gitignore
โโโ eslint.config.js
โโโ package.json
โโโ tsconfig.json
โโโ tsconfig.eslint.json
โโโ src/
โโโ server.ts
โโโ config.ts
โโโ logger.ts
โโโ auth.ts
โโโ health.ts
โโโ __tests__/
โ โโโ auth.test.ts
โโโ argocd/
โ โโโ types.ts
โ โโโ client.ts
โโโ tools/
โโโ index.ts
โโโ listApplications.ts
โโโ getApplication.ts
โโโ getApplicationHealth.ts
โโโ getApplicationSyncStatus.ts
โโโ listApplicationResources.ts
โโโ getApplicationEvents.ts
โโโ refreshApplication.ts
โโโ syncApplication.ts
Prerequisites
- Node.js 20 or later
- npm 10 or later
- A running Argo CD instance (v2.8+) with network access from this server
- An Argo CD API token (see Argo CD Token Setup section in the README)
- Docker (optional, for containerised deployment)
Quick Start
# 1. Clone / copy the template
git clone https://github.com/your-org/argo-cd-mcp-server.git
cd argo-cd-mcp-server
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env โ set ARGOCD_SERVER_URL, ARGOCD_TOKEN, MCP_API_KEY
# 4. Build TypeScript
npm run build
# 5. Start the server
npm start
# 6. Verify health
curl http://localhost:3000/health
# 7. Run tests
npm test
Features
- Streamable HTTP transport โ compatible with all MCP clients that support HTTP-based servers
- API key auth โ every request to
/mcpmust carry a validAuthorization: Bearer <key>header - Argo CD REST API client โ typed, reusable client with token injection and TLS options
- 8 Argo CD tools โ list apps, get app detail, health, sync status, resources, events, refresh, and (opt-in) sync
- Zod input validation โ all tool inputs are validated before any upstream API call is made
- Pino structured logging โ JSON output with correlation IDs, redacted secrets, configurable
LOG_LEVEL - Rate limiting โ configurable via
RATE_LIMIT_WINDOW_MSandRATE_LIMIT_MAX - Health endpoint โ
GET /healthchecks Argo CD API reachability, returns structured JSON - Multi-stage Dockerfile โ builder stage compiles TypeScript; production stage runs only
dist/ - docker-compose.yml โ one-command local stack
- Jest + Supertest tests โ covers health, auth rejection, and tool execution
- GitHub Actions CI โ lint, type-check, test, and Docker build on every push
Works With
- Claude Desktop โ add server to
claude_desktop_config.jsonundermcpServers - Claude Code โ use
claude mcp addwith--transport http - Cursor โ add under Settings โ MCP โ Add Server
- Windsurf โ configure in
mcp_config.json - Continue โ add as an MCP provider in
config.json
Example Claude Desktop configuration
{
"mcpServers": {
"argocd": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/mcp"],
"env": {
"MCP_REMOTE_HEADER_AUTHORIZATION": "Bearer your-mcp-api-key"
}
}
}
}
Configuration
| Variable | Description | Required |
|---|---|---|
PORT | TCP port the Express server listens on | Optional (default 3000) |
LOG_LEVEL | Pino log level: trace debug info warn error | Optional (default info) |
MCP_API_KEY | Secret key that MCP clients must send as Bearer token | Required |
ARGOCD_SERVER_URL | Base URL of your Argo CD server, e.g. https://argocd.example.com | Required |
ARGOCD_TOKEN | Argo CD API token (never logged or returned to clients) | Required |
ARGOCD_INSECURE_TLS | Set true to skip TLS verification (dev/self-signed only) | Optional (default false) |
ARGOCD_NAMESPACE | Default Argo CD namespace filter for queries | Optional (default argocd) |
RATE_LIMIT_WINDOW_MS | Rate limit window in milliseconds | Optional (default 60000) |
RATE_LIMIT_MAX | Max requests per window per IP | Optional (default 60) |
ENABLE_SYNC_TOOL | Set true to enable the sync_application write tool | Optional (default false) |
NODE_ENV | development or production | Optional (default production) |
Deployment
Docker (recommended)
# Build
docker build -t argo-cd-mcp-server:latest .
# Run
docker run -d \
--name argo-cd-mcp \
-p 3000:3000 \
--env-file .env \
argo-cd-mcp-server:latest
Docker Compose (local dev)
docker compose up --build
Railway
npm install -g @railway/cli
railway login
railway new
railway up
# Set env vars in the Railway dashboard
Fly.io
npm install -g flyctl
fly launch --name argo-cd-mcp-server
fly secrets set MCP_API_KEY=xxx ARGOCD_TOKEN=yyy ARGOCD_SERVER_URL=https://argocd.example.com
fly deploy
Argo CD Token Setup
- Log in to Argo CD:
argocd login argocd.example.com - Create a dedicated service account:
argocd account create mcp-server - Grant read-only RBAC in
argocd-rbac-cmConfigMap:
policy.csv: |
p, role:mcp-readonly, applications, get, */*, allow
p, role:mcp-readonly, applications, list, */*, allow
p, role:mcp-readonly, clusters, get, *, allow
g, mcp-server, role:mcp-readonly
- If you need sync, add the sync action to the role:
p, role:mcp-readonly, applications, sync, */*, allow
- Generate the token:
argocd account generate-token --account mcp-server - Copy the token value into
ARGOCD_TOKENin your.env.
โ ๏ธ Never commit the token. Use environment variables or a secrets manager.
Production Checklist
-
MCP_API_KEYis a strong random value (32+ chars), stored in a secrets manager -
ARGOCD_TOKENis scoped to the minimum required RBAC permissions -
ARGOCD_INSECURE_TLSisfalsein production -
ENABLE_SYNC_TOOLisfalseunless your team has reviewed the sync workflow -
LOG_LEVELisinfoorwarnin production (notdebugortrace) - Rate limiting values are tuned for your expected agent call volume
- The container runs as a non-root user (the Dockerfile enforces this)
- Health endpoint is wired up to your load balancer / Kubernetes liveness probe
- TLS is terminated at the ingress layer โ do not expose port 3000 directly
- GitHub Actions CI passes on the
mainbranch before any deploy - Docker image is scanned for CVEs before pushing to production
- Logs are shipped to a centralised log aggregator (Datadog, Loki, etc.)
- Argo CD token rotation procedure is documented and scheduled
-
NODE_ENV=productionis set so stack traces are not leaked in error responses - Resource limits (CPU/memory) are set on the Kubernetes Deployment or Docker container
Testing
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Manual health check
curl http://localhost:3000/health
# Manual MCP call โ list applications
curl -X POST http://localhost:3000/mcp \
-H "Authorization: Bearer your-mcp-api-key" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_applications","arguments":{}}}'
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
401 Unauthorized on /mcp | Wrong or missing MCP_API_KEY | Check Authorization: Bearer <key> header matches .env |
health shows argocd: unreachable | ARGOCD_SERVER_URL wrong or network blocked | Verify URL, firewall, and TLS cert |
403 from Argo CD tools | Token lacks RBAC permissions | Review argocd-rbac-cm policy |
| TLS errors connecting to Argo CD | Self-signed cert | Set ARGOCD_INSECURE_TLS=true for dev only |
sync_application tool not found | ENABLE_SYNC_TOOL not set | Set ENABLE_SYNC_TOOL=true and restart |
| Rate limit 429 | Too many requests | Increase RATE_LIMIT_MAX or reduce agent call frequency |