Rust: ergonomic SamplingHandler + typed MCP sampling request on sampling.requested

Open
#1,942 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
rust

Research direction

Start with rust/src/generated/session_events.rs and rust/src/handler.rs, comparing SamplingRequestedData with the existing handler traits. Then trace session-builder wiring in rust/src/session.rs and the sampling RPCs in rust/src/generated/rpc.rs. Done means a typed sampling request and registered SamplingHandler work together while the existing low-level methods remain usable.

Written by the indexing model from the issue text.

Description

enhancement wishlist

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):

  1. 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.
  2. 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

  1. 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.
  2. 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.rsSamplingRequestedData / SamplingCompletedData.
  • rust/src/generated/rpc.rshandle_pending_sampling, execute_sampling, cancel_sampling_execution.
  • MCP spec — sampling/createMessage and its CreateMessageRequest params.
Dominant language
Java
Stars
10.5k
Forks
1.5k
Avg merge
1d 12h
Merged PRs (30d)
133

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from github/copilot-sdk

All issues in github/copilot-sdk

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.