Fix missing resultType on complete result responses (#1684)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tarek Mahmoud Sayed <tarekms@ntdev.microsoft.com>
This commit is contained in:
Pranav Senthilnathan 2026-07-12 21:24:21 -04:00 • committed by GitHub
commit 374e76eecd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 118 additions and 4 deletions

View file

@ -9,5 +9,5 @@ namespace ModelContextProtocol.Protocol;
public sealed class EmptyResult : Result
{
[JsonIgnore]
internal static EmptyResult Instance { get; } = new();
internal static EmptyResult Instance { get; } = new() { ResultType = "complete" };
}

View file

@ -27,7 +27,7 @@ public abstract class Result
/// </summary>
/// <remarks>
/// <para>
/// When absent or set to <c>"complete"</c>, the result is a normal completed response.
/// When set to <c>"complete"</c>, the result is a normal completed response.
/// When set to <c>"input_required"</c>, the result is an <see cref="InputRequiredResult"/> indicating
/// that additional input is needed before the request can be completed.
/// When set to <c>"task"</c>, the result is a <see cref="CreateTaskResult"/> indicating that the server

View file

@ -621,6 +621,7 @@ internal sealed partial class McpServerImpl : McpServer
Instructions = options.ServerInstructions,
ServerInfo = options.ServerInfo ?? DefaultImplementation,
Capabilities = ServerCapabilities ?? new(),
ResultType = "complete",
};
},
McpJsonUtilities.JsonContext.Default.InitializeRequestParams,
@ -651,6 +652,7 @@ internal sealed partial class McpServerImpl : McpServer
// their "do not cache" behavior while satisfying the wire requirement.
TimeToLive = TimeSpan.Zero,
CacheScope = CacheScope.Private,
ResultType = "complete",
});
},
McpJsonUtilities.JsonContext.Default.DiscoverRequestParams,
@ -703,7 +705,7 @@ internal sealed partial class McpServerImpl : McpServer
await SendSubscriptionAckAsync(statelessSubscription, cancellationToken).ConfigureAwait(false);
return new EmptyResult();
return EmptyResult.Instance;
}
// Filter the requested notifications against what the server actually supports.
@ -743,7 +745,7 @@ internal sealed partial class McpServerImpl : McpServer
_activeSubscriptions.TryRemove(jsonRpcRequest.Id, out _);
}
return new EmptyResult();
return EmptyResult.Instance;
},
McpJsonUtilities.JsonContext.Default.SubscriptionsListenRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult);
@ -1916,6 +1918,21 @@ internal sealed partial class McpServerImpl : McpServer
};
}
if (typeof(Result).IsAssignableFrom(typeof(TResult)))
{
var innerHandler = handler;
handler = async (request, cancellationToken) =>
{
var result = await innerHandler(request, cancellationToken).ConfigureAwait(false);
if (result is Result protocolResult && protocolResult.ResultType is null)
{
protocolResult.ResultType = "complete";
}
return result;
};
}
_requestHandlers.Set(method,
(request, jsonRpcRequest, cancellationToken) =>
InvokeHandlerAsync(handler, request, jsonRpcRequest, cancellationToken),
@ -1930,6 +1947,25 @@ internal sealed partial class McpServerImpl : McpServer
JsonTypeInfo<CreateTaskResult> taskResultTypeInfo)
where TResult : Result
{
var innerHandler = handler;
handler = async (request, cancellationToken) =>
{
var result = await innerHandler(request, cancellationToken).ConfigureAwait(false);
if (result.IsTask)
{
if (result.TaskCreated is { ResultType: null } taskCreated)
{
taskCreated.ResultType = "task";
}
}
else if (result.Result is { ResultType: null } immediateResult)
{
immediateResult.ResultType = "complete";
}
return result;
};
_requestHandlers.SetTaskAugmented(method,
(request, jsonRpcRequest, cancellationToken) =>
InvokeHandlerAsync(handler, request, jsonRpcRequest, cancellationToken),