Summary
The Rust binding exposes MCP sampling on the wire — the sampling.requested / sampling.completed events and the handle_pending_sampling (and execute_sampling / cancel_sampling_execution) methods — but there are two gaps for a consumer that wants to service MCP sampling requests itself (i.e. answer an MCP server's sampling/createMessage with its own model):
- The request content isn't on the typed event.
SamplingRequestedData carries only { requestId, serverName, mcpRequestId } — not the MCP CreateMessageRequest params (the messages, modelPreferences, systemPrompt, maxTokens, etc.) that a handler needs to actually produce a completion. So a consumer receives "a sampling request happened" but not what to sample.
- There's no ergonomic handler. Every other consumer-serviced request has a first-class handler trait (
PermissionHandler, ElicitationHandler, McpAuthHandler, UserInputHandler, …) wired on the session builder. Sampling has none — a consumer must subscribe to the raw sampling.requested event and call the low-level, Experimental handle_pending_sampling by hand.
Current state (public Rust binding)
rust/src/generated/session_events.rs:
pub struct SamplingRequestedData {
pub mcp_request_id: serde_json::Value,
pub request_id: RequestId,
pub server_name: String,
}
pub struct SamplingCompletedData { pub request_id: RequestId }
No sampling request params (messages / model preferences) are present.
rust/src/generated/rpc.rs: handle_pending_sampling(UIHandlePendingSamplingRequest) -> UIHandlePendingResult, execute_sampling(...), cancel_sampling_execution(...) — all marked Experimental.
rust/src/handler.rs: ergonomic handler traits exist for permission, elicitation, MCP auth, user input, exit-plan-mode, auto-mode-switch — but not sampling.
rust/src/session.rs: those handlers are wired on the builder (e.g. mcp_auth_handler); there is no sampling equivalent.
Why this is needed
This is a developer-experience / completeness improvement for consumers that want to fulfill MCP sampling requests with their own inference instead of the default behavior. Today such a consumer:
- can't get the request content from the typed event (has to reach past
SamplingRequestedData), and
- has to hand-wire the raw event + Experimental RPC instead of implementing one trait.
It is not a capability blocker (the low-level RPCs exist), so this is a quality-of-life ask, not urgent.
Proposed change
- Expose the MCP sampling request params on
SamplingRequestedData as a typed field (the CreateMessageRequest params: messages, modelPreferences, systemPrompt, includeContext, maxTokens, temperature, stopSequences, metadata), so a handler can read what to sample directly from the event.
- Add an ergonomic
SamplingHandler trait in rust/src/handler.rs mirroring McpAuthHandler:
#[async_trait]
pub trait SamplingHandler: Send + Sync + 'static {
async fn handle(
&self,
session_id: SessionId,
request_id: RequestId,
request: SamplingRequest, // typed CreateMessageRequest params
) -> SamplingResult; // completion, or None/err to reject
}
Wire it on the session builder alongside the other handlers, and have the binding translate the trait's result into the existing handle_pending_sampling call (and reject/cancel when the handler declines), so consumers never touch the raw event or the Experimental RPC directly.
Acceptance criteria
- A consumer can register one
SamplingHandler on the session builder and receive the typed MCP sampling request (messages + model preferences), returning a completion or a rejection.
- The sampling request params are available as typed data (not only reachable via the untyped/low-level path).
- The existing low-level
handle_pending_sampling / execute_sampling methods continue to work for consumers that prefer them.
References
rust/src/handler.rs — existing handler-trait pattern (McpAuthHandler et al.) to mirror.
rust/src/generated/session_events.rs — SamplingRequestedData / SamplingCompletedData.
rust/src/generated/rpc.rs — handle_pending_sampling, execute_sampling, cancel_sampling_execution.
- MCP spec —
sampling/createMessage and its CreateMessageRequest params.
Summary
The Rust binding exposes MCP sampling on the wire — the
sampling.requested/sampling.completedevents and thehandle_pending_sampling(andexecute_sampling/cancel_sampling_execution) methods — but there are two gaps for a consumer that wants to service MCP sampling requests itself (i.e. answer an MCP server'ssampling/createMessagewith its own model):SamplingRequestedDatacarries only{ requestId, serverName, mcpRequestId }— not the MCPCreateMessageRequestparams (themessages,modelPreferences,systemPrompt,maxTokens, etc.) that a handler needs to actually produce a completion. So a consumer receives "a sampling request happened" but not what to sample.PermissionHandler,ElicitationHandler,McpAuthHandler,UserInputHandler, …) wired on the session builder. Sampling has none — a consumer must subscribe to the rawsampling.requestedevent and call the low-level, Experimentalhandle_pending_samplingby hand.Current state (public Rust binding)
rust/src/generated/session_events.rs:rust/src/generated/rpc.rs:handle_pending_sampling(UIHandlePendingSamplingRequest) -> UIHandlePendingResult,execute_sampling(...),cancel_sampling_execution(...)— all marked Experimental.rust/src/handler.rs: ergonomic handler traits exist for permission, elicitation, MCP auth, user input, exit-plan-mode, auto-mode-switch — but not sampling.rust/src/session.rs: those handlers are wired on the builder (e.g.mcp_auth_handler); there is no sampling equivalent.Why this is needed
This is a developer-experience / completeness improvement for consumers that want to fulfill MCP sampling requests with their own inference instead of the default behavior. Today such a consumer:
SamplingRequestedData), andIt is not a capability blocker (the low-level RPCs exist), so this is a quality-of-life ask, not urgent.
Proposed change
SamplingRequestedDataas a typed field (theCreateMessageRequestparams:messages,modelPreferences,systemPrompt,includeContext,maxTokens,temperature,stopSequences,metadata), so a handler can read what to sample directly from the event.SamplingHandlertrait inrust/src/handler.rsmirroringMcpAuthHandler:handle_pending_samplingcall (and reject/cancel when the handler declines), so consumers never touch the raw event or the Experimental RPC directly.Acceptance criteria
SamplingHandleron the session builder and receive the typed MCP sampling request (messages + model preferences), returning a completion or a rejection.handle_pending_sampling/execute_samplingmethods continue to work for consumers that prefer them.References
rust/src/handler.rs— existing handler-trait pattern (McpAuthHandleret al.) to mirror.rust/src/generated/session_events.rs—SamplingRequestedData/SamplingCompletedData.rust/src/generated/rpc.rs—handle_pending_sampling,execute_sampling,cancel_sampling_execution.sampling/createMessageand itsCreateMessageRequestparams.