Releases: hexamatic/pihole-mcp
Release list
v0.8.0
Highlights
This release is about being findable and being verifiable. pihole-mcp is now listed in the official MCP Registry as io.github.hexamatic/pihole-mcp — the index MCP clients and directory aggregators read — so it can be installed by name, with every configuration variable described and the required ones flagged, rather than found by chance on GitHub. Ownership of the listing is proved cryptographically against the published container image on each release.
It is also the first release whose artefacts you can actually check. Keyless cosign signatures, SPDX SBOMs and SLSA build provenance have been wired up since v0.7.0 but never yet exercised by a tag; from v0.8.0 every checksum file, archive and container image ships with them, and SECURITY.md gives you the one-line command to verify each. Behind that, CodeQL now scans both the Go source and the CI workflows, container base images are pinned by digest so a rebuild cannot drift, and patch coverage has become a merge gate rather than a suggestion.
Homebrew users should notice nothing except a smoother first run: the tap now ships a cask instead of a deprecated formula, installs on macOS and Linux exactly as before, and clears the macOS quarantine attribute so the binary no longer trips Gatekeeper.
Added
- Listed in the official MCP Registry as
io.github.hexamatic/pihole-mcp. Clients that support registry install can add pihole-mcp by name and will prompt forPIHOLE_URLandPIHOLE_PASSWORD, with the optional settings (TZ,PIHOLE_TLS_SKIP_VERIFY, timeouts and retries) described alongside their defaults. The listing is published automatically on each tag and points at theghcr.ioimage. - Signed, attested releases. Release artefacts are now signed with keyless cosign (checksum file and Docker images), ship SPDX SBOMs for every archive, and carry SLSA build provenance verifiable with
gh attestation verify. SECURITY.md documents every verification command. The checksum-file signature ships as a single Sigstore bundle,pihole-mcp_0.8.0_SHA256SUMS.bundle, which carries the signature, the signing certificate and the transparency-log inclusion proof together — verify it withcosign verify-blob --bundle(cosign v3 or newer). - OpenSSF Scorecard — a weekly supply-chain security analysis now runs against the repository, publishes its score to scorecard.dev, and feeds the new README badge. All GitHub Actions across every workflow are now pinned by commit SHA (maintained automatically by Dependabot).
Changed
- Homebrew now installs a cask rather than a formula.
brew install hexamatic/tap/pihole-mcpis unchanged and still works on both macOS and Linux; existing installations migrate automatically onbrew upgrade. On macOS the cask now clears the quarantine attribute during install, so the binary no longer trips Gatekeeper on first run. GoReleaser deprecated binary formulae in v2.10 — the cask is the supported form and covers both platforms. - README badge lineup refreshed. The Go Report Card badge has been removed — the service was sunset on 1 July 2026 — and replaced with OpenSSF Scorecard and Go Reference (pkg.go.dev) badges. Its lint-quality role has long been covered by golangci-lint in CI.
Security
- Two dependency advisories resolved —
golang.org/x/textto v0.39.0 (CVE-2026-56852, infinite loop on invalid input) andgolang.org/x/netto v0.56.0 (CVE-2026-46600, panic parsing a malformed SVCB or HTTPS DNS record). Neither was reachable from pihole-mcp's own code paths, so no released version was exploitable through this server; both are fixed regardless. - CodeQL static analysis now runs on every push, every pull request, and weekly, over both the Go source and the GitHub Actions workflows. golangci-lint covers style and a good deal of correctness but is not a taint-tracking engine; CodeQL finds the dataflow issues a linter structurally cannot.
- Container base images are pinned by digest as well as by tag, so a rebuild cannot silently pick up a different
golang:1.26-alpineordistroless/static-debian13. Dependabot maintains the digests. - Patch coverage is now an enforced gate rather than advisory, and
govulncheck,gitleaks, the fuzz smoke and the changelog check are required to pass before merge.
Dependencies
golang.org/x/text0.37.0 → 0.39.0,golang.org/x/net0.55.0 → 0.56.0,golang.org/x/sys0.45.0 → 0.46.0 (see Security above)google.golang.org/grpc1.81.1 → 1.82.1- GitHub Actions:
actions/setup-gov6 → v7,codecov/codecov-actionv5 → v7,actions/attest-build-provenancev3 → v4
Installation
MCP Registry — add by name in any client that supports registry install:
io.github.hexamatic/pihole-mcp
Homebrew (macOS and Linux):
brew install hexamatic/tap/pihole-mcp
Go install:
go install github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.8.0
Docker (multi-arch):
docker pull ghcr.io/hexamatic/pihole-mcp:0.8.0
Binary download: grab the archive for your platform from the release assets. Every archive is checksummed, cosign-signed, and ships an SPDX SBOM and SLSA provenance — see SECURITY.md to verify before you run it.
| Variant | Binary | Download | Docker image |
|---|---|---|---|
| Default | 16.8 MB | 6.4 MB | 18.7 MB |
| Slim (no OpenTelemetry) | 9.6 MB | 3.8 MB | 11.5 MB |
Requirements
- Pi-hole v6.6+ with the REST API enabled (verified against FTL v6.7)
- An admin password or application password
- Docker, if installing via the MCP Registry listing — it points at the container image
No configuration changes in this release. See the README for the full variable reference and client-specific setup guides.
Full Changelog: v0.7.0...v0.8.0
v0.7.0
Highlights
This release makes every timestamp unambiguous. Tool output used to render times with no timezone marker, in whatever zone the server happened to be running in — which, in the published Docker image, silently meant UTC: the distroless base ships no timezone database, so even setting TZ on the container did nothing (#23). Every timestamp now carries an explicit zone marker, and the IANA timezone database is embedded in the binary itself, so TZ=Australia/Adelaide on the container — or on any of the native binaries, Windows included — renders query logs in your local time out of the box. Left unset, output is UTC and says so.
Alongside that: an explicit opt-in for Pi-holes serving self-signed HTTPS certificates (PIHOLE_TLS_SKIP_VERIFY), and a round of hardening that has been on the list since v0.4.0 — coverage reporting, fuzz testing of the input validators, full-history secret scanning with gitleaks, and a generated tool reference that CI keeps honest so the docs can never again drift from the code.
Added
TZtimezone support for rendered timestamps (#23). SetTZto an IANA zone (e.g.Australia/Adelaide) and every timestamp in tool output renders in that zone. The IANA timezone database is embedded in the binary, so this works in the Docker image — and on Windows — with no extra packages or volume mounts. An unrecognisedTZlogs a startup warning and falls back rather than refusing to start.PIHOLE_TLS_SKIP_VERIFY(defaultfalse) — opt-in for Pi-hole instances serving self-signed HTTPS certificates. Verification stays on by default; the README documents why a trusted certificate is the better fix.- Full generated tool reference at docs/TOOLS.md — every tool with its parameters, produced from the registered tool definitions by
cmd/toolsdocand checked for drift in CI, so it cannot go stale. - Coverage reporting via Codecov on every push and pull request.
- Secret scanning with gitleaks, both as a pre-commit hook and as a full-history CI scan.
- Fuzz testing for the tool-parameter validators, run continuously in CI alongside the existing table-driven tests.
Changed
- Every timestamp now carries an explicit zone marker (e.g.
19 Jul 2026, 9:41 AM UTCrather than19 Jul 2026, 9:41 AM). Previously the Docker image silently rendered timestamps in unlabelled UTC — ambiguous for both people and AI agents trying to convert times (#23).
Full Changelog: v0.6.0...v0.7.0
v0.6.0
Highlights
This release is about the things that go wrong. Pi-hole's embedded web server drops connections under load, and until now pihole-mcp passed that straight through — a dropped connection became a failed tool call. Requests are now retried with exponential backoff, but deliberately not uniformly: measured against FTL v6.7, the 429 you are actually most likely to meet is api_seats_exceeded, and it is not a rate limit at all. Pi-hole allows only 16 concurrent API sessions by default, every client that logs in takes one, and a seat is freed only when a session times out half an hour later. Retrying it is futile, so pihole-mcp doesn't — it tells you how to fix it, pointing you at the tools that list and revoke sessions. This is the most frequently reported Pi-hole v6 API problem and it used to surface as an opaque error.
Two bugs surfaced from running the server against a real Pi-hole rather than a mock. pihole_info_messages had never shown the text of a diagnostic message — FTL returns it under plain, we read a key that does not exist, and every warning rendered as a type and a timestamp with nothing between them. It hid for five releases because a healthy Pi-hole reports no messages at all, so no test ever covered a populated response. And configuring a second Pi-hole silently stripped the output schema from every tool that had one — the flagship multi-instance feature quietly degrading the structured-output support it should have showcased.
On security: golang.org/x/net was carrying seven advisories, and the Go toolchain a reachable one in crypto/tls. All are fixed here. None of it was visible, because there was no vulnerability scanning in CI and every automated dependency update had been failing its checks since the day those checks were added. Both problems are now fixed, and govulncheck runs on every push, every pull request, and weekly — because advisories get disclosed against code that hasn't changed.
Verified end to end against Pi-hole FTL v6.7.
Added
pihole_info_dismiss_message— dismiss an FTL diagnostic message once you have dealt with it. Previously you could see Pi-hole's warnings but not clear them, so thedaily_reportandsecurity_auditprompts had to send you to the web interface.pihole_info_messagesnow prints each message's ID to pass to it.- Automatic retry with exponential backoff and jitter for transient Pi-hole failures.
PIHOLE_MAX_RETRIES(default3,0disables) andPIHOLE_RETRY_MAX_DELAY(default8s). - A distinct, actionable error when Pi-hole's API session pool is full, explaining that the limit is
webserver.api.max_sessions(default 16), that every client takes a seat, and that you can free one withpihole_auth_sessionsandpihole_auth_revoke_session. - Per-instance resources. With more than one Pi-hole configured, each is addressable at
pihole://<instance>/statusandpihole://<instance>/summary, with apihole://instancesindex. The unprefixed URIs still read the first-declared instance. - README sections for Troubleshooting (session exhaustion, auth failures, Docker networking, dropped connections) and Resources, which had never been documented despite being advertised.
Changed
- Verified against Pi-hole FTL v6.7; the development and CI containers now run
2026.07.2. - The SSE transport is documented as deprecated, in line with the MCP specification superseding HTTP+SSE with Streamable HTTP. It still works and still receives fixes; new deployments should use
-transport http. - Retries are method-aware. A rate-limited request is safe to replay for any method, because Pi-hole rejected it before processing it. A connection that failed without a reply is only replayed for reads — when we cannot know whether Pi-hole applied a delete, a duplicated delete is worse than the error it would have papered over.
just refresh-fixturesnow seeds the development Pi-hole first. A newly created Pi-hole has no query history, so every statistics endpoint answers empty and fixtures captured from it assert nothing.- Documented artefact sizes are now measured rather than remembered. The long-repeated "9 MB Docker image" was wrong — that was roughly the binary size. Default: 16.4 MB binary, 6.1 MB download, 18.2 MB image. Slim: 9.2 MB, 3.6 MB, 11.8 MB.
Fixed
pihole_info_messagesdisplayed no message text. FTL returns the body underplain; the client decoded amessagekey the API does not send, so every diagnostic warning rendered as a bare type and timestamp. Present since the tool was introduced.- Configuring a second Pi-hole stripped structured output from every tool. A multi-instance tool can return either a single-instance result or an
instance=allaggregate, and rather than describe both, the output schema was discarded entirely — so nine tools advertised a schema on one Pi-hole and none did on two. Both shapes are now declared as aoneOf. - A 5xx during login reported itself as "authentication failed". A server that cannot answer is not a bad password.
DoRawandPostMultipartbypassed the 401 re-authentication path that every other request had.- Automated dependency updates had been failing CI since the checks were introduced. The changelog enforcer skipped on labels that did not exist in the repository, so no bot pull request ever carried one; and commitlint's 100-character body limit tripped on the single unwrappable line Dependabot writes to name every module in a grouped update. Ungrouped updates passed, which made the failure look intermittent rather than systematic.
- Intermittent
sending auth request: EOFfailures in CI. The end-to-end suite starts a process per tool call, each authenticating afresh, which exhausts FTL's session table faster than a flat 0.3-second retry could outlast. READMEclaimed nine prompts while listing six.security_audit,weekly_trendsandupstream_healthshipped in v0.2.0 and had been invisible to users ever since.
Security
golang.org/x/net0.52.0 → 0.55.0, fixing GO-2026-4918, GO-2026-5025, GO-2026-5026, GO-2026-5027, GO-2026-5028, GO-2026-5029 and GO-2026-5030.golang.org/x/syslikewise fixes GO-2026-5024.- Go toolchain pinned to 1.26.5, fixing GO-2026-5856 — an Encrypted Client Hello privacy leak in
crypto/tlsthatgovulncheckreports as reachable from the HTTP transport and the Pi-hole client's TLS paths. govulncheckadded to CI, on every push and pull request plus a weekly schedule. The schedule is the point: advisories are disclosed against code that has not changed, so scanning only on push leaves a quiet repository silently vulnerable.
Dependencies
github.com/mark3labs/mcp-go0.54.1 → 0.56.0go.opentelemetry.io/oteland friends 1.43.0 → 1.44.0github.com/grpc-ecosystem/grpc-gateway/v22.28.0 → 2.29.0actions/checkout6 → 7- Go toolchain 1.26.4 → 1.26.5
Full Changelog: v0.5.0...v0.6.0
v0.5.0
Highlights
This release makes pihole-mcp a first-class tool for households and homelabs that run more than one Pi-hole. Multiple instances can now be configured side by side (PIHOLE_1_URL, PIHOLE_2_URL, …); every tool takes an optional instance argument, results are labelled with their source instance, and read-only tools accept instance=all to query the whole fleet concurrently and return a single structured aggregate. Two new tools turn the server into a safe alternative to standalone sync utilities: pihole_instance_diff reports exactly how two Pi-holes differ (adlists, allow/deny rules, groups, clients, local DNS), and pihole_instance_sync reconciles a target towards a source as a dry-run plan you confirm before anything is written — one direction only, host-specific and secret settings never touched. A new pihole_padd dashboard tool collapses what used to be half a dozen calls — queries, blocking state, top domain/client, cache, versions, and host health — into one snapshot, making it the natural first call for any status check. Alignment with the latest MCP best practices deepens across the board: every tool now carries a human-readable title with consistent behaviour hints, long-running actions emit progress notifications, the server emits structured log messages (with credential redaction), and the investigate_domain prompt offers argument completions sourced from your live domain rules. Tracks the current Pi-hole release (FTL v6.6.2 / docker 2026.05.0) and toolchain (mcp-go v0.54.1).
Added
- Multi-instance support — configure several Pi-holes with
PIHOLE_1_URL/PIHOLE_1_PASSWORD(optionalPIHOLE_1_NAME),PIHOLE_2_URL, and so on. The single-instancePIHOLE_URL/PIHOLE_PASSWORDform is unchanged and is namedprimary. Every tool gains an optionalinstanceargument (advertised on the schema only when more than one instance is configured) and labels its result with the source instance. Read-only tools also acceptinstance=all, which now queries every instance concurrently and returns a structured aggregate (summarycounts plus a per-instance array, each entry labelled and carrying its own data or error) alongside the### instance: <name>text fallback; a failure on one instance no longer fails the whole call. State-changing tools rejectinstance=all. The first-declared instance is the default and backs all resources. pihole_instance_diff— compare configuration between two instances (groups, adlists/allowlists, allow/deny exact and regex rules, clients, local DNS A/AAAA records, and CNAME records) and report what is added, changed, or only on the target. Read-only; runs no writes. Only registered when more than one instance is configured.pihole_instance_sync— reconcile a target Pi-hole towards a source, one direction only. Runs as a dry-run plan by default and returns aconfirm_token; re-run withmode=applyand that token to apply. The token is derived from the planned changes, so a configuration that drifts between planning and applying is rejected rather than silently overwritten. Adds and updates by default; deletions requireprune=true. A teleporter backup of the target is taken before any change (disable withsnapshot=false). Host-specific and identity/secret settings (DHCP, interface bindings, passwords, TLS, sessions, 2FA) are never synced; group-membership associations are not synced because Pi-hole group IDs are instance-local.pihole_padd— a single-call dashboard snapshot (queries incl.query_frequency, blocking state, top domain/blocked/client, recent blocked, cache counters, FTL CPU/memory, CPU temperature, component versions, and — atdetail=full— the primary network interface and host model). Structured output schema included. Recommended as the first call for a status overview.- Tool titles — all 77 tools now set a human-readable
titleannotation (e.g. "Dashboard Snapshot", "Top Domains") distinct from the programmatic name, which MCP clients can surface in their UI. - Progress notifications —
pihole_action_gravity_updatestreamsnotifications/progressas the gravity rebuild proceeds (when the client supplies aprogressToken); the flush actions emit start/complete progress. - MCP logging — the server now emits
notifications/messagelog events for notable operations (gravity update lifecycle, DNS restart, log/network flush, configuration changes), tagged with the originating instance. Credential-bearing fields are redacted before delivery, and the SDK gates delivery by the client's configured log level. - Prompt argument completions —
completion/completeis now supported; theinvestigate_domainprompt completes itsdomainargument from the configured allow/deny rules on the default instance. - Structured output schemas added to
pihole_info_system,pihole_stats_top_domains, andpihole_stats_top_clients(structured content is emitted even whenformat=csv). - Development tooling for multi-instance work: a second Pi-hole behind the Compose
multiprofile, plusjust dev-up-multi/just dev-down-multiand a multi-instance section in the E2E suite (gated onPIHOLE_2_URL). - A Docker-free, in-process Pi-hole emulator (
internal/pihole/piholefake) that backs the unit tests for routing, aggregation, and sync, plus ajust simwalkthrough that runs the full plan→apply→converge flow locally without containers. CI now starts a second Pi-hole and runs the multi-instance integration tests and E2E suite against both.
Fixed
- E2E harness: a parameter-default expansion (
${2:-{}}) appended a stray}to every supplied tool-argument payload, so multi-instance E2E calls sent malformed JSON. The harness now also retries transient transport failures (a dropped connection under rapid-fire load is not a tool failure) and isolates the single- and multi-instance environment forms, making the suite a reliable CI gate.
Changed
- Server instructions now recommend
pihole_paddas the entry point and document the Pi-hole FTL v6.6 / v6.5 configuration keys (resolver.macNames,database.forceDisk,dns.cache.rrtype) that are settable viapihole_config_set. - Tool count is now 77 (was 74): added
pihole_padd,pihole_instance_diff, andpihole_instance_sync(the latter two appear only in multi-instance setups). - Behaviour annotations are now internally consistent: read-only tools are no longer also flagged destructive or open-world (mcp-go's
NewTooldefaults both to true), so MCP clients render accurate hints. A test locks this invariant in across every tool.
Dependencies
- Bumped
github.com/mark3labs/mcp-gov0.54.0 → v0.54.1. - Development and CI Pi-hole image pinned to
pihole/pihole:2026.05.0(FTL v6.6.2 / Core v6.4.2 / Web v6.5). - CI golangci-lint pinned v2.11 → v2.12; release workflow Docker actions bumped to v4 (Node 24 runtime).
Full Changelog: v0.4.0...v0.5.0
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
v0.3.0
Highlights
Five new tools complete the network-management and long-term-history surfaces, raising tool coverage to 73. A fixture-based test infrastructure was introduced and immediately surfaced three Pi-hole API shape bugs that had been silently masking incorrect data — all now corrected. Conventional Commits and CHANGELOG.md updates are now enforced both locally and in CI, and releases publish straight to GitHub Releases without a manual draft step.
Added
pihole_history_database— query the long-term FTL database for total query counts grouped by interval.from/untilparameters scope the window and default to the last 7 days when omitted.pihole_history_database_clients— per-client breakdown of long-term query history over the same windowed interval.pihole_network_routes— list every routing-table entry observed by FTL with family, scope, and source attribution.pihole_network_interfaces— list network interfaces with link state, speed, addresses, and per-interface byte counters.pihole_network_delete_device— remove a stale device record from the FTL network table.- Fixture-based testing harness under
testdata/fixtures/(13 captured Pi-hole responses) plus aloadFixture(t, name)helper ininternal/tools/fixtures_test.go. Fixture refresh is automated viascripts/refresh-fixtures.shand thejust refresh-fixturesrecipe. RELEASING.mdrunbook documenting the tag-driven release procedure.
Changed
pihole_history_graphandpihole_history_clientsnow operate exclusively on in-memory FTL data. The overloadedfrom/untilparameters that previously routed those tools through the long-term database have been removed — that path now lives in the dedicatedpihole_history_database*tools above.internal/pihole/types.go—DatabaseInfo,NetworkInterface, andNetworkInterfaceStatswere updated to match the actual Pi-hole v6 wire format observed in fixtures (see Fixed below).
Fixed
/api/info/database— the response is flat at the top level, not wrapped in adatabasekey. The previous shape silently returned all-zeros for SQLite version, file size, and timestamp fields.DatabaseInfois now flat and the affected handler renders correct values.info_database.earliest_timestamp— Pi-hole emitsfloat64(sub-second precision), notint64. Type updated; previous decode path discarded fractional seconds and could fail on values that exceeded the int range./api/network/interfaces[].speed— nullable for loopback and tunnel interfaces. Now*int; previous non-pointer field caused JSON decode errors against real Pi-hole instances./api/network/interfaces[].addresses[].prefixlen— corrected fromprefixtoprefixlento match the Pi-hole field name./api/network/interfaces[].stats.rx_bytes/tx_bytes— wire format is{unit, value}envelopes, not raw integers. Type updated andformat.Bytes()rendering applies.
Release Pipeline
.goreleaser.yamlnow setsrelease.draft: falseandrelease.mode: keep-existing. Tags publish straight to GitHub Releases with no manual draft step, and re-runs do not overwrite already-published bodies.- Conventional Commits are enforced locally via a lefthook
commit-msghook (zero deps, pure shell regex) and on PRs viawagoid/commitlint-github-action. CHANGELOG.mdupdates are enforced on PRs viadangoslen/changelog-enforcer. TheSkip-Changeloglabel exists for legitimately internal-only PRs (refactors, tests, CI, dev tooling, no-op dependency bumps).scripts/release-notes.shextracts each release body from this file and feeds it to goreleaser via--release-notes=NOTES.md, replacing the auto-generated changelog block.scripts/changelog-draft.shscaffolds the next[Unreleased]section fromgit logwhen prepping a release.
Migration Notes
pihole_history_graphandpihole_history_clientsno longer acceptfrom/untilparameters. Long-term database queries that previously routed through these tools are now served by the dedicatedpihole_history_databaseandpihole_history_database_clientstools, both of which default to a 7-day window whenfrom/untilare omitted.
Installation
Go install:
go install github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.3.0
Docker (multi-arch):
docker pull ghcr.io/hexamatic/pihole-mcp:0.3.0
Binary download: grab the archive for your platform from the release assets.
Requirements
- Pi-hole v6.6+ with the REST API enabled
- 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 |
See the README for client-specific setup guides (Claude Desktop, Cursor, Windsurf, VS Code, Cline) and the OpenTelemetry section for opt-in tracing configuration.
Full Changelog: v0.2.0...v0.3.0
v0.2.0
Highlights
The repository moved from lloydmcl/pihole-mcp to hexamatic/pihole-mcp — existing GitHub URLs redirect, stars, watchers, issues, and forks remain in place; new container images publish to ghcr.io/hexamatic/pihole-mcp. This release also lands the v0.2.0 feature set: twelve new tools (68 total, ~95% Pi-hole v6 REST API coverage), three new MCP prompts (nine total), structured output schemas on key tools per the MCP 2025-11-25 specification, and sixteen new unit-test files raising coverage across all 17 tool categories.
Added
- Long-term database statistics — four new tools surface historical analysis over the FTL database with mandatory
from/untilwindows:pihole_stats_database_top_domainspihole_stats_database_top_clientspihole_stats_database_upstreamspihole_stats_database_query_types
- Granular configuration — three new tools support dotted-path access into Pi-hole's hierarchical config and deferred FTL restart for batch operations:
pihole_config_get_valuepihole_config_add_valuepihole_config_remove_value
- System info — three new tools expose FTL engine internals, live DNS/DHCP metrics, and hardware temperature sensors (
pihole_info_ftl,pihole_info_metrics,pihole_info_sensors). - Session management — two new tools for security auditing:
pihole_auth_sessions(list active sessions) andpihole_auth_revoke_session(terminate a session by ID). - MCP prompts — three new prompts:
security_audit— reviews active sessions, authentication configuration, and diagnostic messages for unauthorised access patterns.weekly_trends— week-over-week DNS statistics comparison via the new long-term database tools.upstream_health— DNS resolver performance, cache efficiency, and DNSSEC validation analysis.
- Structured output schemas —
pihole_dns_get_blocking,pihole_stats_summary, andpihole_domains_listnow return structured output per MCP 2025-11-25, allowing downstream agents to parse results programmatically without LLM interpretation of formatted text.
Changed
- Repository transferred from
lloydmcl/pihole-mcptohexamatic/pihole-mcp. Existing GitHub URLs redirect; the Go module path, container image references, badge URLs, and goreleaser configuration are rewritten to the new namespace acrossREADME.md,CONTRIBUTING.md,SECURITY.md, the issue templates, and every Go source file. - Container images now publish to
ghcr.io/hexamatic/pihole-mcp(multi-arch: linux/amd64, linux/arm64). - Project branding — replaced the Pi-hole Vortex logo with custom branding to avoid trademark conflicts.
- Shared helpers extracted across tool handlers (
toolError,getCountCapped) centralise error formatting and count clamping that were previously duplicated.
Fixed
justfilePATH composition —mise bin-pathsemits one path per line; the previous concatenation truncatedPATHat the first entry, leavinggoreleaserand other mise-managed tools unreachable fromjust release-dry. Now collapses newlines into the colon separator before prepending.
Quality
- Sixteen new unit-test files raise tool-handler coverage across all 17 tool categories, including error paths and parameter validation.
- The E2E suite is extended to cover the twelve new tools and a config add/remove round-trip against a live Pi-hole.
Migration Notes
- Container image path — pull from
ghcr.io/hexamatic/pihole-mcp:0.2.0(or:latest). The previousghcr.io/lloydmcl/pihole-mcppath is no longer published; existing images at the old path remain accessible but receive no updates. - Go module path —
go install github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.2.0. Existing imports ofgithub.com/lloydmcl/pihole-mcpshould be updated; the GitHub redirect handles the source pull, but Go's module proxy may cache under the new path.
Installation
Go install:
go install github.com/hexamatic/pihole-mcp/cmd/pihole-mcp@v0.2.0
Docker (multi-arch):
docker pull ghcr.io/hexamatic/pihole-mcp:0.2.0
Binary download: grab the archive for your platform from the release assets.
Requirements
- Pi-hole v6.6+ with the REST API enabled
- 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 |
See the README for client-specific setup guides (Claude Desktop, Cursor, Windsurf, VS Code, Cline) and the OpenTelemetry section for opt-in tracing configuration.
v0.1.0
Initial Release
A production-grade MCP server for Pi-hole v6, providing complete API coverage through 55 tools, 6 prompts, and 5 resources — all in a single Go binary.
Highlights
- 55 tools across 16 categories: DNS blocking, statistics, queries, domains, groups, clients, lists, config, actions, network, DHCP, logs, and more
- 6 MCP prompts for guided workflows: DNS diagnosis, domain investigation, blocked domain review, network audit, blocklist optimisation, and daily reporting
- 5 MCP resources for quick status checks
- Response controls —
detail(minimal/normal/full) andformat(text/csv) parameters on applicable tools - Session lifecycle management — lazy login, auto-refresh on 401, compare-and-swap to prevent thundering herd, session cleanup on shutdown
- Optional OpenTelemetry tracing for observability
- Transports — stdio (default), HTTP (Streamable HTTP), SSE
Installation
Go install:
go install github.com/lloydmcl/pihole-mcp/cmd/pihole-mcp@v0.1.0
Docker (multi-arch):
docker pull ghcr.io/lloydmcl/pihole-mcp:0.1.0
Binary download: grab the archive for your platform below.
Requirements
- Pi-hole v6.6+ with the REST API enabled
- 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 |
See the README for client-specific setup guides (Claude Desktop, Cursor, Windsurf, VS Code, Cline).