refactor: split RequestContext between server and client (#1987)

This commit is contained in:
Marcelo Trylesinski 2026-02-03 14:35:07 +01:00 • committed by GitHub
commit b1f7eec3cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 210 additions and 212 deletions

View file

@ -1,30 +1,20 @@
"""Request context for MCP handlers."""
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Any, Generic
from typing_extensions import TypeVar
from mcp.shared.message import CloseSSEStreamCallback
from mcp.shared.session import BaseSession
from mcp.types import RequestId, RequestParamsMeta
SessionT = TypeVar("SessionT", bound=BaseSession[Any, Any, Any, Any, Any])
LifespanContextT = TypeVar("LifespanContextT")
RequestT = TypeVar("RequestT", default=Any)
@dataclass
class RequestContext(Generic[SessionT, LifespanContextT, RequestT]):
@dataclass(kw_only=True)
class RequestContext(Generic[SessionT]):
"""Common context for handling incoming requests."""
request_id: RequestId
meta: RequestParamsMeta | None
session: SessionT
lifespan_context: LifespanContextT
# NOTE: This is typed as Any to avoid circular imports. The actual type is
# mcp.server.experimental.request_context.Experimental, but importing it here
# triggers mcp.server.__init__ -> mcpserver -> tools -> back to this module.
# The Server sets this to an Experimental instance at runtime.
experimental: Any = field(default=None)
request: RequestT | None = None
close_sse_stream: CloseSSEStreamCallback | None = None
close_standalone_sse_stream: CloseSSEStreamCallback | None = None

View file

@ -5,15 +5,7 @@ from typing import Generic
from pydantic import BaseModel
from mcp.shared.context import LifespanContextT, RequestContext
from mcp.shared.session import (
BaseSession,
ReceiveNotificationT,
ReceiveRequestT,
SendNotificationT,
SendRequestT,
SendResultT,
)
from mcp.shared.context import RequestContext, SessionT
from mcp.types import ProgressToken
@ -23,8 +15,8 @@ class Progress(BaseModel):
@dataclass
class ProgressContext(Generic[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]):
session: BaseSession[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]
class ProgressContext(Generic[SessionT]):
session: SessionT
progress_token: ProgressToken
total: float | None
current: float = field(default=0.0, init=False)
@ -39,15 +31,9 @@ class ProgressContext(Generic[SendRequestT, SendNotificationT, SendResultT, Rece
@contextmanager
def progress(
ctx: RequestContext[
BaseSession[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT],
LifespanContextT,
],
ctx: RequestContext[SessionT],
total: float | None = None,
) -> Generator[
ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT],
None,
]:
) -> Generator[ProgressContext[SessionT], None]:
progress_token = ctx.meta.get("progress_token") if ctx.meta else None
if progress_token is None: # pragma: no cover
raise ValueError("No progress token provided")