Skip to content

mcp: explicitly registered prompts cannot inject the MCP capability services #96

Description

@carldebilly

A prompt registered through ReplMcpServerOptions.Prompt(name, handler) cannot take any of Repl's
MCP capability services. A handler declaring IMcpSampling, IMcpClientRoots, IMcpElicitation or
IMcpFeedback fails the prompts/get request outright; one declaring IServiceProvider receives a
provider that cannot resolve them.

This is not specific to the reusable-options path — it reproduces on mcp serve too, where
McpServer.Create is given the session's provider.

Reproduction

Registered on McpTestFixture (the McpServerHandler path):

handler result
() => "plain-ok" works
(IServiceProvider sp) => sp.GetService(typeof(IMcpSampling)) is null ? "sp-null" : "sp-ok" sp-null
(IMcpSampling sampling) => sampling.IsSupported ? "on" : "off" request fails

Command-backed prompts (ReplMcpServerPrompt, built from the documentation model) are unaffected —
they dispatch through McpToolAdapter, which resolves against the session services directly.

Cause

McpServerHandler.CollectPrompts hands explicit registrations to the SDK's own
McpServerPrompt.Create, which resolves handler parameters from RequestContext<T>.Services. The
SDK populates that by asking the server's provider for an IServiceScopeFactory and opening a scope
— and the value observed there is a ServiceProviderEngineScope, i.e. a scope from the inner
container.

McpServiceProviderOverlay puts the four capability services in front of that container by
delegation only. It is not scope-aware: a scope factory obtained through it belongs to the inner
provider, so every service resolved from the resulting scope is resolved without the overlay.

Verified while reviewing #71: assigning request.Services from the wrapper before delegating does
not change the outcome, so supplying the provider later is not the missing piece.

Why it is filed rather than fixed in #71

The migration does not introduce it and does not widen it — Prompt(...) has no test coverage at
all today, on either path. The fix is the scoping question already tracked by #74 and #70: the
capability services need to live in the container the SDK scopes from, instead of an overlay wrapped
around it. Doing that here would pull the DI rework into the SDK migration.

What a fix needs

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

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions