@yawlabs/caddy-mcp 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,7 +80,7 @@ That's it. Now ask your AI assistant:
80
80
  |---|---|---|
81
81
  | `CADDY_ADMIN_URL` | `http://localhost:2019` | Caddy admin API URL. Set to `http://caddy:2019` inside Docker, or an https URL for remote admin. |
82
82
  | `CADDY_API_TOKEN` | (none) | Optional Bearer token for authenticated admin endpoints. Only needed if you've configured Caddy with auth. |
83
- | `CADDY_MAX_RETRIES` | `2` | Number of retries on transient failures (5xx, network errors). 4xx and 412 never retry. Hard-capped at 5. Set to `0` to disable. |
83
+ | `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. Set to `0` to disable. |
84
84
  | `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. |
85
85
 
86
86
  **Alternate MCP clients:**
@@ -134,7 +134,7 @@ Browsable read-only data — MCP clients can fetch these directly without a tool
134
134
  - `caddy://config` — Current full Caddy JSON configuration.
135
135
  - `caddy://servers` — Summary of all configured HTTP servers.
136
136
  - `caddy://upstreams` — Reverse proxy upstream health status.
137
- - `caddy://metrics` — Prometheus metrics (text exposition format).
137
+ - `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.
138
138
 
139
139
  ## Examples
140
140
 
@@ -234,7 +234,7 @@ npm install
234
234
  npm run lint # Biome check
235
235
  npm run lint:fix # Auto-fix
236
236
  npm run build # tsup bundle
237
- npm test # Vitest (150 unit tests; +8 live-Caddy integration tests gated by CADDY_MCP_INTEGRATION=1)
237
+ npm test # Vitest (182 unit tests; +8 live-Caddy integration tests gated by CADDY_MCP_INTEGRATION=1)
238
238
  npm run typecheck # tsc --noEmit
239
239
  ```
240
240
 
package/dist/index.js CHANGED
@@ -24,12 +24,18 @@ function setEtag(path, etag) {
24
24
  function getBaseUrl() {
25
25
  return (process.env.CADDY_ADMIN_URL || DEFAULT_URL).replace(/\/+$/, "");
26
26
  }
27
+ var warnedRetryClamp = false;
27
28
  function getMaxRetries() {
28
29
  const raw = process.env.CADDY_MAX_RETRIES;
29
30
  if (raw === void 0) return 2;
30
31
  const n = Number(raw);
31
32
  if (!Number.isFinite(n) || n < 0) return 2;
32
- return Math.min(Math.floor(n), RETRY_HARD_CAP);
33
+ const floored = Math.floor(n);
34
+ if (floored > RETRY_HARD_CAP && !warnedRetryClamp) {
35
+ warnedRetryClamp = true;
36
+ console.error(`caddy-mcp: CADDY_MAX_RETRIES=${raw} exceeds hard cap; using ${RETRY_HARD_CAP}.`);
37
+ }
38
+ return Math.min(floored, RETRY_HARD_CAP);
33
39
  }
34
40
  function getHeaders(contentType) {
35
41
  const headers = {};
@@ -257,11 +263,12 @@ function formatResult(res) {
257
263
  }
258
264
 
259
265
  // src/tools/operational.ts
266
+ var HTTPS_PORT_RE = /:443(?:\D|$)/;
260
267
  function describeServer(raw) {
261
268
  const listen = Array.isArray(raw.listen) ? raw.listen : [];
262
269
  const routes = Array.isArray(raw.routes) ? raw.routes : [];
263
270
  const hasExplicitTls = !!raw.tls_connection_policies;
264
- const listensHttps = listen.some((l) => typeof l === "string" && l.includes(":443"));
271
+ const listensHttps = listen.some((l) => typeof l === "string" && HTTPS_PORT_RE.test(l));
265
272
  const tls = hasExplicitTls ? "enabled" : listensHttps ? "auto (HTTPS)" : "off (HTTP only)";
266
273
  const listenStr = listen.length > 0 ? listen.map(String).join(", ") : "default";
267
274
  return `${routes.length} route(s), listen: ${listenStr}, TLS: ${tls}`;
@@ -835,6 +842,9 @@ function registerRouteTools(server) {
835
842
  ]
836
843
  };
837
844
  }
845
+ if (isParentMissing(putRes)) {
846
+ return serverNotFoundError(srv);
847
+ }
838
848
  return formatResult(putRes);
839
849
  }
840
850
  if (!isUnknownId(existing)) {
package/dist/server.js CHANGED
@@ -22,12 +22,18 @@ function setEtag(path, etag) {
22
22
  function getBaseUrl() {
23
23
  return (process.env.CADDY_ADMIN_URL || DEFAULT_URL).replace(/\/+$/, "");
24
24
  }
25
+ var warnedRetryClamp = false;
25
26
  function getMaxRetries() {
26
27
  const raw = process.env.CADDY_MAX_RETRIES;
27
28
  if (raw === void 0) return 2;
28
29
  const n = Number(raw);
29
30
  if (!Number.isFinite(n) || n < 0) return 2;
30
- return Math.min(Math.floor(n), RETRY_HARD_CAP);
31
+ const floored = Math.floor(n);
32
+ if (floored > RETRY_HARD_CAP && !warnedRetryClamp) {
33
+ warnedRetryClamp = true;
34
+ console.error(`caddy-mcp: CADDY_MAX_RETRIES=${raw} exceeds hard cap; using ${RETRY_HARD_CAP}.`);
35
+ }
36
+ return Math.min(floored, RETRY_HARD_CAP);
31
37
  }
32
38
  function getHeaders(contentType) {
33
39
  const headers = {};
@@ -255,11 +261,12 @@ function formatResult(res) {
255
261
  }
256
262
 
257
263
  // src/tools/operational.ts
264
+ var HTTPS_PORT_RE = /:443(?:\D|$)/;
258
265
  function describeServer(raw) {
259
266
  const listen = Array.isArray(raw.listen) ? raw.listen : [];
260
267
  const routes = Array.isArray(raw.routes) ? raw.routes : [];
261
268
  const hasExplicitTls = !!raw.tls_connection_policies;
262
- const listensHttps = listen.some((l) => typeof l === "string" && l.includes(":443"));
269
+ const listensHttps = listen.some((l) => typeof l === "string" && HTTPS_PORT_RE.test(l));
263
270
  const tls = hasExplicitTls ? "enabled" : listensHttps ? "auto (HTTPS)" : "off (HTTP only)";
264
271
  const listenStr = listen.length > 0 ? listen.map(String).join(", ") : "default";
265
272
  return `${routes.length} route(s), listen: ${listenStr}, TLS: ${tls}`;
@@ -833,6 +840,9 @@ function registerRouteTools(server) {
833
840
  ]
834
841
  };
835
842
  }
843
+ if (isParentMissing(putRes)) {
844
+ return serverNotFoundError(srv);
845
+ }
836
846
  return formatResult(putRes);
837
847
  }
838
848
  if (!isUnknownId(existing)) {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@yawlabs/caddy-mcp",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
+ "mcpName": "io.github.YawLabs/caddy-mcp",
4
5
  "description": "MCP server for managing Caddy web servers via the admin API",
5
6
  "license": "MIT",
6
7
  "author": "Yaw Labs <contact@yaw.sh> (https://yaw.sh)",