fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Allow `sampling_handler` fallback even when client rejects sampling?

Open CNSeniorious000 opened this issue 2 months ago • 2 comments

Enhancement

I noticed that when calling ctx.sample(), FastMCP will use the server’s sampling_handler as a fallback only if the client doesn’t support sampling capability.
However, if the client does support sampling and the user/client rejects the request, FastMCP just raises mcp.shared.exceptions.McpError: The user has denied permission to call this method. and don't falls back to the provided handler.

That seems a bit inconsistent — fallback works for low-capability clients, but not when a “better” client explicitly rejects the sampling request.


I think although a user rejection may mean the tool should fail, in practice the meaning of rejection can be ambiguous:

  1. For example, in VS Code Copilot (which supports sampling primitives), if the user accepts sampling but then selects zero models, ctx.sample() will raise:

    mcp.shared.exceptions.McpError: The user has denied permission to call this method.
    

    This does not necessarily mean “don’t sample”, it could mean “don’t use VS Code’s provided models for sampling”.

  2. FastMCP allows passing a sampling_handler at initialization as a fallback, used automatically when the client does not support sampling. But there’s no way to debug this handler in MCP Inspector — rejecting the sampling request triggers the same McpError, otherwise I have to manually fill in a sample result.

Suggestion: Add a configurable behavior (e.g. sampling_handler_behavior with values like "always", "fallback", and perhaps a new one like "lazy"/"best-effort") meaning: always try client first, and if rejected, fall back to the server handler.


Of course this is not strictly necessary — for point (1) developers can use a CLI flag to set sampling_handler_behavior="always" and ask users to do so, or simply wrap their own LLM request helpers instead of using sampling_handler (which is what I’m currently doing); for point (2) they can use the FastMCP Client to test the handler.

I’m just raising it as a possible niche use‑case. If it’s too narrow, feel free to close as unplanned.

CNSeniorious000 avatar Oct 21 '25 01:10 CNSeniorious000

@Strawgate đź‘‹ What do you think?

CNSeniorious000 avatar Oct 21 '25 01:10 CNSeniorious000

I think part of the challenge here is that an error for rejecting a sampling request isn't really part of the specification.

There's basically just "Client Failure" and the message of the failure may or may not semantically mean the sampling request was rejected, but there's no "Rejected Sample Request" error code. In other words, we can't tell the difference between sampling-related errors that occur at the client-side.

Right now the two modes for the sampling handler are always and fallback. I suppose we could introduce an on-error mode which falls back to the sampling handler on any client error encountered during sampling

Would that address your use-case?

strawgate avatar Oct 21 '25 01:10 strawgate