# Caddy Mcp

> Caddy admin API MCP server -- complete coverage of config, routes, reverse proxies, TLS, PKI, and metrics, with ETag-safe concurrent writes and idempotent-by-default mutations.

- **Type:** MCP server
- **Install:** `agentstack add mcp-yawlabs-caddy-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [YawLabs](https://agentstack.voostack.com/s/yawlabs)
- **Installs:** 0
- **Category:** [Data & Analytics](https://agentstack.voostack.com/c/data-and-analytics)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [YawLabs](https://github.com/YawLabs)
- **Source:** https://github.com/YawLabs/caddy-mcp

## Install

```sh
agentstack add mcp-yawlabs-caddy-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# @yawlabs/caddy-mcp

[](https://www.npmjs.com/package/@yawlabs/caddy-mcp)
[](https://opensource.org/licenses/MIT)
[](https://github.com/YawLabs/caddy-mcp/stargazers)

**Manage Caddy web servers from Claude Code, Cursor, and any MCP client.** 18 tools + 4 resources covering every endpoint of Caddy's admin API — config, routes, reverse proxies, TLS, PKI, metrics, snapshots.

Built and maintained by [Yaw Labs](https://yaw.sh).

[](https://yaw.sh/mcp/install?name=Caddy&command=npx&args=-y%2C%40yawlabs%2Fcaddy-mcp&description=Manage%20Caddy%20web%20servers%20-%20config%2C%20routes%2C%20TLS%2C%20PKI&source=https%3A%2F%2Fgithub.com%2FYawLabs%2Fcaddy-mcp)

One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.

## Why this one?

Other Caddy MCP servers wrap half the admin API and silently swallow errors. This one doesn't.

- **Complete admin API coverage** — every documented endpoint: `/load`, `/config/*`, `/id/*`, `/stop`, `/adapt`, `/pki/ca/*`, `/reverse_proxy/upstreams`, `/metrics`. No placeholder tools that 404.
- **Safe concurrent writes** — uses ETags (`If-Match`) so your changes never silently overwrite someone else's. Surfaces `HTTP 412 Precondition Failed` as a clear message, not a cryptic error.
- **Safe-by-default mutations** — `caddy_config_set` defaults to idempotent `overwrite` (PATCH), not `append` (POST). Calling twice doesn't duplicate your route.
- **Defensive parsing** — `caddy_list_routes` never crashes on malformed config, even if routes are null, handlers are strings, or matchers are non-arrays. Regression-tested.
- **No leaked credentials in errors** — if `CADDY_ADMIN_URL` contains a token in the path/query, the connect-failed message shows only the origin.
- **Fallback error surfacing** — when a TLS write PATCH fails and the POST fallback also fails, both error bodies are returned so you know what actually went wrong.
- **Tool annotations** — every tool declares `readOnlyHint`, `destructiveHint`, and `idempotentHint`, so MCP clients can skip confirmations for safe ops.
- **Instant startup** — ships as a single bundle with two runtime deps (the MCP SDK + Zod). No 5-minute `node_modules` install.
- **Input hardening** — adapter names, `@id` values, server names, and CA ids are all regex-validated with length caps. Blocks CRLF header injection and ReDoS.

## Quick start

**1. Enable the Caddy admin API**

Caddy ships with the admin API enabled on `localhost:2019` by default. If you're running Caddy in Docker or on a remote host, expose it via `CADDY_ADMIN_URL`.

**2. Create `.mcp.json` in your project root**

macOS / Linux / WSL:

```json
{
  "mcpServers": {
    "caddy": {
      "command": "npx",
      "args": ["-y", "@yawlabs/caddy-mcp@latest"]
    }
  }
}
```

Windows:

```json
{
  "mcpServers": {
    "caddy": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@yawlabs/caddy-mcp@latest"]
    }
  }
}
```

> **Why the extra step on Windows?** Since Node 20, `child_process.spawn` cannot directly execute `.cmd` files (that's what `npx` is on Windows). Wrapping with `cmd /c` is the standard workaround. This file is safe to commit — it contains no secrets.

**3. Restart and approve**

Restart Claude Code (or your MCP client) and approve the Caddy MCP server when prompted.

That's it. Now ask your AI assistant:

> "Proxy api.local to localhost:3000"
>
> "What routes are configured on srv0?"
>
> "Show me the Prometheus metrics"

## Configuration

| Environment variable | Default | Description |
|---|---|---|
| `CADDY_ADMIN_URL` | `http://localhost:2019` | Caddy admin API URL. Set to `http://caddy:2019` inside Docker, or an https URL for remote admin. |
| `CADDY_API_TOKEN` | (none) | Optional Bearer token for authenticated admin endpoints. Only needed if you've configured Caddy with auth. |
| `CADDY_MAX_RETRIES` | `2` | Number of retries on transient failures (5xx, network errors). 4xx and 412 never retry. POSTs to `/config/*` and `/id/*` also skip retry (non-idempotent appends/creates -- retrying could duplicate routes or 409 a half-applied create). POSTs to `/load`, `/adapt`, `/stop` still retry. Hard-capped at 5; values above the cap log a one-time stderr notice so the clamp is visible. Set to `0` to disable. |
| `CADDY_TIMEOUT` | `10000` | Timeout in ms for all admin API requests except `/load` (which uses `CADDY_LOAD_TIMEOUT`). Non-numeric, ` "Proxy api.example.com to my app on port 3000"
→ caddy_reverse_proxy({ from: "api.example.com", to: ["localhost:3000"] })
```

### Idempotent reverse proxy (safe to re-run from automation)

```
> "Make sure api.example.com points at localhost:3000, with a stable id"
→ caddy_reverse_proxy({ from: "api.example.com", to: ["localhost:3000"], id: "api-prod" })
  # First call creates the route under @id="api-prod".
  # Subsequent calls with the same id REPLACE in place — no duplicate routes.
  # Refuses with a clear error if "api-prod" is already in use by a non-route
  # config object (TLS issuer, server, etc.) — @ids are config-global in Caddy.
```

### Filter Prometheus metrics

```
> "Just the HTTP request metrics, please"
→ caddy_metrics({ filter: "http_requests" })
  # Keeps sample lines whose metric name contains "http_requests",
  # plus their `# HELP` / `# TYPE` lines. Drops the rest.
```

### Preview a Caddyfile before applying it

```
> "Convert this Caddyfile to JSON so I can review it:
   example.com {
     reverse_proxy localhost:8080
   }"
→ caddy_adapt({ config: "..." })
```

### Diagnose slow routes

```
> "Fetch Prometheus metrics and tell me which route is slowest"
→ caddy_metrics()
```

### Safely update a route by @id

```
> "Update the route with @id 'api-v2' to point to the new backend"
→ caddy_config_by_id({ id: "api-v2", action: "set", value: {...} })
  # Uses ETags — you'll get HTTP 412 if someone else changed it first
```

### Atomic deploy

```
> "Replace the whole config with this Caddyfile"
→ caddy_adapt({ config: "..." })  # validate first
→ caddy_load({ config: adaptedJson })  # apply atomically
```

## Troubleshooting

**"Cannot connect to Caddy admin API"**

- Make sure Caddy is running. `caddy run` or `systemctl status caddy`.
- Check the admin endpoint. Default is `http://localhost:2019`. If Caddy is in Docker, use the container hostname.
- Set `CADDY_ADMIN_URL` in your MCP config `env` to match.

**"HTTP 412 Precondition Failed"**

- Someone (or something) changed the config between your read and your write.
- The cached ETag has been invalidated. Re-read the config and retry.

**"HTTP 403" on /load or /config writes**

- You have `admin.listen` or `admin.origins` restrictions set in your Caddy config, or you're missing an `Authorization` header.
- Set `CADDY_API_TOKEN` in your MCP config env if Caddy expects a Bearer token.

**Windows: MCP server doesn't start**

- Use the `cmd /c npx ...` pattern from the Quick start section. Node 20+ can't spawn `.cmd` files directly.

## Requirements

- Node.js 20+
- Caddy 2.x with admin API enabled (default: `localhost:2019`)

## Contributing

```bash
git clone https://github.com/YawLabs/caddy-mcp.git
cd caddy-mcp
npm install
npm run lint       # Biome check
npm run lint:fix   # Auto-fix
npm run build      # tsup bundle
npm test           # Vitest (230 unit tests; +8 live-Caddy integration tests gated by CADDY_MCP_INTEGRATION=1)
npm run typecheck  # tsc --noEmit
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow, including release process.

## License

MIT

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [YawLabs](https://github.com/YawLabs)
- **Source:** [YawLabs/caddy-mcp](https://github.com/YawLabs/caddy-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-yawlabs-caddy-mcp
- Seller: https://agentstack.voostack.com/s/yawlabs
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
