diff --git a/Dockerfile b/Dockerfile index 4f30625..52da1f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,5 +22,5 @@ USER appuser # Expose the SSE port EXPOSE 8000 -# Run in SSE mode -CMD ["python", "server.py", "sse", "--host", "0.0.0.0", "--port", "8000"] +# Run via FastMCP CLI in SSE mode +CMD ["fastmcp", "run", "server.py:mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"] diff --git a/server.py b/server.py index f33f182..faaf036 100644 --- a/server.py +++ b/server.py @@ -209,55 +209,10 @@ def query_notebook(notebook: str, query: str, limit: int = 5) -> str: except Exception as e: return f"Query failed: {e}" -if __name__ == "__main__": - import uvicorn - # Bypass FastMCP CLI wrapper and run uvicorn directly - # This avoids the Typer/Click argument parsing issues entirely. - logging.info("Starting knowledge-mcp server via programmatic Uvicorn...") - - # FastMCP uses _sse_app internally for the Starlette app - # We force initialization of it by accessing it or calling a method if needed. - # But usually it's lazy. - - if not hasattr(mcp, "_sse_app"): - logging.warning("mcp._sse_app not found, attempting to initialize...") - # Accessing settings might trigger init, or we might need to rely on - # the fact that it's created when run() is called. - # But since we can't call run(), we might need to construct it. - # However, looking at FastMCP source, it seems to use Starlette. - pass - - try: - # We can't easily extract the app if it's private and lazy. - # So we will fall back to the CLI method but with NO arguments, - # relying on the hardcoded sys.argv injection above as the safety net. - # BUT we must ensure sys.argv is CLEAN first. - - import sys - # Reset sys.argv to just the script name + our desired args - # This nukes any "bad" args passed by Docker CMD - sys.argv = [sys.argv[0], "sse", "--host", "0.0.0.0", "--port", "8000"] - - logging.info(f"Reset sys.argv to: {sys.argv}") - mcp.run() - - except BaseException as e: - logging.critical(f"Server crashed: {e}", exc_info=True) - raise - -# Expose as ASGI app for uvicorn to pick up if run directly (uvicorn server:app) -# This bypasses the Typer CLI entirely. -app = mcp._sse_app if hasattr(mcp, "_sse_app") else None -if app is None: - # Fallback if _sse_app isn't pre-initialized or private - try: - from starlette.applications import Starlette - from mcp.server.sse import SseServerTransport - - # We need to construct the app manually if FastMCP hides it - # But FastMCP typically creates it on demand. - # Let's try to find it or create a wrapper. - pass - except ImportError: - pass +# NOTE: +# We intentionally do NOT call mcp.run() here. +# This module is meant to be run via the FastMCP CLI: +# fastmcp run server.py:mcp --transport sse --host 0.0.0.0 --port 8000 +# The CLI handles proper server startup; calling mcp.run() directly has been +# exiting cleanly (exit code 0) under this container setup.