diff --git a/orthocal/asgi.py b/orthocal/asgi.py index e5c1238..060ebf5 100644 --- a/orthocal/asgi.py +++ b/orthocal/asgi.py @@ -13,6 +13,7 @@ from django.conf import settings from django.core.asgi import get_asgi_application +from mcp.server.transport_security import TransportSecuritySettings django_application = get_asgi_application() @@ -24,7 +25,19 @@ # owns its own /mcp route and lifespan (which starts/stops its session # manager's background task). Django's ASGIHandler doesn't implement the # lifespan protocol at all, so lifespan scope is only ever handled here. -mcp_application = mcp.streamable_http_app() +# +# transport_security must be set explicitly: with no host argument, the SDK +# defaults host to '127.0.0.1' and auto-enables DNS-rebinding protection +# restricted to localhost -- fine in local dev, but it rejects every real +# request in production (Cloud Run's own hostname as the Host header, +# forwarded by the Firebase Hosting proxy in front of it, isn't on that +# allowlist). DNS-rebinding protection defends a server bound to localhost +# against a malicious webpage's JS reaching it through the browser; it +# doesn't apply to a public HTTPS API with no localhost-only trust boundary, +# so disabling it here is the correct fix, not a workaround. +mcp_application = mcp.streamable_http_app( + transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False), +) async def application(scope, receive, send):