Files
nadlan-mcp/run_http_server.py
chaim c4d3699669 fix(security): suppress uvicorn server-version banner + trust proxy headers
- server_header=False — uvicorn was adding "server: uvicorn" after our
  middleware ran, defeating the banner-strip in security_headers().
- proxy_headers + forwarded_allow_ips=* — so request.client.host
  reflects the real client IP from X-Forwarded-For (Traefik), which
  is what the per-IP rate limiter keys on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:27:03 +00:00

63 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""
HTTP Server Entry Point for Nadlan-MCP.
Serves three things on a single port:
/ → React SPA (search & browse Israeli real estate data)
/api/... → REST endpoints used by the SPA (also documented at /api/docs)
/mcp → FastMCP streamable-http endpoint (for LLM clients)
For local Claude Desktop integration, use run_fastmcp_server.py (stdio).
"""
import logging
import os
import sys
import uvicorn
from nadlan_mcp.web_app import create_app
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
# Module-level app for ASGI servers (uvicorn web_app:app, gunicorn, etc.).
app = create_app()
def main() -> None:
port = int(os.environ.get("PORT", 8000))
host = "0.0.0.0"
logger.info("=" * 60)
logger.info("Starting Nadlan-MCP HTTP Server")
logger.info("=" * 60)
logger.info(f" UI: http://{host}:{port}/")
logger.info(f" API docs: http://{host}:{port}/api/docs")
logger.info(f" MCP: http://{host}:{port}/mcp")
logger.info(f" Health: http://{host}:{port}/health")
logger.info("=" * 60)
try:
# server_header=False stops uvicorn from advertising its name+version;
# date_header=False is harmless and trims one more byte of fingerprint.
# proxy_headers=True + forwarded_allow_ips="*" so request.client.host
# reflects the real client IP behind Traefik (used by the rate limiter).
uvicorn.run(
app,
host=host,
port=port,
log_level="info",
server_header=False,
proxy_headers=True,
forwarded_allow_ips="*",
)
except Exception as e:
logger.error(f"Failed to start HTTP server: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
main()