Allow JsonSerializerOptions parameters for user-defined and inputs and hardcode everything else to the source generator. (#182)

* Allow user-defined JsonSerializerOptions for user-defined and inputs and hardcode everything else to the source generator.

* Add validation for user-defined JsonSerializerOptions.

* Remove out of date comment.

* Address feedback and add tests
This commit is contained in:
Eirik Tsarpalis 2025-04-02 18:43:54 +01:00 • committed by GitHub
commit f1af251c32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,6 +5,8 @@ using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace ModelContextProtocol.Tests.Server;
@ -171,7 +173,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<PingResult>(response);
JsonObject jObj = Assert.IsType<JsonObject>(response);
Assert.Empty(jObj);
});
}
@ -184,9 +187,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<InitializeResult>(response);
var result = (InitializeResult)response;
var result = JsonSerializer.Deserialize<InitializeResult>(response);
Assert.NotNull(result);
Assert.Equal("TestServer", result.ServerInfo.Name);
Assert.Equal("1.0", result.ServerInfo.Version);
Assert.Equal("2024", result.ProtocolVersion);
@ -202,10 +204,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<CompleteResult>(response);
var result = (CompleteResult)response;
Assert.NotNull(result.Completion);
var result = JsonSerializer.Deserialize<CompleteResult>(response);
Assert.NotNull(result?.Completion);
Assert.Empty(result.Completion.Values);
Assert.Equal(0, result.Completion.Total);
Assert.False(result.Completion.HasMore);
@ -233,10 +233,8 @@ public class McpServerTests : LoggedTest
},
assertResult: response =>
{
Assert.IsType<CompleteResult>(response);
var result = (CompleteResult)response;
Assert.NotNull(result.Completion);
CompleteResult? result = JsonSerializer.Deserialize<CompleteResult>(response);
Assert.NotNull(result?.Completion);
Assert.NotEmpty(result.Completion.Values);
Assert.Equal("test", result.Completion.Values[0]);
Assert.Equal(2, result.Completion.Total);
@ -273,10 +271,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<ListResourceTemplatesResult>(response);
var result = (ListResourceTemplatesResult)response;
Assert.NotNull(result.ResourceTemplates);
var result = JsonSerializer.Deserialize<ListResourceTemplatesResult>(response);
Assert.NotNull(result?.ResourceTemplates);
Assert.NotEmpty(result.ResourceTemplates);
Assert.Equal("test", result.ResourceTemplates[0].UriTemplate);
});
@ -304,10 +300,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<ListResourcesResult>(response);
var result = (ListResourcesResult)response;
Assert.NotNull(result.Resources);
var result = JsonSerializer.Deserialize<ListResourcesResult>(response);
Assert.NotNull(result?.Resources);
Assert.NotEmpty(result.Resources);
Assert.Equal("test", result.Resources[0].Uri);
});
@ -331,7 +325,7 @@ public class McpServerTests : LoggedTest
{
return Task.FromResult(new ReadResourceResult
{
Contents = [new TextResourceContents() { Text = "test" }]
Contents = [new TextResourceContents { Text = "test" }]
});
},
ListResourcesHandler = (request, ct) => throw new NotImplementedException(),
@ -341,10 +335,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<ReadResourceResult>(response);
var result = (ReadResourceResult)response;
Assert.NotNull(result.Contents);
var result = JsonSerializer.Deserialize<ReadResourceResult>(response);
Assert.NotNull(result?.Contents);
Assert.NotEmpty(result.Contents);
TextResourceContents textResource = Assert.IsType<TextResourceContents>(result.Contents[0]);
@ -380,10 +372,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<ListPromptsResult>(response);
var result = (ListPromptsResult)response;
Assert.NotNull(result.Prompts);
var result = JsonSerializer.Deserialize<ListPromptsResult>(response);
Assert.NotNull(result?.Prompts);
Assert.NotEmpty(result.Prompts);
Assert.Equal("test", result.Prompts[0].Name);
});
@ -411,9 +401,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<GetPromptResult>(response);
var result = (GetPromptResult)response;
var result = JsonSerializer.Deserialize<GetPromptResult>(response);
Assert.NotNull(result);
Assert.Equal("test", result.Description);
});
}
@ -446,9 +435,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<ListToolsResult>(response);
var result = (ListToolsResult)response;
var result = JsonSerializer.Deserialize<ListToolsResult>(response);
Assert.NotNull(result);
Assert.NotEmpty(result.Tools);
Assert.Equal("test", result.Tools[0].Name);
});
@ -482,9 +470,8 @@ public class McpServerTests : LoggedTest
configureOptions: null,
assertResult: response =>
{
Assert.IsType<CallToolResponse>(response);
var result = (CallToolResponse)response;
var result = JsonSerializer.Deserialize<CallToolResponse>(response);
Assert.NotNull(result);
Assert.NotEmpty(result.Content);
Assert.Equal("test", result.Content[0].Text);
});
@ -496,7 +483,7 @@ public class McpServerTests : LoggedTest
await Throws_Exception_If_No_Handler_Assigned(new ServerCapabilities { Tools = new() }, RequestMethods.ToolsCall, "CallTool handler not configured");
}
private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, string method, Action<McpServerOptions>? configureOptions, Action<object> assertResult)
private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, string method, Action<McpServerOptions>? configureOptions, Action<JsonNode?> assertResult)
{
await using var transport = new TestServerTransport();
var options = CreateOptions(serverCapabilities);
@ -524,7 +511,6 @@ public class McpServerTests : LoggedTest
var response = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(5));
Assert.NotNull(response);
Assert.NotNull(response.Result);
assertResult(response.Result);
@ -610,10 +596,11 @@ public class McpServerTests : LoggedTest
supportsSampling ? new ClientCapabilities { Sampling = new SamplingCapability() } :
null;
public Task<T> SendRequestAsync<T>(JsonRpcRequest request, CancellationToken cancellationToken) where T : class
public Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken)
{
CreateMessageRequestParams rp = Assert.IsType<CreateMessageRequestParams>(request.Params);
CreateMessageRequestParams? rp = JsonSerializer.Deserialize<CreateMessageRequestParams>(request.Params);
Assert.NotNull(rp);
Assert.Equal(0.75f, rp.Temperature);
Assert.Equal(42, rp.MaxTokens);
Assert.Equal(["."], rp.StopSequences);
@ -634,7 +621,12 @@ public class McpServerTests : LoggedTest
Role = "assistant",
StopReason = "endTurn",
};
return Task.FromResult((T)(object)result);
return Task.FromResult(new JsonRpcResponse
{
Id = new RequestId("0"),
Result = JsonSerializer.SerializeToNode(result),
});
}
public ValueTask DisposeAsync() => default;
@ -670,7 +662,7 @@ public class McpServerTests : LoggedTest
await transport.SendMessageAsync(new JsonRpcNotification
{
Method = NotificationMethods.ProgressNotification,
Params = new ProgressNotification()
Params = JsonSerializer.SerializeToNode(new ProgressNotification
{
ProgressToken = new("abc"),
Progress = new()
@ -679,11 +671,12 @@ public class McpServerTests : LoggedTest
Total = 100,
Message = "Progress message",
},
},
}),
}, TestContext.Current.CancellationToken);
var notification = await notificationReceived.Task;
var progress = (ProgressNotification)notification.Params!;
var progress = JsonSerializer.Deserialize<ProgressNotification>(notification.Params);
Assert.NotNull(progress);
Assert.Equal("abc", progress.ProgressToken.ToString());
Assert.Equal(50, progress.Progress.Progress);
Assert.Equal(100, progress.Progress.Total);