"""Per-request MCP context: who is calling, and which app they are calling. The /mcp transport bypasses Quart's request pipeline, so neither `session` nor `current_app` exists inside a tool. The ASGI auth wrapper in server.py sets these from the resolved bearer token before dispatching; tools read them through `current_caller()` and `mcp_app()`. Neither falls back to a default: a tool reached without an authenticated caller is a bug in the transport, and it raises rather than acting for nobody. """ from __future__ import annotations from contextvars import ContextVar from typing import TYPE_CHECKING if TYPE_CHECKING: from quart import Quart from steward.core.api_tokens import Caller _caller_ctx: ContextVar["Caller | None"] = ContextVar("steward_mcp_caller", default=None) _app_ctx: ContextVar["Quart | None"] = ContextVar("steward_mcp_app", default=None) # The scheme and host the caller reached /mcp on, e.g. "https://steward.example". _base_url_ctx: ContextVar[str] = ContextVar("steward_mcp_base_url", default="") def current_caller() -> "Caller": caller = _caller_ctx.get() if caller is None: raise RuntimeError("no MCP caller in context: the request was not bearer-authenticated") return caller def current_caller_or_none() -> "Caller | None": return _caller_ctx.get() def mcp_app() -> "Quart": app = _app_ctx.get() if app is None: raise RuntimeError("no Steward app in MCP context") return app def public_url() -> str: """Steward's externally reachable base URL, as the web side's public_base_url() gives it: Settings → General → Public base URL when set, else the scheme and host this MCP request arrived on; "" when neither is known.""" app = _app_ctx.get() configured = (app.config.get("PUBLIC_BASE_URL") or "").strip() if app is not None else "" return (configured or _base_url_ctx.get()).rstrip("/")