"""`BaseContext` - the user-facing per-request context. Composition over a `DispatchContext`: forwards the transport metadata, the back-channel (`send_raw_request`/`notify`), progress reporting, and the cancel event. Adds `meta` (the inbound request's `_meta` field). Satisfies `Outbound`, so `ClientPeer` can wrap it. Shared between client and server: the server's `Context` extends this with `lifespan`/`connection`; `ClientContext` is just an alias. """ from collections.abc import Mapping from typing import Any, Generic import anyio from mcp_types import RequestParamsMeta from typing_extensions import TypeVar from mcp.shared.dispatcher import CallOptions, DispatchContext from mcp.shared.transport_context import TransportContext __all__ = ["BaseContext"] TransportT = TypeVar("TransportT", bound=TransportContext, default=TransportContext, covariant=True) class BaseContext(Generic[TransportT]): """Per-request context wrapping a `DispatchContext`. `ServerRunner` constructs one per inbound request and passes it to the user's handler. """ def __init__(self, dctx: DispatchContext[TransportT], meta: RequestParamsMeta | None = None) -> None: self._dctx = dctx self._meta = meta @property def transport(self) -> TransportT: """Transport-specific metadata for this inbound request.""" return self._dctx.transport @property def cancel_requested(self) -> anyio.Event: """Set when the peer sends `notifications/cancelled` for this request.""" return self._dctx.cancel_requested @property def can_send_request(self) -> bool: """Whether the back-channel can currently deliver server-initiated requests. `False` when the transport has no back-channel, or when the underlying dispatch context has been closed because the inbound request finished. """ return self._dctx.can_send_request @property def meta(self) -> RequestParamsMeta | None: """The inbound request's `_meta` field, if present.""" return self._meta async def send_raw_request( self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None, ) -> dict[str, Any]: """Send a request to the peer on the back-channel. Raises: MCPError: The peer responded with an error. NoBackChannelError: `can_send_request` is `False`. """ return await self._dctx.send_raw_request(method, params, opts) async def notify(self, method: str, params: Mapping[str, Any] | None, opts: CallOptions | None = None) -> None: """Send a notification to the peer on the back-channel.""" await self._dctx.notify(method, params, opts) async def report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None: """Report progress for this request, if the peer supplied a progress token. A no-op when no token was supplied. """ await self._dctx.progress(progress, total, message)