@@ -1208,7 +1208,13 @@ internal bool IsStatefulSession() =>
12081208 }
12091209
12101210 // Resolve each input request by sending the corresponding JSON-RPC call to the client.
1211- var inputResponses = await ResolveInputRequestsAsync ( inputRequests , cancellationToken ) . ConfigureAwait ( false ) ;
1211+ // Route the outgoing requests via the same DestinationBoundMcpServer used for normal tool
1212+ // handlers, so they go through the POST's response stream (RelatedTransport) rather than
1213+ // the session-level transport. Without this, the messages can race with the client's GET
1214+ // stream startup and be silently dropped by StreamableHttpServerTransport.SendMessageAsync
1215+ // when no GET request has arrived yet.
1216+ var destinationServer = CreateDestinationBoundServer ( request ) ;
1217+ var inputResponses = await ResolveInputRequestsAsync ( destinationServer , inputRequests , cancellationToken ) . ConfigureAwait ( false ) ;
12121218
12131219 // Reconstruct request params with inputResponses and requestState for the retry.
12141220 var paramsObj = request . Params ? . DeepClone ( ) as JsonObject ?? new JsonObject ( ) ;
@@ -1233,12 +1239,15 @@ internal bool IsStatefulSession() =>
12331239
12341240 /// <summary>
12351241 /// Resolves a batch of MRTR input requests concurrently by dispatching each as a standard
1236- /// JSON-RPC request to the client. On the first failure all remaining handlers are cancelled
1237- /// so user-facing flows (sampling/elicitation prompts) don't keep running once the caller has
1238- /// given up, and exceptions from late-completing tasks are observed before the original
1239- /// exception is rethrown.
1242+ /// JSON-RPC request to the client. The requests are routed via <paramref name="destinationServer"/>
1243+ /// so they go out through the POST's response stream (matching the behavior of tool-initiated
1244+ /// server-to-client requests like <c>server.SampleAsync</c>) and avoid racing with the client's
1245+ /// GET stream startup. On the first failure all remaining handlers are cancelled so user-facing
1246+ /// flows (sampling/elicitation prompts) don't keep running once the caller has given up, and
1247+ /// exceptions from late-completing tasks are observed before the original exception is rethrown.
12401248 /// </summary>
1241- private async Task < IDictionary < string , InputResponse > > ResolveInputRequestsAsync (
1249+ private static async Task < IDictionary < string , InputResponse > > ResolveInputRequestsAsync (
1250+ McpServer destinationServer ,
12421251 IDictionary < string , InputRequest > inputRequests ,
12431252 CancellationToken cancellationToken )
12441253 {
@@ -1248,7 +1257,7 @@ private async Task<IDictionary<string, InputResponse>> ResolveInputRequestsAsync
12481257 int i = 0 ;
12491258 foreach ( var kvp in inputRequests )
12501259 {
1251- keyed [ i ++ ] = ( kvp . Key , ResolveInputRequestAsync ( kvp . Value , linkedCts . Token ) ) ;
1260+ keyed [ i ++ ] = ( kvp . Key , ResolveInputRequestAsync ( destinationServer , kvp . Value , linkedCts . Token ) ) ;
12521261 }
12531262
12541263 try
@@ -1279,28 +1288,29 @@ private async Task<IDictionary<string, InputResponse>> ResolveInputRequestsAsync
12791288
12801289 /// <summary>
12811290 /// Resolves a single MRTR <see cref="InputRequest"/> by dispatching it as a standard JSON-RPC
1282- /// request to the client. This is the server-side mirror of the client's input resolution logic,
1283- /// used for backward compatibility when the client doesn't support MRTR.
1291+ /// request to the client via <paramref name="destinationServer"/>. This is the server-side mirror
1292+ /// of the client's input resolution logic, used for backward compatibility when the client doesn't
1293+ /// support MRTR.
12841294 /// </summary>
1285- private async Task < InputResponse > ResolveInputRequestAsync ( InputRequest inputRequest , CancellationToken cancellationToken )
1295+ private static async Task < InputResponse > ResolveInputRequestAsync ( McpServer destinationServer , InputRequest inputRequest , CancellationToken cancellationToken )
12861296 {
12871297 switch ( inputRequest . Method )
12881298 {
12891299 case RequestMethods . ElicitationCreate :
12901300 var elicitParams = inputRequest . ElicitationParams
12911301 ?? throw new McpException ( "Failed to deserialize elicitation parameters from MRTR input request." ) ;
1292- var elicitResult = await ElicitAsync ( elicitParams , cancellationToken ) . ConfigureAwait ( false ) ;
1302+ var elicitResult = await destinationServer . ElicitAsync ( elicitParams , cancellationToken ) . ConfigureAwait ( false ) ;
12931303 return InputResponse . FromElicitResult ( elicitResult ) ;
12941304
12951305 case RequestMethods . SamplingCreateMessage :
12961306 var samplingParams = inputRequest . SamplingParams
12971307 ?? throw new McpException ( "Failed to deserialize sampling parameters from MRTR input request." ) ;
1298- var samplingResult = await SampleAsync ( samplingParams , cancellationToken ) . ConfigureAwait ( false ) ;
1308+ var samplingResult = await destinationServer . SampleAsync ( samplingParams , cancellationToken ) . ConfigureAwait ( false ) ;
12991309 return InputResponse . FromSamplingResult ( samplingResult ) ;
13001310
13011311 case RequestMethods . RootsList :
13021312 var rootsParams = inputRequest . RootsParams ?? new ListRootsRequestParams ( ) ;
1303- var rootsResult = await RequestRootsAsync ( rootsParams , cancellationToken ) . ConfigureAwait ( false ) ;
1313+ var rootsResult = await destinationServer . RequestRootsAsync ( rootsParams , cancellationToken ) . ConfigureAwait ( false ) ;
13041314 return InputResponse . FromRootsResult ( rootsResult ) ;
13051315
13061316 default :
0 commit comments