2026-07-06 02:29:19 +02:00
|
|
|
"""Entry point: load settings from the environment and run the MCP server.
|
|
|
|
|
|
2026-07-07 16:52:30 +02:00
|
|
|
Two transports share one build:
|
|
|
|
|
- ``stdio`` (default): Cursor/Claude launch one process per user; the key comes
|
|
|
|
|
from the environment, so it is required here.
|
|
|
|
|
- ``streamable-http``: one process serves many users, each passing their own key
|
|
|
|
|
per request; the key is enforced by the transport's auth middleware instead.
|
|
|
|
|
|
|
|
|
|
For stdio, stdout is the protocol channel, so every log line goes to stderr.
|
2026-07-06 02:29:19 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
|
2026-07-07 16:52:30 +02:00
|
|
|
from mcp.server.fastmcp import FastMCP
|
|
|
|
|
|
2026-07-06 02:29:19 +02:00
|
|
|
from .config import Settings
|
|
|
|
|
from .server import build_server
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
stream=sys.stderr,
|
|
|
|
|
format="%(levelname)s %(name)s: %(message)s",
|
|
|
|
|
)
|
|
|
|
|
settings = Settings.from_env()
|
2026-07-07 16:52:30 +02:00
|
|
|
transport = os.environ.get("SURFSENSE_MCP_TRANSPORT", "stdio").strip() or "stdio"
|
2026-07-06 02:29:19 +02:00
|
|
|
mcp, _client = build_server(settings)
|
|
|
|
|
|
2026-07-07 16:52:30 +02:00
|
|
|
if transport in ("streamable-http", "http"):
|
|
|
|
|
_run_http(mcp, settings)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if transport == "stdio" and not settings.api_key:
|
|
|
|
|
raise SystemExit(
|
|
|
|
|
"SURFSENSE_API_KEY is required for stdio transport. Create an API "
|
|
|
|
|
"key in SurfSense (Settings -> API) and pass it via the "
|
|
|
|
|
"SURFSENSE_API_KEY environment variable."
|
|
|
|
|
)
|
2026-07-06 02:29:19 +02:00
|
|
|
mcp.run(transport=transport)
|
|
|
|
|
|
|
|
|
|
|
2026-07-07 16:52:30 +02:00
|
|
|
def _run_http(mcp: FastMCP, settings: Settings) -> None:
|
|
|
|
|
"""Serve the streamable-http app directly, so the per-request identity
|
|
|
|
|
middleware wraps the SDK's MCP endpoint."""
|
|
|
|
|
import uvicorn
|
|
|
|
|
|
|
|
|
|
from .core.transport import build_http_app
|
|
|
|
|
|
|
|
|
|
uvicorn.run(build_http_app(mcp), host=settings.host, port=settings.port)
|
|
|
|
|
|
|
|
|
|
|
2026-07-06 02:29:19 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|