Skip to content

Commit bd598ea

Browse files
Add optional JsonSerializerOptions parameter to ToContentBlock and ToJsonObject
- Added optional JsonSerializerOptions parameter to ToContentBlock method - Added optional JsonSerializerOptions parameter to ToJsonObject helper method - Updated all serialization calls within ToContentBlock to use provided options - Updated XML documentation references in McpServerTool and McpServerToolAttribute - Added test to verify custom JsonSerializerOptions are properly used - Enables user-defined serialization options to be passed through for anonymous types and custom converters Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
1 parent 867d044 commit bd598ea

4 files changed

Lines changed: 51 additions & 13 deletions

File tree

src/ModelContextProtocol.Core/AIContentExtensions.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,11 @@ public static class AIContentExtensions
138138
}
139139

140140
/// <summary>Converts the specified dictionary to a <see cref="JsonObject"/>.</summary>
141-
internal static JsonObject? ToJsonObject(this IReadOnlyDictionary<string, object?> properties) =>
142-
JsonSerializer.SerializeToNode(properties, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(IReadOnlyDictionary<string, object?>))) as JsonObject;
141+
internal static JsonObject? ToJsonObject(this IReadOnlyDictionary<string, object?> properties, JsonSerializerOptions? options = null)
142+
{
143+
options ??= McpJsonUtilities.DefaultOptions;
144+
return JsonSerializer.SerializeToNode(properties, options.GetTypeInfo(typeof(IReadOnlyDictionary<string, object?>))) as JsonObject;
145+
}
143146

144147
internal static AdditionalPropertiesDictionary ToAdditionalProperties(this JsonObject obj)
145148
{
@@ -365,12 +368,15 @@ public static IList<AIContent> ToAIContents(this IEnumerable<ResourceContents> c
365368

366369
/// <summary>Creates a new <see cref="ContentBlock"/> from the content of an <see cref="AIContent"/>.</summary>
367370
/// <param name="content">The <see cref="AIContent"/> to convert.</param>
371+
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use for serialization. If <see langword="null"/>, <see cref="McpJsonUtilities.DefaultOptions"/> is used.</param>
368372
/// <returns>The created <see cref="ContentBlock"/>.</returns>
369373
/// <exception cref="ArgumentNullException"><paramref name="content"/> is <see langword="null"/>.</exception>
370-
public static ContentBlock ToContentBlock(this AIContent content)
374+
public static ContentBlock ToContentBlock(this AIContent content, JsonSerializerOptions? options = null)
371375
{
372376
Throw.IfNull(content);
373377

378+
options ??= McpJsonUtilities.DefaultOptions;
379+
374380
ContentBlock contentBlock = content switch
375381
{
376382
TextContent textContent => new TextContentBlock
@@ -404,27 +410,27 @@ public static ContentBlock ToContentBlock(this AIContent content)
404410
{
405411
Id = callContent.CallId,
406412
Name = callContent.Name,
407-
Input = JsonSerializer.SerializeToElement(callContent.Arguments, McpJsonUtilities.DefaultOptions.GetTypeInfo<IDictionary<string, object?>>()!),
413+
Input = JsonSerializer.SerializeToElement(callContent.Arguments, options.GetTypeInfo<IDictionary<string, object?>>()!),
408414
},
409415

410416
FunctionResultContent resultContent => new ToolResultContentBlock()
411417
{
412418
ToolUseId = resultContent.CallId,
413419
IsError = resultContent.Exception is not null,
414420
Content =
415-
resultContent.Result is AIContent c ? [c.ToContentBlock()] :
416-
resultContent.Result is IEnumerable<AIContent> ec ? [.. ec.Select(c => c.ToContentBlock())] :
417-
[new TextContentBlock { Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo<object>()) }],
421+
resultContent.Result is AIContent c ? [c.ToContentBlock(options)] :
422+
resultContent.Result is IEnumerable<AIContent> ec ? [.. ec.Select(c => c.ToContentBlock(options))] :
423+
[new TextContentBlock { Text = JsonSerializer.Serialize(content, options.GetTypeInfo<object>()) }],
418424
StructuredContent = resultContent.Result is JsonElement je ? je : null,
419425
},
420426

421427
_ => new TextContentBlock
422428
{
423-
Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))),
429+
Text = JsonSerializer.Serialize(content, options.GetTypeInfo(typeof(object))),
424430
}
425431
};
426432

427-
contentBlock.Meta = content.AdditionalProperties?.ToJsonObject();
433+
contentBlock.Meta = content.AdditionalProperties?.ToJsonObject(options);
428434

429435
return contentBlock;
430436
}

src/ModelContextProtocol.Core/Server/McpServerTool.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ namespace ModelContextProtocol.Server;
9999
/// </item>
100100
/// <item>
101101
/// <term><see cref="AIContent"/></term>
102-
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
102+
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
103103
/// </item>
104104
/// <item>
105105
/// <term><see cref="string"/></term>
@@ -111,7 +111,7 @@ namespace ModelContextProtocol.Server;
111111
/// </item>
112112
/// <item>
113113
/// <term><see cref="IEnumerable{AIContent}"/> of <see cref="AIContent"/></term>
114-
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
114+
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
115115
/// </item>
116116
/// <item>
117117
/// <term><see cref="IEnumerable{ContentBlock}"/> of <see cref="ContentBlock"/></term>

src/ModelContextProtocol.Core/Server/McpServerToolAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ namespace ModelContextProtocol.Server;
9090
/// </item>
9191
/// <item>
9292
/// <term><see cref="AIContent"/></term>
93-
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
93+
/// <description>Converted to a single <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
9494
/// </item>
9595
/// <item>
9696
/// <term><see cref="string"/></term>
@@ -106,7 +106,7 @@ namespace ModelContextProtocol.Server;
106106
/// </item>
107107
/// <item>
108108
/// <term><see cref="IEnumerable{AIContent}"/> of <see cref="AIContent"/></term>
109-
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent)"/>.</description>
109+
/// <description>Each <see cref="AIContent"/> is converted to a <see cref="ContentBlock"/> object using <see cref="AIContentExtensions.ToContentBlock(AIContent, JsonSerializerOptions)"/>.</description>
110110
/// </item>
111111
/// <item>
112112
/// <term><see cref="IEnumerable{Content}"/> of <see cref="ContentBlock"/></term>

tests/ModelContextProtocol.Tests/AIContentExtensionsTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,36 @@ public void DataContent_ToContentBlock_WithAnonymousTypeInAdditionalProperties_P
305305
Assert.NotNull(imageBlock.Meta);
306306
Assert.True(imageBlock.Meta.ContainsKey("dimensions"));
307307
}
308+
309+
[Fact]
310+
public void ToContentBlock_WithCustomSerializerOptions_UsesProvidedOptions()
311+
{
312+
if (!JsonSerializer.IsReflectionEnabledByDefault)
313+
{
314+
return;
315+
}
316+
317+
// Create custom options with specific settings
318+
var customOptions = new JsonSerializerOptions(McpJsonUtilities.DefaultOptions)
319+
{
320+
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
321+
};
322+
323+
AIContent c = new()
324+
{
325+
AdditionalProperties = new()
326+
{
327+
["TestData"] = new { MyProperty = "value" }
328+
}
329+
};
330+
331+
var contentBlock = c.ToContentBlock(customOptions);
332+
333+
Assert.NotNull(contentBlock);
334+
Assert.NotNull(contentBlock.Meta);
335+
336+
// Verify that the custom naming policy was applied
337+
var json = contentBlock.Meta.ToString();
338+
Assert.Contains("my_property", json.ToLowerInvariant());
339+
}
308340
}

0 commit comments

Comments
 (0)