Files
fin/docker-compose.yml
T
EislM0203andClaude Opus 5 43c90dcd43 fix(mcp): trust the deployed hostname under streamable-http
FastMCP enables DNS rebinding protection inside __init__ when it is built with
a loopback bind address, and create_server() takes the default 127.0.0.1:

    if transport_security is None and host in ("127.0.0.1", "localhost", "::1"):
        transport_security = TransportSecuritySettings(
            enable_dns_rebinding_protection=True,
            allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"],
        )

main() reassigns settings.host afterwards, which never revisits that decision,
so the allow-list stayed loopback-only. Any client reaching the server under a
real hostname got 421 Misdirected Request with "Invalid Host header". It reads
as a hang rather than a rejection, because clients retry until they time out --
bifrost reported fin as simply disconnected after exhausting its retries.

Rebuild the settings after the host is known. Protection stays on; the loopback
patterns are kept and FIN_MCP_ALLOWED_HOSTS adds to them (comma separated,
"host:*" allows any port). Unlisted hosts are still refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RTuguQqazhoWUsh2GkPg9a
2026-08-23 14:23:29 +02:00

51 lines
1.4 KiB
YAML

services:
api:
image: harbor.cloud.traunseenet.com/library/fin-api:latest
build: ./api
volumes:
- sqlite_data:/data
environment:
- DATABASE_URL=sqlite:////data/fin.db
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
interval: 30s
timeout: 10s
retries: 3
mcp:
image: harbor.cloud.traunseenet.com/library/fin-mcp:latest
build: ./cli
environment:
- FIN_API_URL=http://api:8000
- FIN_MCP_TRANSPORT=streamable-http
- FIN_MCP_HOST=0.0.0.0
- FIN_MCP_PORT=8000
- FIN_MCP_PATH=/mcp
- FIN_MCP_ALLOW_WRITES=1
# Clients reach this service as "mcp" on the compose network, and FastMCP
# rejects any Host it was not told to trust with a 421.
- FIN_MCP_ALLOWED_HOSTS=mcp:*
# No ports mapping on purpose: the API has no auth, so the MCP surface
# stays reachable only from inside the compose network. Add a mapping
# only if you also set FIN_API_TOKEN.
depends_on:
api:
condition: service_healthy
restart: unless-stopped
web:
image: harbor.cloud.traunseenet.com/library/fin-web:latest
build: ./web
ports:
- "80:80"
environment:
- API_UPSTREAM=api:8000
depends_on:
api:
condition: service_healthy
restart: unless-stopped
volumes:
sqlite_data: