Skip to content

ElicitAsync<T> throws where the untyped ElicitAsync succeeds under an outgoing-request interceptor #1854

Description

@ConnorMoss02

ElicitAsync<T> throws where the untyped ElicitAsync succeeds, when an outgoing-request interceptor is installed.

The untyped overload skips the capability check on purpose in that case, and records why (src/ModelContextProtocol.Core/Server/McpServer.Methods.cs:348-351):

Capability checks (ThrowIfElicitationUnsupported) are intentionally skipped because the interceptor's alternate channel is responsible for delivering the request to the client.

The generic overload runs the check itself before delegating (:414):

ThrowIfElicitationUnsupported(request);

var raw = await ElicitAsync(request, cancellationToken).ConfigureAwait(false);

So with an interceptor installed and a client that does not advertise elicitation, the untyped overload reaches the interceptor and the generic one throws Elicitation is not supported in stateless mode. Since WithOutgoingRequestInterceptor is how the Tasks package delivers outgoing requests, a server eliciting from a background task can use one API and not the other.

SampleAsync (:96-107) and RequestRootsAsync (:302-312) both test the interceptor before the capability check. ElicitAsync<T> is the only overload that does not.

Repro

Initialize a server with empty ClientCapabilities, install an interceptor, then call each overload. The untyped one returns the interceptor's result; the generic one throws.

Suggested fix

Drop the early check from the generic overload and let the untyped one run it, since it already handles both the interceptor and non-interceptor paths. That leaves one source of truth for the check and makes the generic overload match its two siblings.

On scope: WithOutgoingRequestInterceptor is MCPEXP002, so this is an inconsistency between two overloads of an experimental seam rather than a break in stable API. That path has no test coverage either. grep finds no reference to WithOutgoingRequestInterceptor anywhere under tests/.

#1853 implements that, with the first test to cover the interceptor path.

Note

This issue was prepared with AI assistance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions