You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Moves the MCP server onto protocol revision 2026-07-28 (MCP C# SDK 2.2.0, merged in #231) while keeping clients that use the initialize handshake working.
1. Destructive-command confirmation via multi-round-trip requests
Destructive commands (delete, rm, rmcon, rmdb) asked for confirmation through McpServer.ElicitAsync. That is a server-to-client request; it needs a session, and 2026-07-28 removes it from Streamable HTTP.
Prompt transport (ToolOperations)
The prompt is raised as InputRequiredException with one elicitation input request under key confirm. The prompt text is unchanged.
2026-07-28 clients show the prompt and retry tools/call with the answer.
For initialize clients, the SDK sends elicitation/create on the session and retries the handler, so they see the same prompt as before.
Clients that don't advertise elicitation are still refused before anything runs.
On stateless requests the SDK reports ClientCapabilities as null, so the gate falls back to the per-request capabilities in JsonRpcRequest.Context.
Binding the answer (new ConfirmationRequestState)
requestState holds stateVersion + "\n" + commandLine, signed with HMAC-SHA256 using a random key generated per process.
On retry, a state that is missing, forged, or for another command line is refused.
If the shell state version changed between rounds, the existing "context changed" error is returned.
Decline or cancel returns the existing "not approved" error.
A server restart invalidates pending confirmations, because the key changes.
Behavior change: for initialize clients, if the elicitation request itself fails, the SDK now returns a JSON-RPC error instead of the shell's "could not be completed" tool error. Nothing executes in either case.
2. subscriptions/listen for cosmos://shell/current-location
The SDK's built-in listen handler never sends notifications/resources/updated, and on stateless requests it grants nothing, so LocationResourceSubscriptions.ListenAsync replaces it.
Sends notifications/subscriptions/acknowledged listing only cosmos://shell/current-location. Other URIs and list-changed filters are left out.
If nothing is honored, the request completes right after the acknowledgement.
Otherwise it streams notifications/resources/updated on the listen response. Every notification carries the listen request ID in _meta["io.modelcontextprotocol/subscriptionId"].
The listener is removed when the client cancels or disconnects, or when the host stops. Held-open POSTs are not ended by the transport on shutdown, so the service does that itself.
resources/subscribe / resources/unsubscribe remain for initialize clients, with the existing session-lifetime behavior.
Retry flow in ExecuteToolAsync: the first round raises the input request without executing; accept executes; decline, cancel, an answer for another command, and a context change between rounds do not execute.
McpConfirmationTests: end to end over HTTP for both 2026-07-28 (native multi-round-trip) and 2025-11-25 (bridged to elicitation/create). The client's decline is honored in both.
ListeningClient_ReceivesLocationChangeOnListenStream: checks the acknowledgement contents and subscription ID, delivery of a location change, and that cancelling releases the listener.
The existing resources/subscribe tests now pin 2025-11-25. The transport test asserts the new session mode.
Docs
docs/mcp.md covers the multi-round-trip confirmation flow, both subscription mechanisms, and which clients get a session.
The linked token is intended to combine request cancellation with StopAsync, but the acknowledgement write still receives only the request token. If shutdown begins while this write is pending, cancelling stopping cannot release the held-open POST and can delay host shutdown. Pass the linked token here.
This issue also appears on line 184 of the same file.
…order-independent
The web server stops before LocationResourceSubscriptions and waits for open
requests until the host shutdown timeout, so cancelling held-open listen POSTs
in StopAsync came too late and shutdown took 30 s. Cancel them when the
application starts stopping instead.
CallTool_EchoCommand_ReturnsSuccessResult relied on its history entry being new;
history drops duplicates and another test records the same echo line, so the
new tests' execution order made it fail. Use a unique message.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each subscriptions/listen request now drains its own coalescing queue and sends
with its own request-linked token, so a slow or stalled stream delays only
itself. The acknowledgement also uses the linked token, so shutdown releases it.
Pending confirmation nonces are tracked in creation order and pruned from the
front, capped at 1,024 entries; beyond that the oldest is dropped.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverses the confirmation flow: the server supplies the elicitation prompt and asks the client for an answer; it does not ask the client to provide a prompt. Reword this so the documentation matches InputRequest.ForElicitation.
Fixed the "Correct elicitation flow documentation" finding in 7dcefc4: docs/mcp.md now says the server sends the client an elicitation prompt describing the exact command line and waits for the user's answer before anything runs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the MCP server onto protocol revision
2026-07-28(MCP C# SDK 2.2.0, merged in #231) while keeping clients that use theinitializehandshake working.1. Destructive-command confirmation via multi-round-trip requests
Destructive commands (
delete,rm,rmcon,rmdb) asked for confirmation throughMcpServer.ElicitAsync. That is a server-to-client request; it needs a session, and2026-07-28removes it from Streamable HTTP.ToolOperations)InputRequiredExceptionwith one elicitation input request under keyconfirm. The prompt text is unchanged.2026-07-28clients show the prompt and retrytools/callwith the answer.initializeclients, the SDK sendselicitation/createon the session and retries the handler, so they see the same prompt as before.ClientCapabilitiesas null, so the gate falls back to the per-request capabilities inJsonRpcRequest.Context.ConfirmationRequestState)requestStateholdsstateVersion + "\n" + commandLine, signed with HMAC-SHA256 using a random key generated per process.initializeclients, if the elicitation request itself fails, the SDK now returns a JSON-RPC error instead of the shell's "could not be completed" tool error. Nothing executes in either case.2.
subscriptions/listenforcosmos://shell/current-locationThe SDK's built-in listen handler never sends
notifications/resources/updated, and on stateless requests it grants nothing, soLocationResourceSubscriptions.ListenAsyncreplaces it.notifications/subscriptions/acknowledgedlisting onlycosmos://shell/current-location. Other URIs and list-changed filters are left out.notifications/resources/updatedon the listen response. Every notification carries the listen request ID in_meta["io.modelcontextprotocol/subscriptionId"].resources/subscribe/resources/unsubscriberemain forinitializeclients, with the existing session-lifetime behavior.3.
HttpServerSessionMode.StatefulForInitializeClients2026-07-28requests are served without a session.initializeclients still get a session, used for elicitation andresources/subscribe.RunSessionAsync,SubscribeandUnsubscribetreat an empty session ID as no session.Tests
ConfirmationRequestState: round trip, mismatched command, missing or forged state, tampered payload.ExecuteToolAsync: the first round raises the input request without executing; accept executes; decline, cancel, an answer for another command, and a context change between rounds do not execute.McpConfirmationTests: end to end over HTTP for both2026-07-28(native multi-round-trip) and2025-11-25(bridged toelicitation/create). The client's decline is honored in both.ListeningClient_ReceivesLocationChangeOnListenStream: checks the acknowledgement contents and subscription ID, delivery of a location change, and that cancelling releases the listener.resources/subscribetests now pin2025-11-25. The transport test asserts the new session mode.Docs
docs/mcp.mdcovers the multi-round-trip confirmation flow, both subscription mechanisms, and which clients get a session.