xj*(dZddlZddlmZmZmZddlmZmZm Z m Z m Z ddl Z ddl Z ddlmZddlmZddlmZej(eZgdZe d ed Zd ed edzfdZded efdZGddeZGdde dZe GddeZGddeeeZeeee ee efdzgee!e effZ" eeee ee efdzgedfZ# ee ee efdzge$fZ% de%dzde dee efdzd e$fdZ&Gdd eeeZ'y)!a3Dispatcher Protocol - the call/return boundary between transports and handlers. A Dispatcher turns a duplex message channel into two things: * an outbound API: `send_raw_request(method, params)` and `notify(method, params)` * an inbound pump: `run(on_request, on_notify)` that drives the receive loop and invokes the supplied handlers for each incoming request/notification It is deliberately *not* MCP-aware. Method names are strings, params and results are `dict[str, Any]`. The MCP type layer (request/result models, capability negotiation, `Context`) sits above this; the wire encoding (JSON-RPC, gRPC, in-process direct calls) sits below it. See `JSONRPCDispatcher` for the production implementation and `DirectDispatcher` for an in-memory implementation used in tests and for embedding a server in-process. N) AwaitableCallableMapping)AnyProtocol TypedDictTypeVarruntime_checkable) RequestId)MessageMetadata)TransportContext) CallOptionsDispatchContext DispatcherOnNotifyOnNotifyIntercept OnRequestOutbound ProgressFnT as_request_idcoerce_request_idrun_notify_intercept TransportT_coT)bound covariantvaluereturncVt|ttzrt|ts|Sy)zeNarrow an untyped wire value to a `RequestId`, or None; rejects bool (True would alias request id 1).N) isinstancestrintbool)rs i/mnt/ssd/data/Dropbox/adrian/sandbox/mcp-query/venv/lib/python3.12/site-packages/mcp/shared/dispatcher.pyrr1s!%s#Jud,C  request_idc^t|tr t|S|S#t$rY|SwxYw)aCoerce a stringified int request id back to int so a peer-echoed id still correlates (matches the TS SDK). This is the collision/correlation domain dispatchers share: "7" and 7 are one id for correlation purposes, even where the wire carries the verbatim value. )rr r! ValueError)r%s r#rr8s? *c" z? "     s  ,,c4eZdZdZdededzdedzddfdZy)rzLCallback invoked when a progress notification arrives for a pending request.progresstotalNmessagerc KywNselfr)r*r+s r#__call__zProgressFnT.__call__Is)__name__ __module__ __qualname____doc__floatr r1r.r$r#rrFs&VdudUT\dCRVJd[_dr$rceZdZUdZeed< eed< eed< eed< e ed< e e ge dfed< e e e fed <y) rzPer-call options for `Outbound.send_raw_request`. All keys are optional. Dispatchers ignore keys they do not understand. r%timeoutcancel_on_abandon on_progressresumption_tokenNon_resumption_tokenheaders) r3r4r5r6r __annotations__r7r"rr rrdictr.r$r#rrLso  N\ D"3%4"899#s(^rr$rF)r*c |eZdZdZ d dedeeefdzdedzdeeeffdZ d dedeeefdzdedzddfdZ y) raEAnything that can send requests and notifications to the peer. Both `Dispatcher` (top-level outbound) and `DispatchContext` (back-channel during an inbound request) extend this. The MCP type layer (`ClientPeer`, `Connection`) builds typed `send_request` / convenience methods on top of this raw channel. Nmethodparamsoptsrc Kyw)a Send a request and await its raw result dict. Raises: MCPError: If the peer responded with an error, or the handler raised. Implementations normalize all handler exceptions to `MCPError` so callers see a single exception type. Nr.r0rBrCrDs r#send_raw_requestzOutbound.send_raw_requests  r2c Kyw)z$Send a fire-and-forget notification.Nr.rFs r#notifyzOutbound.notifys  r2r-) r3r4r5r6r rrrr@rGrIr.r$r#rrs$(    S!D(  D  c3h   3 S0AD0H P[^bPb nr r$rc eZdZdZedefdZedefdZede dzfdZ ede fdZ ede jfdZd d ed edzd edzddfd Zy)ra7Per-request context handed to `on_request` / `on_notify`. Carries the transport metadata for the inbound message and provides the back-channel for sending requests/notifications to the peer while handling it. `send_raw_request` raises `NoBackChannelError` if `can_send_request` is `False`. rcy)z5Transport-specific metadata for this inbound message.Nr.r0s r# transportzDispatchContext.transport r$cy)a-Whether the back-channel can currently deliver server-initiated requests. `False` when the transport has no back-channel, or when this context has been closed (the inbound request finished). `send_raw_request` raises `NoBackChannelError` exactly when this is `False`. Nr.rLs r#can_send_requestz DispatchContext.can_send_request r$Ncy)a'The id of the inbound request, or `None` for a notification. For JSON-RPC this is the wire `id` field. Handlers thread it through as `related_request_id` on outbound notifications so HTTP transports can route them onto the originating request's response stream. Nr.rLs r#r%zDispatchContext.request_idrQr$cy)aThe metadata the transport attached to this inbound message, if any. This is `SessionMessage.metadata` passed through verbatim: HTTP transports attach `ServerMessageMetadata` (the HTTP request, SSE stream-close callbacks); stdio and in-memory dispatch attach nothing. Tied to the `SessionMessage` wire format - goes away when transports stop delivering messages that way. Nr.rLs r#message_metadataz DispatchContext.message_metadatas r$cy)zCSet when the peer sends `notifications/cancelled` for this request.Nr.rLs r#cancel_requestedz DispatchContext.cancel_requestedrNr$r)r*r+c Kyw)zReport progress for the inbound request, if the peer supplied a progress token. A no-op when no token was supplied. Nr.r/s r#r)zDispatchContext.progresss r2)NN)r3r4r5r6propertyrrMr"rPr r%r rTanyioEventrVr7r r)r.r$r#rrs =   $   I,    /     %++   u UT\ SVY]S] im r$r interceptrBrCcf|y |||S#t$rtjd|YywxYw)zYInvoke `intercept`, containing a raise to that one notification (never the receive loop).Fz1notification intercept raised; passing %r through) Exceptionlogger exception)r[rBrCs r#rrs@(( LfUs 00c ~eZdZdZ d ej ddedededzdejjdddf d Z y) ra5A duplex request/notification channel with call-return semantics. Implementations own correlation of outbound requests to inbound results, the receive loop, per-request concurrency, and cancellation/progress wiring. The lifecycle surface is provisional; `run()` may change before v2 stable. N) task_status on_request on_notifyon_notify_interceptrarc Kyw)a~Drive the receive loop until the underlying channel closes. Each inbound request is dispatched to `on_request` in its own task; the returned dict (or raised `MCPError`) is sent back as the response. Implementations MUST offer every inbound notification to `on_notify_intercept` synchronously in receive order (via `run_notify_intercept`), handing only unconsumed ones to `on_notify`. `task_status.started()` is called once the dispatcher is ready to accept `send_request`/`notify` calls, so callers can use `await tg.start(dispatcher.run, on_request, on_notify)`. Nr.)r0rbrcrdras r#runzDispatcher.runs ( r2r-) r3r4r5r6rYTASK_STATUS_IGNOREDrrrabc TaskStatusrfr.r$r#rrse9=  382K2K    /5  YY))$/    r$r)(r6loggingcollections.abcrrrtypingrrrr r rY anyio.abc mcp_typesr mcp.shared.messager mcp.shared.transport_contextr getLoggerr3r^__all__robjectrrrrrrr r@rrr"rrrr.r$r#rts$88GG .9   8 $ /?4P I$4 )  e(e 4s)54sn x  :9 h 79 x o&67gc3h>ORV>VWYbcghkmphpcqYrr s r _%56WS#X=NQU=UVXabfXgg hAc738#4t#;