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:
-
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”.
-
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.
Enhancement
I noticed that when calling
ctx.sample(), FastMCP will use the server’ssampling_handleras 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:
For example, in VS Code Copilot (which supports sampling primitives), if the user accepts sampling but then selects zero models,
ctx.sample()will raise:This does not necessarily mean “don’t sample”, it could mean “don’t use VS Code’s provided models for sampling”.
FastMCP allows passing a
sampling_handlerat 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 sameMcpError, otherwise I have to manually fill in a sample result.Suggestion:
Add a configurable behavior (e.g.
sampling_handler_behaviorwith 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 usingsampling_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.