feat(mcp): systemd socket activation and healthz under /mcp (v0.1.1)
Patch release. Two changes to cmd/server, both motivated by running the service behind a unix socket on a reverse-proxied host: the binary now inherits a systemd-passed listener when present, and the healthz handler moves under the same path prefix as the MCP endpoint so a single proxy location forwards both. Bundled with two repository-hygiene changes. cmd/server: - chooseListener (new) — picks a listener at startup. When systemd has passed a LISTEN_FDS fd via github.com/coreos/go-systemd/v22/activation, the binary uses the inherited listener; otherwise it binds TCP at -addr as before. The startup log records "mode":"socket-activated" or "mode":"tcp" so operators can confirm which path is live. Same binary works for local dev and for systemd-managed deployment with no flags or env vars to toggle. - Health probe path is now derived from -path. With the default -path /mcp the probe is served at /mcp/healthz; the legacy /healthz route is no longer registered. A reverse proxy can now route the whole MCP service through a single "/mcp" location prefix instead of maintaining a second forward for /healthz. Consumers of v0.1.0 that hit /healthz directly must switch to /mcp/healthz. Dependencies: - github.com/coreos/go-systemd/v22 v22.7.0 — listener inheritance via LISTEN_FDS. Loaded only by cmd/server. Docs: - README.md "Deployment" section rewritten to be hosting-agnostic. The v0.1.0 draft prescribed a specific systemd-on-HestiaCP layout; the new text points at the Dockerfile and at systemd socket activation as a supported binary mode without dictating one operator's setup. Adds an explicit note that any reverse proxy must disable response buffering on the /mcp location — streamable MCP delivers tool results via Server-Sent Events and default proxy buffering breaks the stream. Repository hygiene: - /deploy/ is now .gitignored. Local deployment artefacts (systemd units, reverse-proxy templates, per-release scripts) are operator-specific by design and live outside the public repository. The Dockerfile at the module root remains the only portable, public-facing build artefact. No tool surface, no validation rules, no index schema, and no behaviour of the indexer changed. Operators upgrading from v0.1.0 must update their health-probe URL to /mcp/healthz (or whichever path matches their -path flag); MCP-protocol clients (Claude, Cursor, Zed, etc.) need no changes.
This commit is contained in:
31
README.md
31
README.md
@@ -159,6 +159,37 @@ All four commands must produce clean output before a PR will be reviewed.
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
The server is a single self-contained static binary. There is no canonical
|
||||
hosting shape — pick whichever matches the rest of your infrastructure. Two
|
||||
patterns cover most cases:
|
||||
|
||||
- **Container.** The [`Dockerfile`](Dockerfile) at the module root produces a
|
||||
distroless runtime image. Build from the einherjar repository root so the
|
||||
indexer can reach every sibling module at image-build time:
|
||||
|
||||
```bash
|
||||
docker build -f mcp/Dockerfile -t einherjar-mcp:0.1.0 .
|
||||
docker run --rm -p 8080:8080 einherjar-mcp:0.1.0
|
||||
```
|
||||
|
||||
- **Systemd / socket-activated binary.** The server detects an inherited
|
||||
listener via `github.com/coreos/go-systemd/v22/activation` and uses it when
|
||||
present, falling back to `-addr` TCP binding otherwise. Same binary works in
|
||||
both modes — no flag, no env var. Drop the binary into `/opt/<somewhere>/`
|
||||
and write a `.socket` + `.service` pair that matches your conventions.
|
||||
|
||||
Whatever shape you pick, the public-facing reverse proxy must keep response
|
||||
buffering **off** on the `/mcp` location. Streamable MCP delivers tool results
|
||||
via Server-Sent Events; default nginx, Envoy, or Caddy buffering batches the
|
||||
stream and breaks Claude's session before the first event arrives. For nginx
|
||||
that means `proxy_buffering off; proxy_cache off; proxy_request_buffering off;
|
||||
chunked_transfer_encoding on;` plus an extended `proxy_read_timeout` for
|
||||
long-lived sessions.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
No ADRs at `v0.1.0`. The structural decisions in this release (synthetic `wire`
|
||||
|
||||
Reference in New Issue
Block a user