Skip to content

Commit 28d6547

Browse files
Update Microsoft.Extensions.AI version & expose JSON configuration on McpServerResourceCreateOptions (#511)
* Update Microsoft.Extensions.AI version * Update Microsoft.Extensions.AI.OpenAI * Fix failing tests; add JSON configuration options to McpServerResourceCreateOptions. * Replace temporary code with `AIFunction.ReturnJsonSchema` --------- Co-authored-by: Eirik Tsarpalis <[email protected]>
1 parent 1e8fb04 commit 28d6547

File tree

9 files changed

+45
-35
lines changed

9 files changed

+45
-35
lines changed

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
<System9Version>9.0.5</System9Version>
55
<System10Version>10.0.0-preview.4.25258.110</System10Version>
6-
<MicrosoftExtensionsAIVersion>9.5.0</MicrosoftExtensionsAIVersion>
6+
<MicrosoftExtensionsAIVersion>9.6.0</MicrosoftExtensionsAIVersion>
77
</PropertyGroup>
88

99
<!-- Product dependencies netstandard -->
@@ -50,7 +50,7 @@
5050
<PrivateAssets>all</PrivateAssets>
5151
</PackageVersion>
5252
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
53-
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.5.0-preview.1.25265.7" />
53+
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.6.0-preview.1.25310.2" />
5454
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(System9Version)" />
5555
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(System9Version)" />
5656
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(System9Version)" />

src/ModelContextProtocol.Core/McpJsonUtilities.cs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -85,30 +85,6 @@ internal static bool IsValidMcpToolSchema(JsonElement element)
8585
return false; // No type keyword found.
8686
}
8787

88-
internal static JsonElement? GetReturnSchema(this AIFunction function, AIJsonSchemaCreateOptions? schemaCreateOptions)
89-
{
90-
// TODO replace with https://github.com/dotnet/extensions/pull/6447 once merged.
91-
if (function.UnderlyingMethod?.ReturnType is not Type returnType)
92-
{
93-
return null;
94-
}
95-
96-
if (returnType == typeof(void) || returnType == typeof(Task) || returnType == typeof(ValueTask))
97-
{
98-
// Do not report an output schema for void or Task methods.
99-
return null;
100-
}
101-
102-
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() is Type genericTypeDef &&
103-
(genericTypeDef == typeof(Task<>) || genericTypeDef == typeof(ValueTask<>)))
104-
{
105-
// Extract the real type from Task<T> or ValueTask<T> if applicable.
106-
returnType = returnType.GetGenericArguments()[0];
107-
}
108-
109-
return AIJsonUtilities.CreateJsonSchema(returnType, serializerOptions: function.JsonSerializerOptions, inferenceOptions: schemaCreateOptions);
110-
}
111-
11288
// Keep in sync with CreateDefaultOptions above.
11389
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
11490
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
@@ -157,6 +133,7 @@ internal static bool IsValidMcpToolSchema(JsonElement element)
157133
[JsonSerializable(typeof(SubscribeRequestParams))]
158134
[JsonSerializable(typeof(UnsubscribeRequestParams))]
159135
[JsonSerializable(typeof(IReadOnlyDictionary<string, object>))]
136+
[JsonSerializable(typeof(PromptMessage[]))]
160137

161138
// Primitive types for use in consuming AIFunctions
162139
[JsonSerializable(typeof(string))]

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerPrompt.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
7171
Description = options?.Description,
7272
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
7373
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
74+
JsonSchemaCreateOptions = options?.SchemaCreateOptions,
7475
ConfigureParameterBinding = pi =>
7576
{
7677
if (pi.ParameterType == typeof(RequestContext<GetPromptRequestParams>))
@@ -151,7 +152,6 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
151152
return null;
152153
}
153154
},
154-
JsonSchemaCreateOptions = options?.SchemaCreateOptions,
155155
};
156156

157157
/// <summary>Creates an <see cref="McpServerPrompt"/> that wraps the specified <see cref="AIFunction"/>.</summary>

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
7777
Name = options?.Name ?? method.GetCustomAttribute<McpServerResourceAttribute>()?.Name,
7878
Description = options?.Description,
7979
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
80-
SerializerOptions = McpJsonUtilities.DefaultOptions,
80+
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
81+
JsonSchemaCreateOptions = options?.SchemaCreateOptions,
8182
ConfigureParameterBinding = pi =>
8283
{
8384
if (pi.ParameterType == typeof(RequestContext<ReadResourceRequestParams>))

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
8686
Description = options?.Description,
8787
MarshalResult = static (result, _, cancellationToken) => new ValueTask<object?>(result),
8888
SerializerOptions = options?.SerializerOptions ?? McpJsonUtilities.DefaultOptions,
89+
JsonSchemaCreateOptions = options?.SchemaCreateOptions,
8990
ConfigureParameterBinding = pi =>
9091
{
9192
if (pi.ParameterType == typeof(RequestContext<CallToolRequestParams>))
@@ -166,7 +167,6 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
166167
return null;
167168
}
168169
},
169-
JsonSchemaCreateOptions = options?.SchemaCreateOptions,
170170
};
171171

172172
/// <summary>Creates an <see cref="McpServerTool"/> that wraps the specified <see cref="AIFunction"/>.</summary>
@@ -366,7 +366,7 @@ public override async ValueTask<CallToolResponse> InvokeAsync(
366366
return null;
367367
}
368368

369-
if (function.GetReturnSchema(toolCreateOptions?.SchemaCreateOptions) is not JsonElement outputSchema)
369+
if (function.ReturnJsonSchema is not JsonElement outputSchema)
370370
{
371371
return null;
372372
}

src/ModelContextProtocol.Core/Server/McpServerResourceCreateOptions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using Microsoft.Extensions.AI;
12
using System.ComponentModel;
3+
using System.Text.Json;
24

35
namespace ModelContextProtocol.Server;
46

@@ -60,6 +62,22 @@ public sealed class McpServerResourceCreateOptions
6062
/// </summary>
6163
public string? MimeType { get; set; }
6264

65+
/// <summary>
66+
/// Gets or sets the JSON serializer options to use when marshalling data to/from JSON.
67+
/// </summary>
68+
/// <remarks>
69+
/// Defaults to <see cref="McpJsonUtilities.DefaultOptions"/> if left unspecified.
70+
/// </remarks>
71+
public JsonSerializerOptions? SerializerOptions { get; set; }
72+
73+
/// <summary>
74+
/// Gets or sets the JSON schema options when creating <see cref="AIFunction"/> from a method.
75+
/// </summary>
76+
/// <remarks>
77+
/// Defaults to <see cref="AIJsonSchemaCreateOptions.Default"/> if left unspecified.
78+
/// </remarks>
79+
public AIJsonSchemaCreateOptions? SchemaCreateOptions { get; set; }
80+
6381
/// <summary>
6482
/// Creates a shallow clone of the current <see cref="McpServerResourceCreateOptions"/> instance.
6583
/// </summary>
@@ -71,5 +89,7 @@ internal McpServerResourceCreateOptions Clone() =>
7189
Name = Name,
7290
Description = Description,
7391
MimeType = MimeType,
92+
SerializerOptions = SerializerOptions,
93+
SchemaCreateOptions = SchemaCreateOptions,
7494
};
7595
}

tests/ModelContextProtocol.Tests/Server/McpServerPromptTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using Moq;
66
using System.ComponentModel;
77
using System.Reflection;
8+
using System.Text.Json;
9+
using System.Text.Json.Nodes;
810

911
namespace ModelContextProtocol.Tests.Server;
1012

@@ -324,6 +326,11 @@ public async Task SupportsSchemaCreateOptions()
324326
{
325327
TransformSchemaNode = (context, node) =>
326328
{
329+
if (node.GetValueKind() is not JsonValueKind.Object)
330+
{
331+
node = new JsonObject();
332+
}
333+
327334
node["description"] = "1234";
328335
return node;
329336
}

tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ public async Task CanReturnResourceContents()
451451
{
452452
Assert.Same(mockServer.Object, server);
453453
return new TextResourceContents() { Text = "hello" };
454-
}, new() { Name = "Test" });
454+
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
455455
var result = await resource.ReadAsync(
456456
new RequestContext<ReadResourceRequestParams>(mockServer.Object) { Params = new() { Uri = "resource://Test" } },
457457
TestContext.Current.CancellationToken);
@@ -507,7 +507,7 @@ public async Task CanReturnCollectionOfStrings()
507507
{
508508
Assert.Same(mockServer.Object, server);
509509
return new List<string>() { "42", "43" };
510-
}, new() { Name = "Test" });
510+
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
511511
var result = await resource.ReadAsync(
512512
new RequestContext<ReadResourceRequestParams>(mockServer.Object) { Params = new() { Uri = "resource://Test" } },
513513
TestContext.Current.CancellationToken);
@@ -547,7 +547,7 @@ public async Task CanReturnCollectionOfAIContent()
547547
new TextContent("hello!"),
548548
new DataContent(new byte[] { 4, 5, 6 }, "application/json"),
549549
};
550-
}, new() { Name = "Test" });
550+
}, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options });
551551
var result = await resource.ReadAsync(
552552
new RequestContext<ReadResourceRequestParams>(mockServer.Object) { Params = new() { Uri = "resource://Test" } },
553553
TestContext.Current.CancellationToken);
@@ -575,5 +575,8 @@ private class DisposableResourceType : IDisposable
575575

576576
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
577577
[JsonSerializable(typeof(DisposableResourceType))]
578+
[JsonSerializable(typeof(List<AIContent>))]
579+
[JsonSerializable(typeof(List<string>))]
580+
[JsonSerializable(typeof(TextResourceContents))]
578581
partial class JsonContext6 : JsonSerializerContext;
579582
}

tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public async Task CanReturnCollectionOfAIContent()
190190
new DataContent("data:image/png;base64,1234"),
191191
new DataContent("data:audio/wav;base64,1234")
192192
};
193-
});
193+
}, new() { SerializerOptions = JsonContext2.Default.Options });
194194

195195
var result = await tool.InvokeAsync(
196196
new RequestContext<CallToolRequestParams>(mockServer.Object),
@@ -288,7 +288,7 @@ public async Task CanReturnCollectionOfStrings()
288288
{
289289
Assert.Same(mockServer.Object, server);
290290
return new List<string>() { "42", "43" };
291-
});
291+
}, new() { SerializerOptions = JsonContext2.Default.Options });
292292
var result = await tool.InvokeAsync(
293293
new RequestContext<CallToolRequestParams>(mockServer.Object),
294294
TestContext.Current.CancellationToken);
@@ -632,5 +632,7 @@ record Person(string Name, int Age);
632632
[JsonSerializable(typeof(AsyncDisposableToolType))]
633633
[JsonSerializable(typeof(AsyncDisposableAndDisposableToolType))]
634634
[JsonSerializable(typeof(JsonSchema))]
635+
[JsonSerializable(typeof(List<AIContent>))]
636+
[JsonSerializable(typeof(List<string>))]
635637
partial class JsonContext2 : JsonSerializerContext;
636638
}

0 commit comments

Comments
 (0)