v0.4.0
Highlights
This release hardens the HTTP and SSE transports so that pihole-mcp can safely be exposed beyond stdio — a per-session token-bucket rate limiter and an Origin/Host validator now wrap every HTTP and SSE request, matching the DNS-rebinding protection that the MCP 2025-11-25 specification recommends and that the reference Go SDK has shipped since early 2026. Defaults protect loopback only, so existing setups stay safe; LAN exposure now needs an explicit PIHOLE_ALLOWED_ORIGINS extension. Alongside the security work, every mutating tool now validates user-supplied domains, URLs, and free-form strings before any Pi-hole API call. A new pihole_config_properties tool surfaces the read-only config keys introduced in Pi-hole FTL v6.6.1 (tool count 73 → 74), and a new slim build tag strips OpenTelemetry support to drop the binary ~45% (17 MB → 9 MB stripped) for users who don't run a tracing backend. Both default and slim builds are now shipped as first-class release artefacts — same six platforms, separate tarball and Docker tag families.
Added
- HTTP and SSE transport hardening — two new middlewares wrap the
httpandssehandlers (stdio is unaffected):- Rate limiting — per-session token bucket keyed by
Mcp-Session-Id(fallback to client IP), default 120 req/min with burstmax(perMinute/4, 30). Configure viaPIHOLE_RATE_LIMIT;0disables. Throttled requests return HTTP 429 withRetry-After: 1. - Origin and Host validation — DNS-rebinding protection per the MCP 2025-11-25 spec recommendation. Configure via
PIHOLE_ALLOWED_ORIGINS(comma-separated). Defaultlocalhost,127.0.0.1,[::1]; the literal*disables (documented as unsafe). MissingOriginis allowed for non-browser clients (LibreChat, custom Go clients). Mismatches return HTTP 403.
- Rate limiting — per-session token bucket keyed by
pihole_config_properties— new tool that lists configuration keys locked as read-only bypihole.tomlor environment variable, with reason and human-readable description. Useful after apihole_config_setrejection to confirm whether a key is intentionally immutable. Requires Pi-hole FTL v6.6.1+; the handler surfaces a friendly fallback error against older releases. Tool count is now 74.- Slim build variant —
go build -tags slim(orjust build-slim) excludes OpenTelemetry support entirely. The slim Linux amd64 binary drops from ~17 MB to ~9 MB stripped (~45% smaller; ~3.5 MB compressed vs ~6 MB). Both default and slim artefacts are now published for every release: tarballs aspihole-mcp-slim_X.Y.Z_*and Docker images as:X.Y.Z-slim/:latest-slim. - Input validation at handler entry for every mutating tool —
pihole_domains_*,pihole_lists_*,pihole_clients_*,pihole_groups_*, andpihole_config_*. Domain names are checked for RFC 1035 compliance (length, labels, no shell metacharacters); list URLs must parse ashttp/https/filewith a non-empty host or path; comments and free-form names are length-capped (1024 / 255 characters). Invalid inputs now return a friendly MCP error before any Pi-hole API call is made, instead of surfacing a raw 400 from the Pi-hole server. format=csvadded topihole_stats_recent_blocked,pihole_stats_query_types,pihole_stats_upstreams,pihole_stats_database_upstreams, andpihole_dhcp_leases. Total CSV-capable tool count is now 15, saving ~30-40% tokens on large tables.
Changed
- The
httpandssetransports now run inside anet/http.Serverconstructed bycmd/pihole-mcp/main.gorather than mcp-go's built-in.Start()helper. This is what allows the middleware chain to wrap the MCP handler. Behaviourally identical for clients that respect the existing graceful-shutdown signal handling. AReadHeaderTimeoutof 10 seconds is now enforced (mitigates slowloris). pihole_history_graph/_history_clients/_history_database/_history_database_clientsdescriptions now lead with the data source ("in-memory" vs "database") and cross-reference each other — removes the cognitive overhead of working out which tool you want from name alone.pihole_network_infodescription clarified to point users topihole_network_routes/pihole_network_interfacesfor richer per-route or per-interface detail.pihole_config_setis now annotatedopenWorldHint: true— the tool can affect DNS resolution and other services system-wide, and the hint surfaces that to MCP clients that gate destructive operations.
Fixed
- Hardened
pihole_network_devicesagainst invalid UTF-8 bytes in the upstreammacVendorfield (Pi-hole FTL upstream issue #2868). Go'sencoding/jsonalready silently replaces non-UTF-8 sequences with U+FFFD during decode, so this MCP server was unaffected — a regression-prevention test is now in place to lock that behaviour in.
Quality
- Fixture suite expanded from 13 → 22 captured Pi-hole API responses.
scripts/refresh-fixtures.shnow also captures the full stats family (top_domains,top_clients,upstreams,query_types,recent_blocked), the fourstats_database_*endpoints, andconfig_properties(skipped on older Pi-hole versions that return an empty body for the endpoint). - New
_RealFixtureshape-validation tests across the stats and auth surfaces. Each runs the handler against the captured response and confirms the handler doesn't crash and emits non-empty output. Hand-written value-assertion mocks remain in place for tests that pin specific numbers.
Dependencies
github.com/mark3labs/mcp-gobumped 0.47.0 → 0.54.0. Brings panic recovery to the SSE message handler, stdio worker, task goroutines, and session hook goroutines; adds a transport-agnosticHandleentry point; adds OpenTelemetry server-side tracing hooks; addsWithStrictInputSchemaDefault. No breaking changes for our usage — everyserver.NewMCPServer,server.NewStreamableHTTPServer, andserver.NewSSEServercall site compiles and passes tests unchanged.golang.org/x/timev0.15.0 added as a direct dependency to back the rate-limit token bucket.
Migration Notes
- HTTP and SSE transports now enforce Origin and Host validation by default. Requests are accepted only when the
Host(andOrigin, if present) header resolves to a host inPIHOLE_ALLOWED_ORIGINS. The default allowlist covers loopback (localhost,127.0.0.1,[::1]) only — if you're exposing pihole-mcp on a LAN or behind a reverse proxy, extend the list:Setexport PIHOLE_ALLOWED_ORIGINS="localhost,127.0.0.1,[::1],pihole-mcp.lan"
PIHOLE_ALLOWED_ORIGINS=*to disable the check entirely (only when behind a proxy doing its own access control). stdio is unaffected. - Per-session rate limiting is on by default at 120 req/min with burst 30. If your client legitimately exceeds that during batch refreshes, raise
PIHOLE_RATE_LIMIT(e.g.600) or setPIHOLE_RATE_LIMIT=0to disable. - Slim build variant is opt-in by archive or tag name — defaults are unchanged. To pull the lean binary, grab
pihole-mcp-slim_0.4.0_*.tar.gzfrom the release assets or theghcr.io/hexamatic/pihole-mcp:0.4.0-slimDocker tag.OTEL_EXPORTER_OTLP_ENDPOINTis silently ignored in slim builds.
Installation
Go install:
go install github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.4.0
For the slim variant (no OpenTelemetry): go install -tags slim github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.4.0
Docker (multi-arch):
docker pull ghcr.io/hexamatic/pihole-mcp:0.4.0 # default (includes OpenTelemetry)
docker pull ghcr.io/hexamatic/pihole-mcp:0.4.0-slim # slim (~45% smaller, no OTel)
Binary download: grab the archive for your platform from the release assets — pihole-mcp_0.4.0_{os}_{arch}.tar.gz for the default build, pihole-mcp-slim_0.4.0_{os}_{arch}.tar.gz for the slim build.
Requirements
- Pi-hole v6.6+ with the REST API enabled (v6.6.1+ for
pihole_config_properties) - An admin password or application password
Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
PIHOLE_URL |
Yes | — | Pi-hole base URL |
PIHOLE_PASSWORD |
Yes | — | Admin or application password |
PIHOLE_REQUEST_TIMEOUT |
No | 30s |
HTTP request timeout |
PIHOLE_RATE_LIMIT |
No | 120 |
Per-session requests/min cap on HTTP/SSE transports; 0 disables |
PIHOLE_ALLOWED_ORIGINS |
No | localhost,127.0.0.1,[::1] |
Origin/Host allowlist for HTTP/SSE transports; * disables (unsafe) |
OTEL_EXPORTER_OTLP_ENDPOINT |
No | — | OpenTelemetry endpoint (enables tracing; ignored in slim builds) |
See the README for client-specific setup guides (Claude Desktop, Cursor, Windsurf, VS Code, Cline) and the Security section for the transport hardening details.
Full Changelog: v0.3.0...v0.4.0