Summary
Since July 8, the kai-api MCP server (quay.io/konveyor/kai-solution-server:latest) returns HTTP 421 Misdirected Request for every request when deployed behind a proxy/ingress (minikube, and likely OpenShift routes). The server is completely unreachable through the ingress.
Root cause
Two compounding problems:
- The image build ignores the lockfile.
tools/deploy/Containerfile runs pip3.12 install --no-cache-dir . against pyproject.toml only — requirements.txt / uv.lock (which pin fastmcp==2.14.3, mcp==1.25.0) are never copied into the image. pyproject declares only a floor: fastmcp>=2.8.0, so every rebuild resolves the newest fastmcp from PyPI.
- FastMCP 3.4.3 enabled host/origin (DNS-rebinding) protection by default —
http_host_origin_protection: bool = True with http_allowed_hosts: None, i.e. an effectively empty allowlist. The security middleware validates the Host header and returns 421 "Invalid Host header" on mismatch. Same failure mode hit other projects the same week: modelcontextprotocol/python-sdk#1798, CrowdStrike/falcon-mcp#291, blockscout/mcp-server#333, write-up.
The merge of #932 triggered the fresh :latest build that picked up fastmcp ≥3.4.3 — but the dependency bumps in #932 are themselves incidental; any merge would have produced the same broken image. (Earlier drafts of this issue blamed the starlette bump; that was wrong — starlette has no 421 code path.)
Behind a proxy, the forwarded Host header is the external host/IP (e.g. 192.168.49.2 via tackle-hub's /hub/services/kai/api route), which never matches the empty/localhost allowlist → every request is rejected before reaching the MCP session layer.
Evidence
kai-api pod logs — the server itself returns 421, not nginx:
INFO: Starting MCP server 'KaiSolutionServer' with transport 'streamable-http' on http://0.0.0.0:8000/api
INFO: 10.244.0.16:45922 - "POST /api HTTP/1.1" 421 Misdirected Request
INFO: 10.244.0.16:37090 - "POST /api HTTP/1.1" 421 Misdirected Request
... (every request, deterministic)
Impact
- All
editor-extensions CI infrastructure tests (@requires-minikube) are broken
- Any deployment behind an ingress/route that preserves the external Host header is unreachable
Reproduction
- Deploy kai-api via the konveyor operator on minikube with ingress enabled
curl -k -X POST https://$(minikube ip)/hub/services/kai/api -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"initialize","id":0,"params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"0.0.0"}}}'
- Observe HTTP 421
Suggested fixes (increasing durability)
- Immediate mitigation for existing deployments: set
FASTMCP_HTTP_HOST_ORIGIN_PROTECTION=false on the kai-api container
- Make the image reproducible: install from the lock (
uv sync --frozen, or copy requirements.txt into the image and pip install -r) instead of resolving floors at build time
- Cap the constraint (
fastmcp>=2.8.0,<3) until a deliberate 3.x migration, and/or configure http_allowed_hosts explicitly for proxied deployments
Summary
Since July 8, the kai-api MCP server (
quay.io/konveyor/kai-solution-server:latest) returns HTTP 421 Misdirected Request for every request when deployed behind a proxy/ingress (minikube, and likely OpenShift routes). The server is completely unreachable through the ingress.Root cause
Two compounding problems:
tools/deploy/Containerfilerunspip3.12 install --no-cache-dir .againstpyproject.tomlonly —requirements.txt/uv.lock(which pinfastmcp==2.14.3,mcp==1.25.0) are never copied into the image. pyproject declares only a floor:fastmcp>=2.8.0, so every rebuild resolves the newest fastmcp from PyPI.http_host_origin_protection: bool = Truewithhttp_allowed_hosts: None, i.e. an effectively empty allowlist. The security middleware validates theHostheader and returns421 "Invalid Host header"on mismatch. Same failure mode hit other projects the same week: modelcontextprotocol/python-sdk#1798, CrowdStrike/falcon-mcp#291, blockscout/mcp-server#333, write-up.The merge of #932 triggered the fresh
:latestbuild that picked up fastmcp ≥3.4.3 — but the dependency bumps in #932 are themselves incidental; any merge would have produced the same broken image. (Earlier drafts of this issue blamed the starlette bump; that was wrong — starlette has no 421 code path.)Behind a proxy, the forwarded
Hostheader is the external host/IP (e.g.192.168.49.2via tackle-hub's/hub/services/kai/apiroute), which never matches the empty/localhost allowlist → every request is rejected before reaching the MCP session layer.Evidence
kai-api pod logs — the server itself returns 421, not nginx:
Impact
editor-extensionsCI infrastructure tests (@requires-minikube) are brokenReproduction
curl -k -X POST https://$(minikube ip)/hub/services/kai/api -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"initialize","id":0,"params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"0.0.0"}}}'Suggested fixes (increasing durability)
FASTMCP_HTTP_HOST_ORIGIN_PROTECTION=falseon the kai-api containeruv sync --frozen, or copyrequirements.txtinto the image andpip install -r) instead of resolving floors at build timefastmcp>=2.8.0,<3) until a deliberate 3.x migration, and/or configurehttp_allowed_hostsexplicitly for proxied deployments