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
A prompt registered through
ReplMcpServerOptions.Prompt(name, handler)cannot take any of Repl'sMCP capability services. A handler declaring
IMcpSampling,IMcpClientRoots,IMcpElicitationorIMcpFeedbackfails theprompts/getrequest outright; one declaringIServiceProviderreceives aprovider that cannot resolve them.
This is not specific to the reusable-options path — it reproduces on
mcp servetoo, whereMcpServer.Createis given the session's provider.Reproduction
Registered on
McpTestFixture(theMcpServerHandlerpath):() => "plain-ok"(IServiceProvider sp) => sp.GetService(typeof(IMcpSampling)) is null ? "sp-null" : "sp-ok"sp-null(IMcpSampling sampling) => sampling.IsSupported ? "on" : "off"Command-backed prompts (
ReplMcpServerPrompt, built from the documentation model) are unaffected —they dispatch through
McpToolAdapter, which resolves against the session services directly.Cause
McpServerHandler.CollectPromptshands explicit registrations to the SDK's ownMcpServerPrompt.Create, which resolves handler parameters fromRequestContext<T>.Services. TheSDK populates that by asking the server's provider for an
IServiceScopeFactoryand opening a scope— and the value observed there is a
ServiceProviderEngineScope, i.e. a scope from the innercontainer.
McpServiceProviderOverlayputs the four capability services in front of that container bydelegation 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.Servicesfrom the wrapper before delegating doesnot 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 atall 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
IMcpClientRoots,IMcpSampling,IMcpElicitationandIMcpFeedbackso that a scopetaken from the session provider still resolves them, rather than layering them over it.
Given_test per path (handler andBuildMcpServerOptions) covering a capability-injectingprompt handler, since the API currently has none.
resolves reflects the client that asked — the point raised in the review thread on feat(mcp)!: migrate to ModelContextProtocol 2.2.0 — requires SDK 2.x, changes IMcpFeedback.SendMessageAsync, and makes the 2026-07-28 tool list invariant #71.