@yawlabs/caddy-mcp

@yawlabs/caddy-mcp

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

Category
访问服务器

README

@yawlabs/caddy-mcp

npm version License: MIT GitHub stars

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.

Add to Yaw 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 mutationscaddy_config_set defaults to idempotent overwrite (PATCH), not append (POST). Calling twice doesn't duplicate your route.
  • Defensive parsingcaddy_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:

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

Windows:

{
  "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_LOAD_TIMEOUT 60000 Timeout in ms for the /load endpoint; raise for ACME-heavy bring-ups where provisioning many certificates can exceed the default. Non-numeric, <= 0, or fractional values below 1ms fall back to the default.

Alternate MCP clients:

Client Config file
Claude Code .mcp.json (project root) or ~/.claude.json (global)
Claude Desktop ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
Cursor ~/.cursor/mcp.json
Windsurf ~/.codeium/windsurf/mcp_config.json
VS Code .vscode/mcp.json

Use the same JSON block shown above in any of these.

Tools

Config management (6)

  • caddy_config_get — Read config at any JSON path (or the full config).
  • caddy_config_set — Write config at a path. Modes: overwrite (PATCH, default, idempotent), append (POST), insert (PUT, for array positions).
  • caddy_config_delete — Delete config at a path.
  • caddy_config_by_id — Get/set/delete config by @id tag — much easier than navigating deep paths.
  • caddy_load — Replace the entire config atomically. 60-second timeout for cert provisioning. Auto-snapshots the prior config.
  • caddy_revert — Manage config snapshots for rollback. Actions: list, save, apply (confirm-gated). In-memory, last 10.

Route operations (4)

  • caddy_reverse_proxy — Add a reverse proxy in one call: from='api.local' to=['localhost:3000']. Pass an optional id for idempotent writes — repeat calls replace the route in place instead of duplicating.
  • caddy_add_route — Add a route with full match/handle control (any Caddy handler).
  • caddy_remove_route — Remove a route by @id (preferred) or by index. Requires confirm=true.
  • caddy_list_routes — Human-readable route summary. Defensive: never crashes on weird config.

TLS & config conversion (2)

  • caddy_tls — Check or set TLS settings: ACME email, ACME CA URL. PATCH first; on a fresh install, POSTs a minimal config. On an existing config it deep-merges into the issuer path and PUTs the result back, preserving siblings (custom certs, on_demand, additional policies). Refuses with a shape-specific error if the existing structure is unexpected — never clobbers.
  • caddy_adapt — Convert a config in any registered adapter format to Caddy JSON without applying it. caddyfile (built-in, default) plus any adapter module compiled into your Caddy binary — e.g., nginx (caddy-nginx-adapter), yaml (caddy-yaml). Great for previewing or porting from existing configs.

Server operations (6)

  • caddy_status — Connectivity check + config summary (server count, routes, TLS mode).
  • caddy_list_servers — List all HTTP servers with names, addresses, route counts, and TLS status.
  • caddy_upstreams — Reverse proxy backend health.
  • caddy_metrics — Prometheus metrics (request counts, durations, connections, TLS handshakes). Optional filter (substring match on metric name, keeps # HELP / # TYPE lines for retained metrics) and max_lines (default 500) keep responses compact on busy servers.
  • caddy_pki — CA info and certificate chains (default CA: local).
  • caddy_stop — Graceful shutdown. Requires confirm=true to prevent accidents.

Resources

Browsable read-only data — MCP clients can fetch these directly without a tool call:

  • caddy://config — Current full Caddy JSON configuration.
  • caddy://servers — Summary of all configured HTTP servers.
  • caddy://upstreams — Reverse proxy upstream health status.
  • caddy://metrics — Prometheus metrics (text exposition format). Capped at the first 500 lines to keep client context bounded; use the caddy_metrics tool with filter / max_lines for filtered or larger output.

Examples

Add a reverse proxy

> "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

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 (188 unit tests; +8 live-Caddy integration tests gated by CADDY_MCP_INTEGRATION=1)
npm run typecheck  # tsc --noEmit

See CONTRIBUTING.md for the full workflow, including release process.

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选