--- description: >- Session record (2026-07-06): triaged Caddy journald warnings on onlyoffice.localhost/mcp showing "context canceled" SSE aborts (~1.5ms) from opencode 1.17.13 MCP client against the OnlyOffice MCP server on :3847. Verified upstream liveness, opened GitLab issue #92 in satware/mcp/onlyoffice, and documented the Caddy reverse-proxy as a manual override. tags: - learning - caddy - onlyoffice - mcp - sse - reverse-proxy - investigation last_updated: '2026-07-06' --- # Caddy "context canceled" on onlyoffice.localhost/mcp - investigation - 2026-07-06 ## Summary Caddy (systemd `caddy.service`) on `mw-manjaro-pf` is emitting a high volume of `warn`-level journald entries: `aborting with incomplete response ... error: "reading: context canceled"` for `GET /mcp` requests on `https://onlyoffice.localhost`. Each abort fires ~1.5ms after the request is forwarded to the upstream `localhost:3847` (OnlyOffice MCP server). The upstream is alive and correctly rejects non-SSE GETs with HTTP 406, so this is a stream-lifecycle issue, not a server-down issue. Investigation handed off to the OnlyOffice MCP repo as GitLab issue #92. This workstation's Caddy reverse-proxy config is now recorded as a manual override (not Ansible-managed). ## Incident | Field | Value | |-------|-------| | Symptom | Repeated `context canceled` warnings in `journalctl -u caddy` | | Caddy site | `https://onlyoffice.localhost` -> `reverse_proxy localhost:3847` | | Upstream | `localhost:3847` (OnlyOffice MCP server) | | Request | `GET /mcp` with `Accept: text/event-stream` | | Client | `opencode/1.17.13` (MCP streamable-HTTP transport) | | MCP protocol | `2025-11-25` | | Abort duration | ~1.5ms (request forwarded, then read context canceled) | | Frequency | Multiple per minute, across distinct `Mcp-Session-Id` values | ## Evidence ### Caddy log sample (recurring) ```json { "level": "warn", "logger": "http.handlers.reverse_proxy", "msg": "aborting with incomplete response", "upstream": "localhost:3847", "duration": 0.00173631, "request": { "method": "GET", "host": "onlyoffice.localhost", "uri": "/mcp", "headers": { "Accept": ["text/event-stream"], "Mcp-Protocol-Version": ["2025-11-25"], "Mcp-Session-Id": ["a5e859b4-01f1-4c24-81bb-78ee02d74357"], "User-Agent": ["opencode/1.17.13"], "Via": ["1.1 Caddy"] } }, "error": "reading: context canceled" } ``` Observed `Mcp-Session-Id` values (each retried over several minutes): `a5e859b4...`, `4bfbb912...`, `3c4a0454...`, `08397c3d...`, `ed762f08...`, `b0fae341...`. ### Upstream liveness verification ```bash $ ss -tlnp | grep :3847 LISTEN 0 4096 0.0.0.0:3847 0.0.0.0:* $ curl -sS -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" \ --max-time 3 http://localhost:3847/mcp HTTP 406 in 0.004665s ``` HTTP 406 is expected for a plain GET without the required SSE headers. The MCP server is alive and rejecting non-SSE requests correctly. ## Diagnosis ### Root cause (not yet confirmed - under investigation) Five candidate hypotheses, listed in the GitLab issue. The leading suspect is **client-side disconnect**: opencode 1.17.13 opens the SSE stream then immediately closes it (the ~1.5ms duration is too short for a genuine read). Secondary suspects are server-side stream teardown, MCP protocol-version mismatch (`2025-11-25`), Caddy HTTP/1.1 framing of SSE, or a client session-pool bug creating and abandoning sessions. ### Why this workstation's concern The Caddy reverse-proxy is the boundary that surfaces the symptom. Even if the root cause is client- or server-side, the local config matters because: 1. Caddy logs are being polluted with warn-level noise (masks real errors). 2. The reverse-proxy block has no SSE-specific tuning (`flush_interval`, explicit HTTP/1.1 transport) that could mask or mitigate upstream misbehavior. 3. The Caddy config at `/etc/caddy/conf.d/localhost-dev.conf` is **not** Ansible-managed by this repo - it is a manual override that must be recorded (see `docs/tech/manual-overrides.md`). ## Environment | Component | Version / Value | |-----------|----------------| | Host | `mw-manjaro-pf` (mw-pfeddersheim-workstation) | | OS | Manjaro Linux, Wayland session | | Caddy | systemd `caddy.service`, active since 2026-07-02 | | Caddy config | `/etc/caddy/Caddyfile` + `/etc/caddy/conf.d/localhost-dev.conf` | | TLS cert | mkcert pair at `/etc/caddy/localhost{,-key}.pem` | | Upstream | `localhost:3847` (OnlyOffice MCP server, process TBD) | | MCP client | `opencode/1.17.13` | | MCP protocol | `2025-11-25` (streamable HTTP transport) | ## *.localhost sites configured All defined in `/etc/caddy/conf.d/localhost-dev.conf` (14 sites total): | Site | Backend / Handler | |------|-------------------| | `mcp.localhost` | `reverse_proxy localhost:50880` | | `artifacts.localhost` | `file_server browse` root `/var/www/artifacts` | | `onlyoffice.localhost` | `reverse_proxy localhost:3847` (subject of this issue) | | `project-1.localhost` ... `project-10.localhost` | `respond "project-N - placeholder"` (10 reserved) | | `demo.satware.com.localhost` | `reverse_proxy localhost:38081` (webdevops/php-apache-dev container) | ## Handoff ### GitLab issue | Item | Value | |------|-------| | Project | `satware/mcp/onlyoffice` (`gitlab.satware.com`) | | Issue | [#92](https://gitlab.satware.com/satware/mcp/onlyoffice/-/work_items/92) | | Title | Investigation: Caddy reports 'context canceled' on onlyoffice.localhost/mcp SSE stream (~1.5ms aborts) | | Labels | `bug`, `investigation`, `mcp` | | State | open | The issue body contains the full evidence, 5 hypotheses, 7 investigation tasks, and a proposed Caddy mitigation (`flush_interval -1` + `transport http { versions 1.1 }`). ### Next actions (assigned to OnlyOffice MCP repo) 1. Identify the process listening on `:3847` (`sudo ss -tlnp | grep 3847`). 2. Collect server-side logs for the `Mcp-Session-Id` values cited above. 3. Reproduce with `curl -N -H "Accept: text/event-stream" -H "Mcp-Protocol-Version: 2025-11-25" https://onlyoffice.localhost/mcp`. 4. Check opencode 1.17.13 changelog for MCP SSE session bugs. 5. Verify Caddy `reverse_proxy` SSE tuning (proposed block in issue #92). 6. Confirm whether `notifications/initialized` round-trip completes. 7. Log the negotiated MCP protocol version server-side. ## Local documentation updates | File | Change | |------|-------| | `docs/tech/manual-overrides.md` | Added row for `/etc/caddy/` config (not Ansible-managed) | | `docs/tech/stack.md` | Added "Local Dev Services" section listing Caddy + `*.localhost` sites | | `CHANGELOG.md` | Added `[Unreleased] -> Added` entry | | GitLab issue #92 | Created with full evidence + hypotheses + tasks | ## Proposed Caddy mitigation (to test on this workstation) ```caddyfile https://onlyoffice.localhost { tls /etc/caddy/localhost.pem /etc/caddy/localhost-key.pem reverse_proxy localhost:3847 { flush_interval -1 transport http { versions 1.1 } } } ``` `flush_interval -1` disables Caddy's buffering so SSE events flush to the client immediately; `transport http { versions 1.1 }` pins the upstream leg to HTTP/1.1 (Caddy logs already show HTTP/1.1 for the upstream, this makes it explicit and avoids HTTP/2 SSE framing surprises). Not applied yet - pending GitLab issue #92 resolution to avoid masking the root cause. ## References - GitLab issue: https://gitlab.satware.com/satware/mcp/onlyoffice/-/work_items/92 - Caddy config: `/etc/caddy/conf.d/localhost-dev.conf` - Local skill: `caddy` (`~/.config/opencode/skills/caddy/SKILL.md`) - MCP transport spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports - Manual overrides: `docs/tech/manual-overrides.md` - Software stack: `docs/tech/stack.md`