Files
FabledSteward/steward/mcp/_context.py
T
bvandeusenandClaude Opus 5.5 1d9ac2b46a
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 48s
CI / unit (push) Successful in 1m11s
CI / publish (push) Successful in 1m23s
feat(mcp): built-in playbooks get steward_url from Steward itself
#5015 "Built-in host-agent playbooks make an MCP caller supply steward_url"

- run_playbook, create_schedule and set_alert_action fill a steward-builtin
  playbook's steward_url when the caller leaves it out or empty, from
  Settings → General → Public base URL, else the scheme and host the MCP
  request arrived on — the same source the web actions use.
- run_playbook's reply names what it filled (filled_by_steward), and
  get_playbook shows that value as the variable's default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:30:23 -04:00

51 lines
1.9 KiB
Python

"""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("/")