Skip to content

Commit bb8d2a5

Browse files
Fix service resolution to use GetService instead of GetRequiredService
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
1 parent 2f84c3e commit bb8d2a5

File tree

2 files changed

+55
-76
lines changed

2 files changed

+55
-76
lines changed

src/ModelContextProtocol/McpServerBuilderExtensions.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public static partial class McpServerBuilderExtensions
4545
if (toolMethod.GetCustomAttribute<McpServerToolAttribute>() is not null)
4646
{
4747
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(toolMethod.IsStatic ?
48-
services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
49-
services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
48+
services => McpServerTool.Create(toolMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
49+
services => McpServerTool.Create(toolMethod, static r => CreateTarget(r.Services, typeof(TToolType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
5050
}
5151
}
5252

@@ -93,7 +93,7 @@ public static partial class McpServerBuilderExtensions
9393
builder.Services.AddSingleton(services => McpServerTool.Create(
9494
toolMethod,
9595
toolMethod.IsStatic ? null : target,
96-
new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }));
96+
new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
9797
}
9898
}
9999

@@ -149,8 +149,8 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnume
149149
if (toolMethod.GetCustomAttribute<McpServerToolAttribute>() is not null)
150150
{
151151
builder.Services.AddSingleton((Func<IServiceProvider, McpServerTool>)(toolMethod.IsStatic ?
152-
services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
153-
services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
152+
services => McpServerTool.Create(toolMethod, options: new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
153+
services => McpServerTool.Create(toolMethod, r => CreateTarget(r.Services, toolType), new() { Services = services , SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
154154
}
155155
}
156156
}
@@ -232,8 +232,8 @@ where t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null
232232
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() is not null)
233233
{
234234
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(promptMethod.IsStatic ?
235-
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
236-
services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
235+
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
236+
services => McpServerPrompt.Create(promptMethod, static r => CreateTarget(r.Services, typeof(TPromptType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
237237
}
238238
}
239239

@@ -277,7 +277,7 @@ where t.GetCustomAttribute<McpServerToolTypeAttribute>() is not null
277277
{
278278
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() is not null)
279279
{
280-
builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }));
280+
builder.Services.AddSingleton(services => McpServerPrompt.Create(promptMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
281281
}
282282
}
283283

@@ -333,8 +333,8 @@ public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnu
333333
if (promptMethod.GetCustomAttribute<McpServerPromptAttribute>() is not null)
334334
{
335335
builder.Services.AddSingleton((Func<IServiceProvider, McpServerPrompt>)(promptMethod.IsStatic ?
336-
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
337-
services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
336+
services => McpServerPrompt.Create(promptMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
337+
services => McpServerPrompt.Create(promptMethod, r => CreateTarget(r.Services, promptType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
338338
}
339339
}
340340
}
@@ -416,8 +416,8 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
416416
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() is not null)
417417
{
418418
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(resourceTemplateMethod.IsStatic ?
419-
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
420-
services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
419+
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
420+
services => McpServerResource.Create(resourceTemplateMethod, static r => CreateTarget(r.Services, typeof(TResourceType)), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
421421
}
422422
}
423423

@@ -461,7 +461,7 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
461461
{
462462
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() is not null)
463463
{
464-
builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }));
464+
builder.Services.AddSingleton(services => McpServerResource.Create(resourceTemplateMethod, target, new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }));
465465
}
466466
}
467467

@@ -517,8 +517,8 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
517517
if (resourceTemplateMethod.GetCustomAttribute<McpServerResourceAttribute>() is not null)
518518
{
519519
builder.Services.AddSingleton((Func<IServiceProvider, McpServerResource>)(resourceTemplateMethod.IsStatic ?
520-
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions }) :
521-
services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetRequiredService<IOptions<McpServerOptions>>().Value.JsonSerializerOptions })));
520+
services => McpServerResource.Create(resourceTemplateMethod, options: new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions }) :
521+
services => McpServerResource.Create(resourceTemplateMethod, r => CreateTarget(r.Services, resourceTemplateType), new() { Services = services, SerializerOptions = serializerOptions ?? services.GetService<IOptions<McpServerOptions>>()?.Value.JsonSerializerOptions })));
522522
}
523523
}
524524
}
Lines changed: 40 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,47 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using Microsoft.Extensions.Options;
3-
using ModelContextProtocol.Client;
43
using ModelContextProtocol.Protocol;
54
using ModelContextProtocol.Server;
6-
using ModelContextProtocol.Tests.Utils;
7-
using System.ComponentModel;
85
using System.Text.Json;
96
using System.Text.Json.Serialization;
107

118
namespace ModelContextProtocol.Tests.Configuration;
129

13-
public class McpServerJsonSerializerOptionsTests : ClientServerTestBase
10+
public class McpServerJsonSerializerOptionsTests
1411
{
15-
public McpServerJsonSerializerOptionsTests(ITestOutputHelper testOutputHelper)
16-
: base(testOutputHelper)
12+
[Fact]
13+
public void McpServerOptions_JsonSerializerOptions_DefaultsToNull()
1714
{
15+
// Arrange & Act
16+
var options = new McpServerOptions();
17+
18+
// Assert
19+
Assert.Null(options.JsonSerializerOptions);
1820
}
1921

20-
private class SpecialNumbers
22+
[Fact]
23+
public void McpServerOptions_JsonSerializerOptions_CanBeSet()
2124
{
22-
public double PositiveInfinity { get; set; }
23-
public double NegativeInfinity { get; set; }
24-
public double NotANumber { get; set; }
25+
// Arrange
26+
var customOptions = new JsonSerializerOptions
27+
{
28+
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
29+
};
30+
var options = new McpServerOptions();
31+
32+
// Act
33+
options.JsonSerializerOptions = customOptions;
34+
35+
// Assert
36+
Assert.NotNull(options.JsonSerializerOptions);
37+
Assert.Equal(JsonNumberHandling.AllowNamedFloatingPointLiterals, options.JsonSerializerOptions.NumberHandling);
2538
}
2639

27-
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
40+
[Fact]
41+
public void WithTools_UsesServerWideOptions_WhenNoExplicitOptionsProvided()
2842
{
29-
// Configure server-wide JsonSerializerOptions to allow named floating point literals
43+
// Arrange
44+
var services = new ServiceCollection();
3045
var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions)
3146
{
3247
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
@@ -37,58 +52,22 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
3752
options.JsonSerializerOptions = customOptions;
3853
});
3954

40-
// Register a tool that will use the server-wide JsonSerializerOptions
41-
// The null serializerOptions parameter should cause it to use McpServerOptions.JsonSerializerOptions
42-
services.AddSingleton(sp =>
43-
{
44-
var serverOptions = sp.GetRequiredService<IOptions<McpServerOptions>>().Value;
45-
return McpServerTool.Create(
46-
() => new SpecialNumbers
47-
{
48-
PositiveInfinity = double.PositiveInfinity,
49-
NegativeInfinity = double.NegativeInfinity,
50-
NotANumber = double.NaN
51-
},
52-
new McpServerToolCreateOptions
53-
{
54-
Name = "GetSpecialNumbers",
55-
Description = "Returns special floating point values",
56-
UseStructuredContent = true,
57-
SerializerOptions = serverOptions.JsonSerializerOptions,
58-
Services = sp
59-
});
60-
});
61-
}
55+
var builder = services.AddMcpServer();
6256

63-
[Fact]
64-
public async Task ServerWide_JsonSerializerOptions_Applied_To_Tools()
65-
{
66-
// Arrange
67-
McpClient client = await CreateMcpClientForServer();
57+
// Act - WithTools should pick up the server-wide options
58+
builder.WithTools<TestTools>();
59+
var serviceProvider = services.BuildServiceProvider();
6860

69-
// Act
70-
IList<McpClientTool> tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
71-
CallToolResult result = await client.CallToolAsync("GetSpecialNumbers", cancellationToken: TestContext.Current.CancellationToken);
72-
73-
// Assert
74-
Assert.NotNull(tools);
61+
// Assert - Verify the tool was registered
62+
var tools = serviceProvider.GetServices<McpServerTool>().ToList();
7563
Assert.Single(tools);
76-
Assert.Equal("GetSpecialNumbers", tools[0].Name);
77-
78-
// Verify the result contains structured content with special numbers
79-
Assert.NotNull(result);
80-
Assert.NotNull(result.StructuredContent);
81-
82-
var structuredContent = JsonSerializer.Deserialize<SpecialNumbers>(
83-
result.StructuredContent.ToString(),
84-
new JsonSerializerOptions(McpJsonUtilities.DefaultOptions)
85-
{
86-
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
87-
});
64+
Assert.Equal("TestTool", tools[0].ProtocolTool.Name);
65+
}
8866

89-
Assert.NotNull(structuredContent);
90-
Assert.True(double.IsPositiveInfinity(structuredContent.PositiveInfinity));
91-
Assert.True(double.IsNegativeInfinity(structuredContent.NegativeInfinity));
92-
Assert.True(double.IsNaN(structuredContent.NotANumber));
67+
[McpServerToolType]
68+
private class TestTools
69+
{
70+
[McpServerTool]
71+
public static string TestTool() => "test";
9372
}
9473
}

0 commit comments

Comments
 (0)