Summary
The lighttpd rewrite rule for the MCP endpoint only matches /mcp/... (with a trailing slash), so a request to the bare path /mcp returns 404 instead of reaching the FastCGI MCP app. This breaks remote MCP clients that normalize away the trailing slash (e.g. Claude's web connector), since they connect to https://<host>/mcp.
Location
packaging/linux-leader/files/lighttpd/50-pioreactorui.conf
url.rewrite-once = (
"^(/static($|/.*))$" => "$1",
"^(/api/.*)$" => "/api.fcgi$1",
"^(/unit_api/.*)$" => "/api.fcgi$1",
"^(/mcp/.*)$" => "/api.fcgi$1", # <-- requires trailing slash
)
Why bare /mcp 404s
A request to /mcp (no trailing slash):
- does not match
"^(/mcp/.*)$", so it is never rewritten to /api.fcgi, and
- is excluded from the SPA fallback by the negative match block:
$HTTP["url"] !~ "^(/api($|/)|/api\.fcgi($|/)|/unit_api($|/)|/mcp($|/)|/static($|/)|/exports($|/))" { ... }
(/mcp matches /mcp($|/), so the SPA rewrite is skipped too)
With no rewrite from either branch, lighttpd tries to serve /mcp as a static file and returns 404.
Reproduce
On a leader exposing the UI:
curl -i -X POST http://127.0.0.1/mcp -H 'Content-Type: application/json' -d '{}' # -> 404
curl -i -X POST http://127.0.0.1/mcp/ -H 'Content-Type: application/json' -d '{}' # -> reaches MCP app
The MCP backend itself handles both paths fine — the discrepancy is purely in this rewrite rule. The MCP spec doesn't require clients to preserve a trailing slash on the server URL, and several do strip it, so the bare-path case needs to work.
Suggested fix
Widen the pattern to also match the empty (bare) case:
- "^(/mcp/.*)$" => "/api.fcgi$1",
+ "^(/mcp($|/.*))$" => "/api.fcgi$1",
This is low-risk: it only adds the bare /mcp path; existing /mcp/... behaviour is unchanged. I've been running this patch on a leader (pioreactor 26.5.2) and /mcp now reaches the MCP app — verified end-to-end through a Cloudflare Tunnel + Cloudflare Access, where the bare path now returns the expected MCP OAuth 401 challenge instead of a 404.
(Note: /api and /unit_api carry the same trailing-slash-only pattern, but no client connects to those bare roots, so this report is scoped to /mcp.)
Summary
The lighttpd rewrite rule for the MCP endpoint only matches
/mcp/...(with a trailing slash), so a request to the bare path/mcpreturns 404 instead of reaching the FastCGI MCP app. This breaks remote MCP clients that normalize away the trailing slash (e.g. Claude's web connector), since they connect tohttps://<host>/mcp.Location
packaging/linux-leader/files/lighttpd/50-pioreactorui.confWhy bare
/mcp404sA request to
/mcp(no trailing slash):"^(/mcp/.*)$", so it is never rewritten to/api.fcgi, and/mcpmatches/mcp($|/), so the SPA rewrite is skipped too)With no rewrite from either branch, lighttpd tries to serve
/mcpas a static file and returns 404.Reproduce
On a leader exposing the UI:
The MCP backend itself handles both paths fine — the discrepancy is purely in this rewrite rule. The MCP spec doesn't require clients to preserve a trailing slash on the server URL, and several do strip it, so the bare-path case needs to work.
Suggested fix
Widen the pattern to also match the empty (bare) case:
This is low-risk: it only adds the bare
/mcppath; existing/mcp/...behaviour is unchanged. I've been running this patch on a leader (pioreactor 26.5.2) and/mcpnow reaches the MCP app — verified end-to-end through a Cloudflare Tunnel + Cloudflare Access, where the bare path now returns the expected MCP OAuth 401 challenge instead of a 404.(Note:
/apiand/unit_apicarry the same trailing-slash-only pattern, but no client connects to those bare roots, so this report is scoped to/mcp.)