@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 +3 -3
- package/dist/index.js +12 -2
- package/dist/server.js +12 -2
- package/package.json +2 -1
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 (
|
|
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
|
-
|
|
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" &&
|
|
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
|
-
|
|
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" &&
|
|
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
|
+
"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)",
|