Multi Round-Trip Requests (MRTR) (#1458)
This commit is contained in:
parent
8202bcc932
commit
f6dbe4372f
7 changed files with 5832 additions and 103 deletions
197
src/ModelContextProtocol.Core/Protocol/InputRequest.cs
Normal file
197
src/ModelContextProtocol.Core/Protocol/InputRequest.cs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a server-initiated request that the client must fulfill as part of an MRTR
|
||||
/// (Multi Round-Trip Request) flow.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// An <see cref="InputRequest"/> wraps a server-to-client request such as
|
||||
/// <see cref="RequestMethods.SamplingCreateMessage"/>, <see cref="RequestMethods.ElicitationCreate"/>,
|
||||
/// or <see cref="RequestMethods.RootsList"/>. It is included in an <see cref="InputRequiredResult"/>
|
||||
/// when the server needs additional input before it can complete a client-initiated request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The <see cref="Method"/> property identifies the type of request, and the corresponding
|
||||
/// parameters can be accessed via the typed accessor properties.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public sealed class InputRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the method name identifying the type of this input request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Standard values include:
|
||||
/// <list type="bullet">
|
||||
/// <item><term><see cref="RequestMethods.SamplingCreateMessage"/></term><description>A sampling request.</description></item>
|
||||
/// <item><term><see cref="RequestMethods.ElicitationCreate"/></term><description>An elicitation request.</description></item>
|
||||
/// <item><term><see cref="RequestMethods.RootsList"/></term><description>A roots list request.</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
[JsonPropertyName("method")]
|
||||
public required string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw JSON parameters for this input request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use the typed accessor properties (<see cref="SamplingParams"/>, <see cref="ElicitationParams"/>,
|
||||
/// <see cref="RootsParams"/>) for convenient strongly-typed access.
|
||||
/// </remarks>
|
||||
[JsonPropertyName("params")]
|
||||
public JsonElement? Params { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters as <see cref="CreateMessageRequestParams"/> when <see cref="Method"/>
|
||||
/// is <see cref="RequestMethods.SamplingCreateMessage"/>.
|
||||
/// </summary>
|
||||
/// <returns>The deserialized sampling parameters, or <see langword="null"/> if the method does not match or params are absent.</returns>
|
||||
[JsonIgnore]
|
||||
public CreateMessageRequestParams? SamplingParams =>
|
||||
string.Equals(Method, RequestMethods.SamplingCreateMessage, StringComparison.Ordinal) && Params is { } p
|
||||
? JsonSerializer.Deserialize(p, McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams)
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters as <see cref="ElicitRequestParams"/> when <see cref="Method"/>
|
||||
/// is <see cref="RequestMethods.ElicitationCreate"/>.
|
||||
/// </summary>
|
||||
/// <returns>The deserialized elicitation parameters, or <see langword="null"/> if the method does not match or params are absent.</returns>
|
||||
[JsonIgnore]
|
||||
public ElicitRequestParams? ElicitationParams =>
|
||||
string.Equals(Method, RequestMethods.ElicitationCreate, StringComparison.Ordinal) && Params is { } p
|
||||
? JsonSerializer.Deserialize(p, McpJsonUtilities.JsonContext.Default.ElicitRequestParams)
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters as <see cref="ListRootsRequestParams"/> when <see cref="Method"/>
|
||||
/// is <see cref="RequestMethods.RootsList"/>.
|
||||
/// </summary>
|
||||
/// <returns>The deserialized roots list parameters, or <see langword="null"/> if the method does not match or params are absent.</returns>
|
||||
[JsonIgnore]
|
||||
public ListRootsRequestParams? RootsParams =>
|
||||
string.Equals(Method, RequestMethods.RootsList, StringComparison.Ordinal) && Params is { } p
|
||||
? JsonSerializer.Deserialize(p, McpJsonUtilities.JsonContext.Default.ListRootsRequestParams)
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputRequest"/> for a sampling request.
|
||||
/// </summary>
|
||||
/// <param name="requestParams">The sampling request parameters.</param>
|
||||
/// <returns>A new <see cref="InputRequest"/> instance.</returns>
|
||||
public static InputRequest ForSampling(CreateMessageRequestParams requestParams)
|
||||
{
|
||||
Throw.IfNull(requestParams);
|
||||
return new()
|
||||
{
|
||||
Method = RequestMethods.SamplingCreateMessage,
|
||||
Params = JsonSerializer.SerializeToElement(requestParams, McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputRequest"/> for an elicitation request.
|
||||
/// </summary>
|
||||
/// <param name="requestParams">The elicitation request parameters.</param>
|
||||
/// <returns>A new <see cref="InputRequest"/> instance.</returns>
|
||||
public static InputRequest ForElicitation(ElicitRequestParams requestParams)
|
||||
{
|
||||
Throw.IfNull(requestParams);
|
||||
return new()
|
||||
{
|
||||
Method = RequestMethods.ElicitationCreate,
|
||||
Params = JsonSerializer.SerializeToElement(requestParams, McpJsonUtilities.JsonContext.Default.ElicitRequestParams),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputRequest"/> for a roots list request.
|
||||
/// </summary>
|
||||
/// <param name="requestParams">The roots list request parameters.</param>
|
||||
/// <returns>A new <see cref="InputRequest"/> instance.</returns>
|
||||
public static InputRequest ForRootsList(ListRootsRequestParams requestParams)
|
||||
{
|
||||
Throw.IfNull(requestParams);
|
||||
return new()
|
||||
{
|
||||
Method = RequestMethods.RootsList,
|
||||
Params = JsonSerializer.SerializeToElement(requestParams, McpJsonUtilities.JsonContext.Default.ListRootsRequestParams),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Provides JSON serialization support for <see cref="InputRequest"/>.</summary>
|
||||
public sealed class Converter : JsonConverter<InputRequest>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override InputRequest? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException("Expected StartObject token.");
|
||||
}
|
||||
|
||||
string? method = null;
|
||||
JsonElement? parameters = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
throw new JsonException("Expected PropertyName token.");
|
||||
}
|
||||
|
||||
string propertyName = reader.GetString()!;
|
||||
reader.Read();
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case "method":
|
||||
method = reader.GetString();
|
||||
break;
|
||||
case "params":
|
||||
parameters = JsonElement.ParseValue(ref reader);
|
||||
break;
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (method is null)
|
||||
{
|
||||
throw new JsonException("InputRequest must have a 'method' property.");
|
||||
}
|
||||
|
||||
return new InputRequest
|
||||
{
|
||||
Method = method,
|
||||
Params = parameters,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, InputRequest value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("method", value.Method);
|
||||
if (value.Params is { } p)
|
||||
{
|
||||
writer.WritePropertyName("params");
|
||||
p.WriteTo(writer);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/ModelContextProtocol.Core/Protocol/InputRequiredException.cs
Normal file
109
src/ModelContextProtocol.Core/Protocol/InputRequiredException.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown by a server handler to return an <see cref="Protocol.InputRequiredResult"/>
|
||||
/// to the client, signaling that additional input is needed before the request can be completed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This exception is part of the Multi Round-Trip Requests (MRTR) API. Tool handlers
|
||||
/// throw this exception to directly control the input-required result payload, including
|
||||
/// <see cref="Protocol.InputRequiredResult.InputRequests"/> and <see cref="Protocol.InputRequiredResult.RequestState"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For stateless servers, this enables multi-round-trip flows without requiring the handler to stay
|
||||
/// alive between round trips. The server encodes its state in <see cref="Protocol.InputRequiredResult.RequestState"/>
|
||||
/// and receives it back on retry via <see cref="RequestParams.RequestState"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To return a <c>requestState</c>-only response (e.g., for load shedding), omit
|
||||
/// <see cref="Protocol.InputRequiredResult.InputRequests"/> and set only <see cref="Protocol.InputRequiredResult.RequestState"/>.
|
||||
/// The client will retry the request with the state echoed back.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This exception can only be used when MRTR is supported by the client. Check
|
||||
/// <see cref="Server.McpServer.IsMrtrSupported"/> before throwing. If thrown when MRTR is not
|
||||
/// supported, the exception will propagate as a JSON-RPC internal error.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [McpServerTool, Description("A stateless tool using MRTR")]
|
||||
/// public static string MyTool(McpServer server, RequestContext<CallToolRequestParams> context)
|
||||
/// {
|
||||
/// if (context.Params.RequestState is { } state)
|
||||
/// {
|
||||
/// // Retry: process accumulated state and input responses
|
||||
/// var responses = context.Params.InputResponses;
|
||||
/// return "Final result";
|
||||
/// }
|
||||
///
|
||||
/// if (!server.IsMrtrSupported)
|
||||
/// {
|
||||
/// return "This tool requires MRTR support.";
|
||||
/// }
|
||||
///
|
||||
/// throw new InputRequiredException(
|
||||
/// inputRequests: new Dictionary<string, InputRequest>
|
||||
/// {
|
||||
/// ["user_input"] = InputRequest.ForElicitation(new ElicitRequestParams { ... })
|
||||
/// },
|
||||
/// requestState: "encoded-state");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
public class InputRequiredException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputRequiredException"/> class
|
||||
/// with the specified <see cref="Protocol.InputRequiredResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The input-required result to return to the client.</param>
|
||||
public InputRequiredException(InputRequiredResult result)
|
||||
: base("The server returned an input-required result requiring additional client input.")
|
||||
{
|
||||
Throw.IfNull(result);
|
||||
Result = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputRequiredException"/> class
|
||||
/// with the specified input requests and/or request state.
|
||||
/// </summary>
|
||||
/// <param name="inputRequests">
|
||||
/// Server-initiated requests that the client must fulfill before retrying.
|
||||
/// Keys are server-assigned identifiers.
|
||||
/// </param>
|
||||
/// <param name="requestState">
|
||||
/// Opaque state to be echoed back by the client when retrying. The client must
|
||||
/// treat this as an opaque blob and must not inspect or modify it.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Both <paramref name="inputRequests"/> and <paramref name="requestState"/> are <see langword="null"/>.
|
||||
/// At least one must be provided.
|
||||
/// </exception>
|
||||
public InputRequiredException(
|
||||
IDictionary<string, InputRequest>? inputRequests = null,
|
||||
string? requestState = null)
|
||||
: base("The server returned an input-required result requiring additional client input.")
|
||||
{
|
||||
if (inputRequests is null && requestState is null)
|
||||
{
|
||||
throw new ArgumentException("At least one of inputRequests or requestState must be provided.");
|
||||
}
|
||||
|
||||
Result = new InputRequiredResult
|
||||
{
|
||||
InputRequests = inputRequests,
|
||||
RequestState = requestState,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input-required result to return to the client.
|
||||
/// </summary>
|
||||
public InputRequiredResult Result { get; }
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an input-required result sent by the server to indicate that additional input is needed
|
||||
/// before the request can be completed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// An <see cref="InputRequiredResult"/> is returned in response to a client-initiated request when
|
||||
/// the server needs the client to fulfill one or more server-initiated requests before it can produce
|
||||
/// a final result. Per SEP-2322 the wire format is valid for <see cref="RequestMethods.ToolsCall"/>,
|
||||
/// <see cref="RequestMethods.PromptsGet"/>, and <c>resources/read</c>; this SDK wires the MRTR
|
||||
/// interceptor into all three methods.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// At least one of <see cref="InputRequests"/> or <see cref="RequestState"/> must be present.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This type is part of the Multi Round-Trip Requests (MRTR) mechanism defined in SEP-2322.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
public sealed class InputRequiredResult : Result
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputRequiredResult"/> class.
|
||||
/// </summary>
|
||||
public InputRequiredResult()
|
||||
{
|
||||
ResultType = "input_required";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server-initiated requests that the client must fulfill before retrying the original request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The keys are server-assigned identifiers. The client must include a response for each key in the
|
||||
/// <see cref="RequestParams.InputResponses"/> map when retrying the original request.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[JsonPropertyName("inputRequests")]
|
||||
public IDictionary<string, InputRequest>? InputRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets opaque state to be echoed back by the client when retrying the original request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The client must treat this as an opaque blob and must not inspect, parse, modify, or make
|
||||
/// any assumptions about the contents. If present, the client must include this value in the
|
||||
/// <see cref="RequestParams.RequestState"/> property when retrying the original request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Servers may encode request state in any format (e.g., plain JSON, base64-encoded JSON,
|
||||
/// encrypted JWT, serialized binary). If the state contains sensitive data, servers should
|
||||
/// encrypt it to ensure confidentiality and integrity.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[JsonPropertyName("requestState")]
|
||||
public string? RequestState { get; set; }
|
||||
}
|
||||
128
src/ModelContextProtocol.Core/Protocol/InputResponse.cs
Normal file
128
src/ModelContextProtocol.Core/Protocol/InputResponse.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace ModelContextProtocol.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a client's response to a server-initiated <see cref="InputRequest"/> as part of an MRTR
|
||||
/// (Multi Round-Trip Request) flow.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// An <see cref="InputResponse"/> wraps the result of a server-to-client request such as
|
||||
/// <see cref="CreateMessageResult"/>, <see cref="ElicitResult"/>, or <see cref="ListRootsResult"/>.
|
||||
/// The type of the inner response corresponds to the <see cref="InputRequest.Method"/> of the
|
||||
/// associated input request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The input response does not carry its own type discriminator in JSON. The type is determined by
|
||||
/// the corresponding <see cref="InputRequest.Method"/> key in the <see cref="InputRequiredResult.InputRequests"/> map.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public sealed class InputResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the raw JSON element representing the response.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use <see cref="Deserialize{T}"/> with the <c>JsonTypeInfo<T></c> matching the
|
||||
/// associated <see cref="InputRequest.Method"/> - for elicitation, sampling, or roots see
|
||||
/// <see cref="ElicitResultJsonTypeInfo"/>, <see cref="CreateMessageResultJsonTypeInfo"/>, and
|
||||
/// <see cref="ListRootsResultJsonTypeInfo"/>.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public JsonElement RawValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the raw value to the specified result type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to deserialize to (e.g., <see cref="CreateMessageResult"/>, <see cref="ElicitResult"/>).</typeparam>
|
||||
/// <param name="typeInfo">The JSON type information for <typeparamref name="T"/>.</param>
|
||||
/// <returns>The deserialized result, or <see langword="null"/> if deserialization fails.</returns>
|
||||
public T? Deserialize<T>(System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> typeInfo) =>
|
||||
JsonSerializer.Deserialize(RawValue, typeInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonTypeInfo{T}"/> for <see cref="ElicitResult"/>, suitable for use with
|
||||
/// <see cref="Deserialize{T}"/> when the corresponding <see cref="InputRequest.Method"/> is
|
||||
/// <see cref="RequestMethods.ElicitationCreate"/>.
|
||||
/// </summary>
|
||||
public static JsonTypeInfo<ElicitResult> ElicitResultJsonTypeInfo => McpJsonUtilities.JsonContext.Default.ElicitResult;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonTypeInfo{T}"/> for <see cref="CreateMessageResult"/>, suitable for use with
|
||||
/// <see cref="Deserialize{T}"/> when the corresponding <see cref="InputRequest.Method"/> is
|
||||
/// <see cref="RequestMethods.SamplingCreateMessage"/>.
|
||||
/// </summary>
|
||||
public static JsonTypeInfo<CreateMessageResult> CreateMessageResultJsonTypeInfo => McpJsonUtilities.JsonContext.Default.CreateMessageResult;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonTypeInfo{T}"/> for <see cref="ListRootsResult"/>, suitable for use with
|
||||
/// <see cref="Deserialize{T}"/> when the corresponding <see cref="InputRequest.Method"/> is
|
||||
/// <see cref="RequestMethods.RootsList"/>.
|
||||
/// </summary>
|
||||
public static JsonTypeInfo<ListRootsResult> ListRootsResultJsonTypeInfo => McpJsonUtilities.JsonContext.Default.ListRootsResult;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputResponse"/> from a <see cref="CreateMessageResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The sampling result.</param>
|
||||
/// <returns>A new <see cref="InputResponse"/> instance.</returns>
|
||||
public static InputResponse FromSamplingResult(CreateMessageResult result)
|
||||
{
|
||||
Throw.IfNull(result);
|
||||
return new()
|
||||
{
|
||||
RawValue = JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CreateMessageResult),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputResponse"/> from an <see cref="ElicitResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The elicitation result.</param>
|
||||
/// <returns>A new <see cref="InputResponse"/> instance.</returns>
|
||||
public static InputResponse FromElicitResult(ElicitResult result)
|
||||
{
|
||||
Throw.IfNull(result);
|
||||
return new()
|
||||
{
|
||||
RawValue = JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.ElicitResult),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="InputResponse"/> from a <see cref="ListRootsResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The roots list result.</param>
|
||||
/// <returns>A new <see cref="InputResponse"/> instance.</returns>
|
||||
public static InputResponse FromRootsResult(ListRootsResult result)
|
||||
{
|
||||
Throw.IfNull(result);
|
||||
return new()
|
||||
{
|
||||
RawValue = JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.ListRootsResult),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Provides JSON serialization support for <see cref="InputResponse"/>.</summary>
|
||||
public sealed class Converter : JsonConverter<InputResponse>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override InputResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var element = JsonElement.ParseValue(ref reader);
|
||||
return new InputResponse { RawValue = element };
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, InputResponse value, JsonSerializerOptions options)
|
||||
{
|
||||
value.RawValue.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -74,4 +74,15 @@ public sealed class JsonRpcMessageContext
|
|||
/// </para>
|
||||
/// </remarks>
|
||||
public IDictionary<string, object?>? Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the protocol version from the transport-level header (e.g. <c>Mcp-Protocol-Version</c>)
|
||||
/// that accompanied this JSON-RPC message.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In stateless Streamable HTTP mode, the protocol version cannot be negotiated via the <c>initialize</c>
|
||||
/// handshake because each request creates a new server instance. This property allows the transport layer
|
||||
/// to flow the protocol version header so the server can determine client capabilities.
|
||||
/// </remarks>
|
||||
public string? ProtocolVersion { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
|
@ -25,6 +26,52 @@ public abstract class RequestParams
|
|||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the responses to server-initiated input requests from a previous <see cref="InputRequiredResult"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This property is populated when retrying a request after receiving an <see cref="InputRequiredResult"/>.
|
||||
/// Each key corresponds to a key from the <see cref="InputRequiredResult.InputRequests"/> map, and
|
||||
/// the value is the client's response to that input request.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
[JsonIgnore]
|
||||
public IDictionary<string, InputResponse>? InputResponses
|
||||
{
|
||||
get => InputResponsesCore;
|
||||
set => InputResponsesCore = value;
|
||||
}
|
||||
|
||||
// See ExperimentalInternalPropertyTests.cs before modifying this property.
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("inputResponses")]
|
||||
internal IDictionary<string, InputResponse>? InputResponsesCore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets opaque request state echoed back from a previous <see cref="InputRequiredResult"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This property is populated when retrying a request after receiving an <see cref="InputRequiredResult"/>
|
||||
/// that included a <see cref="InputRequiredResult.RequestState"/> value. The client must echo back the
|
||||
/// exact value without modification.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Mrtr_DiagnosticId, UrlFormat = Experimentals.Mrtr_Url)]
|
||||
[JsonIgnore]
|
||||
public string? RequestState
|
||||
{
|
||||
get => RequestStateCore;
|
||||
set => RequestStateCore = value;
|
||||
}
|
||||
|
||||
// See ExperimentalInternalPropertyTests.cs before modifying this property.
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("requestState")]
|
||||
internal string? RequestStateCore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opaque token that will be attached to any subsequent progress notifications.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -21,4 +21,18 @@ public abstract class Result
|
|||
/// </remarks>
|
||||
[JsonPropertyName("_meta")]
|
||||
public JsonObject? Meta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the result, which allows the client to determine how to parse the result object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When absent or 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <value>Defaults to <see langword="null"/>, which is equivalent to <c>"complete"</c>.</value>
|
||||
[JsonPropertyName("resultType")]
|
||||
public string? ResultType { get; set; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue