From 0e1bae4801ac493a7c13c72920308d13641eb576 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 27 Aug 2025 19:27:05 +0300 Subject: [PATCH] Support multiple contents in sampling results. --- .../Client/McpClient.Methods.cs | 2 +- .../Client/McpClientExtensions.cs | 2 +- .../Client/McpClientImpl.cs | 5 +- .../McpJsonUtilities.cs | 3 +- .../McpSession.Methods.cs | 36 ++++++++ .../Protocol/CreateMessageRequestParams.cs | 6 ++ .../Protocol/CreateMessageResult.cs | 45 +++++++-- .../Protocol/CreateMessageResultDto.cs | 87 ++++++++++++++++++ .../Protocol/IMcpModelSerializer.cs | 92 +++++++++++++++++++ .../RequestHandlers.cs | 59 ++++++++++-- .../Server/McpServer.Methods.cs | 4 +- tests/Common/Utils/TestServerTransport.cs | 2 +- .../HttpServerIntegrationTests.cs | 2 +- .../MapMcpTests.cs | 2 +- .../Client/McpClientCreationTests.cs | 8 +- .../ClientIntegrationTests.cs | 2 +- .../DockerEverythingServerTests.cs | 2 +- .../Server/McpServerExtensionsTests.cs | 4 +- .../Server/McpServerTests.cs | 2 +- 19 files changed, 329 insertions(+), 36 deletions(-) create mode 100644 src/ModelContextProtocol.Core/Protocol/CreateMessageResultDto.cs create mode 100644 src/ModelContextProtocol.Core/Protocol/IMcpModelSerializer.cs diff --git a/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs b/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs index 560ce31dc..9a8ea8387 100644 --- a/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs +++ b/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs @@ -623,7 +623,7 @@ internal static CreateMessageResult ToCreateMessageResult(ChatResponse chatRespo return new() { - Content = content ?? new TextContentBlock { Text = lastMessage?.Text ?? string.Empty }, + Contents = [content ?? new TextContentBlock { Text = lastMessage?.Text ?? string.Empty }], Model = chatResponse.ModelId ?? "unknown", Role = lastMessage?.Role == ChatRole.User ? Role.User : Role.Assistant, StopReason = chatResponse.FinishReason == ChatFinishReason.Length ? "maxTokens" : "endTurn", diff --git a/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs b/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs index e987f30f6..ad8828058 100644 --- a/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs +++ b/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs @@ -703,7 +703,7 @@ internal static CreateMessageResult ToCreateMessageResult(this ChatResponse chat return new() { - Content = content ?? new TextContentBlock { Text = lastMessage?.Text ?? string.Empty }, + Contents = [content ?? new TextContentBlock { Text = lastMessage?.Text ?? string.Empty }], Model = chatResponse.ModelId ?? "unknown", Role = lastMessage?.Role == ChatRole.User ? Role.User : Role.Assistant, StopReason = chatResponse.FinishReason == ChatFinishReason.Length ? "maxTokens" : "endTurn", diff --git a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs index 729185a3e..489bfbd3d 100644 --- a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs +++ b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs @@ -73,12 +73,13 @@ private void RegisterHandlers(ClientCapabilities capabilities, NotificationHandl requestHandlers.Set( RequestMethods.SamplingCreateMessage, + this, (request, _, cancellationToken) => samplingHandler( request, request?.ProgressToken is { } token ? new TokenProgress(this, token) : NullProgress.Instance, cancellationToken), - McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams, - McpJsonUtilities.JsonContext.Default.CreateMessageResult); + CreateMessageRequestParams.ModelSerializer, + CreateMessageResult.ModelSerializer); } if (capabilities.Roots is { } rootsCapability) diff --git a/src/ModelContextProtocol.Core/McpJsonUtilities.cs b/src/ModelContextProtocol.Core/McpJsonUtilities.cs index 8bc9e21b0..30f329cac 100644 --- a/src/ModelContextProtocol.Core/McpJsonUtilities.cs +++ b/src/ModelContextProtocol.Core/McpJsonUtilities.cs @@ -114,7 +114,8 @@ internal static bool IsValidMcpToolSchema(JsonElement element) [JsonSerializable(typeof(CompleteRequestParams))] [JsonSerializable(typeof(CompleteResult))] [JsonSerializable(typeof(CreateMessageRequestParams))] - [JsonSerializable(typeof(CreateMessageResult))] + [JsonSerializable(typeof(CreateMessageResultDto_V1))] + [JsonSerializable(typeof(CreateMessageResultDto_V2))] [JsonSerializable(typeof(ElicitRequestParams))] [JsonSerializable(typeof(ElicitResult))] [JsonSerializable(typeof(EmptyResult))] diff --git a/src/ModelContextProtocol.Core/McpSession.Methods.cs b/src/ModelContextProtocol.Core/McpSession.Methods.cs index c537732f1..8963f1663 100644 --- a/src/ModelContextProtocol.Core/McpSession.Methods.cs +++ b/src/ModelContextProtocol.Core/McpSession.Methods.cs @@ -72,6 +72,42 @@ internal async ValueTask SendRequestAsync( return JsonSerializer.Deserialize(response.Result, resultTypeInfo) ?? throw new JsonException("Unexpected JSON result in response."); } + /// + /// Sends a JSON-RPC request and attempts to deserialize the result to . + /// + /// The type of the request parameters to serialize from. + /// The type of the result to deserialize to. + /// The JSON-RPC method name to invoke. + /// Object representing the request parameters. + /// The request parameter serialization delegate. + /// The result deserialization delegate. + /// The request id for the request. + /// The to monitor for cancellation requests. The default is . + /// A task that represents the asynchronous operation. The task result contains the deserialized result. + internal async ValueTask SendRequestAsync( + string method, + TParameters parameters, + IMcpModelSerializer parametersSerializer, + IMcpModelSerializer resultSerializer, + RequestId requestId = default, + CancellationToken cancellationToken = default) + where TResult : notnull + { + Throw.IfNullOrWhiteSpace(method); + Throw.IfNull(parametersSerializer); + Throw.IfNull(resultSerializer); + + JsonRpcRequest jsonRpcRequest = new() + { + Id = requestId, + Method = method, + Params = parametersSerializer.Serialize(parameters, this), + }; + + JsonRpcResponse response = await SendRequestAsync(jsonRpcRequest, cancellationToken).ConfigureAwait(false); + return resultSerializer.Deserialize(response.Result, this) ?? throw new JsonException("Unexpected JSON result in response."); + } + /// /// Sends a parameterless notification to the connected session. /// diff --git a/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs index b71358df3..ff739afe7 100644 --- a/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs @@ -100,4 +100,10 @@ public sealed class CreateMessageRequestParams : RequestParams /// [JsonPropertyName("temperature")] public float? Temperature { get; init; } + + /// + /// A serializer that wraps the source generated JsonTypeInfo for the type. + /// + internal static IMcpModelSerializer ModelSerializer { get; } = + McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams.ToMcpModelSerializer(); } diff --git a/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs b/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs index ba599d6c5..d1960be2a 100644 --- a/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Serialization; - namespace ModelContextProtocol.Protocol; /// @@ -13,8 +11,24 @@ public sealed class CreateMessageResult : Result /// /// Gets or sets the content of the message. /// - [JsonPropertyName("content")] - public required ContentBlock Content { get; init; } + public required List Contents + { + get; + init + { + if (value is null or []) + { + throw new ArgumentException(nameof(Contents)); + } + + field = value; + } + } + + /// + /// Gets or sets the content of the message. + /// + public ContentBlock Content { get => Contents.First(); init => Contents = [value]; } /// /// Gets or sets the name of the model that generated the message. @@ -28,7 +42,6 @@ public sealed class CreateMessageResult : Result /// enabling appropriate handling based on the model's capabilities and characteristics. /// /// - [JsonPropertyName("model")] public required string Model { get; init; } /// @@ -42,12 +55,28 @@ public sealed class CreateMessageResult : Result /// stopSequenceA specific stop sequence was encountered during generation. /// /// - [JsonPropertyName("stopReason")] public string? StopReason { get; init; } /// /// Gets or sets the role of the user who generated the message. /// - [JsonPropertyName("role")] public required Role Role { get; init; } -} + + /// + /// A serializer that delegates to the appropriate versioned DTO serializer + /// + internal static IMcpModelSerializer ModelSerializer { get; } = + McpModelSerializer.CreateDelegatingSerializer(endpoint => + { + if (endpoint?.NegotiatedProtocolVersion is string version && + DateTime.Parse(version) < new DateTime(2025, 09, 18)) // A hypothetical future version + { + // The negotiated protocol version is before 2025-09-18, so we need to use the V1 serializer. + return CreateMessageResultDto_V1.ModelSerializer; + } + else + { + return CreateMessageResultDto_V2.ModelSerializer; + } + }); +} \ No newline at end of file diff --git a/src/ModelContextProtocol.Core/Protocol/CreateMessageResultDto.cs b/src/ModelContextProtocol.Core/Protocol/CreateMessageResultDto.cs new file mode 100644 index 000000000..e813cfbf8 --- /dev/null +++ b/src/ModelContextProtocol.Core/Protocol/CreateMessageResultDto.cs @@ -0,0 +1,87 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace ModelContextProtocol.Protocol; + +internal sealed class CreateMessageResultDto_V2 // V1, V2, V3, etc. as a placeholder naming convention. + // Could also be the protocol version introducing the breaking change +{ + [JsonPropertyName("content")] + public required List Content { get; init; } + + [JsonPropertyName("model")] + public required string Model { get; init; } + + [JsonPropertyName("stopReason")] + public string? StopReason { get; init; } + + [JsonPropertyName("role")] + public required Role Role { get; init; } + + [JsonPropertyName("_meta")] + public JsonObject? Meta { get; init; } + + /// + /// The serializer for using this DTO. + /// + public static IMcpModelSerializer ModelSerializer { get; } = + McpModelSerializer.CreateDtoSerializer( + toDto: static model => new () + { + Content = model.Contents, + Model = model.Model, + StopReason = model.StopReason, + Role = model.Role, + Meta = model.Meta + }, + fromDto: static dto => new() + { + Contents = dto.Content, + Model = dto.Model, + StopReason = dto.StopReason, + Role = dto.Role, + Meta = dto.Meta + }, + McpJsonUtilities.JsonContext.Default.CreateMessageResultDto_V2); +} + +internal sealed class CreateMessageResultDto_V1 // V1, V2, V3, etc. as a placeholder naming convention. +{ + [JsonPropertyName("content")] + public required ContentBlock Content { get; init; } + + [JsonPropertyName("model")] + public required string Model { get; init; } + + [JsonPropertyName("stopReason")] + public string? StopReason { get; init; } + + [JsonPropertyName("role")] + public required Role Role { get; init; } + + [JsonPropertyName("_meta")] + public JsonObject? Meta { get; init; } + + /// + /// The serializer for using this DTO. + /// + public static IMcpModelSerializer ModelSerializer { get; } = + McpModelSerializer.CreateDtoSerializer( + toDto: static model => new() + { + Content = model.Content, + Model = model.Model, + StopReason = model.StopReason, + Role = model.Role, + Meta = model.Meta + }, + fromDto: static dto => new() + { + Contents = [dto.Content], + Model = dto.Model, + StopReason = dto.StopReason, + Role = dto.Role, + Meta = dto.Meta + }, + McpJsonUtilities.JsonContext.Default.CreateMessageResultDto_V1); +} diff --git a/src/ModelContextProtocol.Core/Protocol/IMcpModelSerializer.cs b/src/ModelContextProtocol.Core/Protocol/IMcpModelSerializer.cs new file mode 100644 index 000000000..2a0526e7e --- /dev/null +++ b/src/ModelContextProtocol.Core/Protocol/IMcpModelSerializer.cs @@ -0,0 +1,92 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization.Metadata; + +namespace ModelContextProtocol.Protocol; + +/// +/// An abstraction for serializing and deserializing MCP model objects to/from JSON, +/// +/// The model type being serialized. +internal interface IMcpModelSerializer +{ + public JsonNode? Serialize(TModel? model, McpSession session); + public TModel? Deserialize(JsonNode? node, McpSession session); +} + +/// +/// Defines a set of factory methods for creating instances. +/// +internal static class McpModelSerializer +{ + /// + /// Creates an MCP model serializer that delegates to different serializers based on the MCP session. + /// + public static IMcpModelSerializer CreateDelegatingSerializer(Func> selector) => + new DelegatingMcpModelSerializer(selector); + + /// + /// Creates an MCP model serializer mapped from a . + /// + /// + /// + /// + public static IMcpModelSerializer ToMcpModelSerializer(this JsonTypeInfo typeInfo) => + new JsonTypeInfoMcpModelSerializer(typeInfo); + + /// + /// Creates an MCP model serializer that maps between a model type and a DTO type for serialization. + /// + /// The model type to serialize. + /// The DTO used to drive serialization. + /// The model-to-dto mapper. + /// The dto-to-model inverse mapper. + /// The governing serialization of the DTO type. + public static IMcpModelSerializer CreateDtoSerializer( + Func toDto, + Func fromDto, + JsonTypeInfo dtoTypeInfo) => + new DtoMappingMcpModelSerializer(toDto, fromDto, dtoTypeInfo); + + private sealed class JsonTypeInfoMcpModelSerializer(JsonTypeInfo typeInfo) : IMcpModelSerializer + { + public TModel? Deserialize(JsonNode? node, McpSession _) => JsonSerializer.Deserialize(node, typeInfo); + public JsonNode? Serialize(TModel? model, McpSession _) => model is null ? null : JsonSerializer.SerializeToNode(model, typeInfo); + } + + private sealed class DelegatingMcpModelSerializer(Func> selector) : IMcpModelSerializer + { + public TModel? Deserialize(JsonNode? node, McpSession session) + { + var serializer = selector(session); + return serializer.Deserialize(node, session); + } + + public JsonNode? Serialize(TModel? model, McpSession session) + { + var serializer = selector(session); + return serializer.Serialize(model, session); + } + } + + private sealed class DtoMappingMcpModelSerializer(Func toDto, Func fromDto, JsonTypeInfo dtoTypeInfo) : IMcpModelSerializer + { + public JsonNode? Serialize(TModel? model, McpSession _) + { + if (model is null) + { + return null; + } + + TDto dto = toDto(model); + return JsonSerializer.SerializeToNode(dto, dtoTypeInfo); + } + + public TModel? Deserialize(JsonNode? node, McpSession _) + { + TDto? dto = JsonSerializer.Deserialize(node, dtoTypeInfo); + return dto is null ? default : fromDto(dto); + } + } +} diff --git a/src/ModelContextProtocol.Core/RequestHandlers.cs b/src/ModelContextProtocol.Core/RequestHandlers.cs index fd95751d9..f0c2f808a 100644 --- a/src/ModelContextProtocol.Core/RequestHandlers.cs +++ b/src/ModelContextProtocol.Core/RequestHandlers.cs @@ -14,8 +14,8 @@ internal sealed class RequestHandlers : DictionaryType of response payload that will be serialized to JSON (not full RPC response) /// Method identifier to register for (e.g., "tools/list", "logging/setLevel") /// Handler function to be called when a request with the specified method identifier is received - /// The JSON contract governing request parameter deserialization - /// The JSON contract governing response serialization + /// The JSON contract governing request parameter deserialization + /// The JSON contract governing response serialization /// /// /// This method is used internally by the MCP infrastructure to register handlers for various protocol methods. @@ -30,19 +30,60 @@ internal sealed class RequestHandlers : Dictionary( string method, Func> handler, - JsonTypeInfo requestTypeInfo, - JsonTypeInfo responseTypeInfo) + JsonTypeInfo requestSerializer, + JsonTypeInfo responseDeserializer) { Throw.IfNull(method); Throw.IfNull(handler); - Throw.IfNull(requestTypeInfo); - Throw.IfNull(responseTypeInfo); + Throw.IfNull(requestSerializer); + Throw.IfNull(responseDeserializer); this[method] = async (request, cancellationToken) => { - TParams? typedRequest = JsonSerializer.Deserialize(request.Params, requestTypeInfo); - object? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false); - return JsonSerializer.SerializeToNode(result, responseTypeInfo); + TParams? typedRequest = JsonSerializer.Deserialize(request.Params, requestSerializer); + TResult? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false); + return JsonSerializer.SerializeToNode(result, responseDeserializer); + }; + } + + /// + /// Registers a handler for incoming requests of a specific method in the MCP protocol. + /// + /// Type of request payload that will be deserialized from incoming JSON + /// Type of response payload that will be serialized to JSON (not full RPC response) + /// Method identifier to register for (e.g., "tools/list", "logging/setLevel") + /// + /// Handler function to be called when a request with the specified method identifier is received + /// The JSON contract governing request parameter deserialization + /// The JSON contract governing response serialization + /// + /// + /// This method is used internally by the MCP infrastructure to register handlers for various protocol methods. + /// When an incoming request matches the specified method, the registered handler will be invoked with the + /// deserialized request parameters. + /// + /// + /// The handler function receives the deserialized request object, the full JSON-RPC request, and a cancellation token, + /// and should return a response object that will be serialized back to the client. + /// + /// + public void Set( + string method, + McpSession session, + Func> handler, + IMcpModelSerializer requestSerializer, + IMcpModelSerializer responseSerializer) + { + Throw.IfNull(method); + Throw.IfNull(handler); + Throw.IfNull(requestSerializer); + Throw.IfNull(responseSerializer); + + this[method] = async (request, cancellationToken) => + { + TParams? typedRequest = requestSerializer.Deserialize(request.Params, session); + TResult? result = await handler(typedRequest, request, cancellationToken).ConfigureAwait(false); + return responseSerializer.Serialize(result, session); }; } } diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs index 00fc0a7cc..f72bceca1 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs @@ -62,8 +62,8 @@ public ValueTask SampleAsync( return SendRequestAsync( RequestMethods.SamplingCreateMessage, request, - McpJsonUtilities.JsonContext.Default.CreateMessageRequestParams, - McpJsonUtilities.JsonContext.Default.CreateMessageResult, + CreateMessageRequestParams.ModelSerializer, + CreateMessageResult.ModelSerializer, cancellationToken: cancellationToken); } diff --git a/tests/Common/Utils/TestServerTransport.cs b/tests/Common/Utils/TestServerTransport.cs index f875fe504..c0c2ce08c 100644 --- a/tests/Common/Utils/TestServerTransport.cs +++ b/tests/Common/Utils/TestServerTransport.cs @@ -74,7 +74,7 @@ private async Task SamplingAsync(JsonRpcRequest request, CancellationToken cance await WriteMessageAsync(new JsonRpcResponse { Id = request.Id, - Result = JsonSerializer.SerializeToNode(new CreateMessageResult { Content = new TextContentBlock { Text = "" }, Model = "model", Role = Role.User }, McpJsonUtilities.DefaultOptions), + Result = JsonSerializer.SerializeToNode(new CreateMessageResult { Contents = [new TextContentBlock { Text = "" }], Model = "model", Role = Role.User }, McpJsonUtilities.DefaultOptions), }, cancellationToken); } diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs index 5da37146a..c7dac5d21 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs @@ -260,7 +260,7 @@ public async Task Sampling_Sse_TestServer() { Model = "test-model", Role = Role.Assistant, - Content = new TextContentBlock { Text = "Test response" }, + Contents = [new TextContentBlock { Text = "Test response" }], }; }; await using var client = await GetClientAsync(options); diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs index bb9746ed7..248a69cb1 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs @@ -179,7 +179,7 @@ public async Task Sampling_DoesNotCloseStream_Prematurely() { Model = "test-model", Role = Role.Assistant, - Content = new TextContentBlock { Text = "Sampling response from client" }, + Contents = [new TextContentBlock { Text = "Sampling response from client" }], }; }, }, diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientCreationTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientCreationTests.cs index 15127502e..b62206d8c 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientCreationTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientCreationTests.cs @@ -68,10 +68,10 @@ public async Task CreateAsync_WithCapabilitiesOptions(Type transportType) SamplingHandler = async (c, p, t) => new CreateMessageResult { - Content = new TextContentBlock { Text = "result" }, - Model = "test-model", - Role = Role.User, - StopReason = "endTurn" + Contents = [new TextContentBlock { Text = "result" }], + Model = "test-model", + Role = Role.User, + StopReason = "endTurn" }, }, Roots = new RootsCapability diff --git a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs index e2f03805f..da7e78aef 100644 --- a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs @@ -385,7 +385,7 @@ public async Task Sampling_Stdio(string clientId) { Model = "test-model", Role = Role.Assistant, - Content = new TextContentBlock { Text = "Test response" }, + Contents = [new TextContentBlock { Text = "Test response" }], }; }, }, diff --git a/tests/ModelContextProtocol.Tests/DockerEverythingServerTests.cs b/tests/ModelContextProtocol.Tests/DockerEverythingServerTests.cs index 842371f88..a7b727565 100644 --- a/tests/ModelContextProtocol.Tests/DockerEverythingServerTests.cs +++ b/tests/ModelContextProtocol.Tests/DockerEverythingServerTests.cs @@ -83,7 +83,7 @@ public async Task Sampling_Sse_EverythingServer() { Model = "test-model", Role = Role.Assistant, - Content = new TextContentBlock { Text = "Test response" }, + Contents = [new TextContentBlock { Text = "Test response" }], }; }, }, diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs index 5569f993c..2db9e5cfb 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs @@ -76,7 +76,7 @@ public async Task SampleAsync_Request_Forwards_To_McpServer_SendRequestAsync() var resultPayload = new CreateMessageResult { - Content = new TextContentBlock { Text = "resp" }, + Contents = [new TextContentBlock { Text = "resp" }], Model = "test-model", Role = Role.Assistant, StopReason = "endTurn", @@ -113,7 +113,7 @@ public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync() var resultPayload = new CreateMessageResult { - Content = new TextContentBlock { Text = "resp" }, + Contents = [new TextContentBlock { Text = "resp" }], Model = "test-model", Role = Role.Assistant, StopReason = "endTurn", diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs index 736d63ec4..8847f88ad 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs @@ -668,7 +668,7 @@ public override Task SendRequestAsync(JsonRpcRequest request, C CreateMessageResult result = new() { - Content = new TextContentBlock { Text = "The Eiffel Tower." }, + Contents = [new TextContentBlock { Text = "The Eiffel Tower." }], Model = "amazingmodel", Role = Role.Assistant, StopReason = "endTurn",