diff --git a/src/ModelContextProtocol.Core/AIContentExtensions.cs b/src/ModelContextProtocol.Core/AIContentExtensions.cs
index b1ba32bf..cb2afeab 100644
--- a/src/ModelContextProtocol.Core/AIContentExtensions.cs
+++ b/src/ModelContextProtocol.Core/AIContentExtensions.cs
@@ -23,6 +23,7 @@ public static class AIContentExtensions
/// satisfy sampling requests using the specified .
///
/// The with which to satisfy sampling requests.
+ /// The to use for serializing user-provided objects. If , is used.
/// The created handler delegate that can be assigned to .
///
///
@@ -36,15 +37,18 @@ public static class AIContentExtensions
///
/// is .
public static Func, CancellationToken, ValueTask> CreateSamplingHandler(
- this IChatClient chatClient)
+ this IChatClient chatClient,
+ JsonSerializerOptions? serializerOptions = null)
{
Throw.IfNull(chatClient);
+ serializerOptions ??= McpJsonUtilities.DefaultOptions;
+
return async (requestParams, progress, cancellationToken) =>
{
Throw.IfNull(requestParams);
- var (messages, options) = ToChatClientArguments(requestParams);
+ var (messages, options) = ToChatClientArguments(requestParams, serializerOptions);
var progressToken = requestParams.ProgressToken;
List updates = [];
@@ -75,12 +79,12 @@ public static class AIContentExtensions
chatResponse.FinishReason == ChatFinishReason.Length ? CreateMessageResult.StopReasonMaxTokens :
chatResponse.FinishReason == ChatFinishReason.ToolCalls ? CreateMessageResult.StopReasonToolUse :
chatResponse.FinishReason.ToString(),
- Meta = chatResponse.AdditionalProperties?.ToJsonObject(),
+ Meta = chatResponse.AdditionalProperties?.ToJsonObject(serializerOptions),
Role = lastMessage?.Role == ChatRole.User ? Role.User : Role.Assistant,
Content = contents,
};
- static (IList Messages, ChatOptions? Options) ToChatClientArguments(CreateMessageRequestParams requestParams)
+ static (IList Messages, ChatOptions? Options) ToChatClientArguments(CreateMessageRequestParams requestParams, JsonSerializerOptions serializerOptions)
{
ChatOptions? options = null;
@@ -126,7 +130,7 @@ public static class AIContentExtensions
List messages = [];
foreach (var sm in requestParams.Messages)
{
- if (sm.Content?.Select(b => b.ToAIContent()).OfType().ToList() is { Count: > 0 } aiContents)
+ if (sm.Content?.Select(b => b.ToAIContent(serializerOptions)).OfType().ToList() is { Count: > 0 } aiContents)
{
messages.Add(new ChatMessage(sm.Role is Role.Assistant ? ChatRole.Assistant : ChatRole.User, aiContents));
}
@@ -138,8 +142,10 @@ public static class AIContentExtensions
}
/// Converts the specified dictionary to a .
- internal static JsonObject? ToJsonObject(this IReadOnlyDictionary properties) =>
- JsonSerializer.SerializeToNode(properties, McpJsonUtilities.JsonContext.Default.IReadOnlyDictionaryStringObject) as JsonObject;
+ internal static JsonObject? ToJsonObject(this IReadOnlyDictionary properties, JsonSerializerOptions options)
+ {
+ return JsonSerializer.SerializeToNode(properties, options.GetTypeInfo(typeof(IReadOnlyDictionary))) as JsonObject;
+ }
internal static AdditionalPropertiesDictionary ToAdditionalProperties(this JsonObject obj)
{
@@ -156,17 +162,18 @@ internal static AdditionalPropertiesDictionary ToAdditionalProperties(this JsonO
/// Converts a to a object.
///
/// The prompt message to convert.
+ /// The to use for deserialization. If , is used.
/// A object created from the prompt message.
///
/// This method transforms a protocol-specific from the Model Context Protocol
/// into a standard object that can be used with AI client libraries.
///
/// is .
- public static ChatMessage ToChatMessage(this PromptMessage promptMessage)
+ public static ChatMessage ToChatMessage(this PromptMessage promptMessage, JsonSerializerOptions? options = null)
{
Throw.IfNull(promptMessage);
- AIContent? content = ToAIContent(promptMessage.Content);
+ AIContent? content = promptMessage.Content.ToAIContent(options);
return new()
{
@@ -181,6 +188,7 @@ public static ChatMessage ToChatMessage(this PromptMessage promptMessage)
///
/// The tool result to convert.
/// The identifier for the function call request that triggered the tool invocation.
+ /// The to use for serialization. If , is used.
/// A object created from the tool result.
///
/// This method transforms a protocol-specific from the Model Context Protocol
@@ -189,12 +197,14 @@ public static ChatMessage ToChatMessage(this PromptMessage promptMessage)
/// serialized .
///
/// or is .
- public static ChatMessage ToChatMessage(this CallToolResult result, string callId)
+ public static ChatMessage ToChatMessage(this CallToolResult result, string callId, JsonSerializerOptions? options = null)
{
Throw.IfNull(result);
Throw.IfNull(callId);
- return new(ChatRole.Tool, [new FunctionResultContent(callId, JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CallToolResult))
+ options ??= McpJsonUtilities.DefaultOptions;
+
+ return new(ChatRole.Tool, [new FunctionResultContent(callId, JsonSerializer.SerializeToElement(result, options.GetTypeInfo()))
{
RawRepresentation = result,
}]);
@@ -248,6 +258,7 @@ public static IList ToPromptMessages(this ChatMessage chatMessage
/// Creates a new from the content of a .
/// The to convert.
+ /// The to use for deserialization. If , is used.
///
/// The created . If the content can't be converted (such as when it's a resource link), is returned.
///
@@ -256,10 +267,12 @@ public static IList ToPromptMessages(this ChatMessage chatMessage
/// content types, enabling seamless integration between the protocol and AI client libraries.
///
/// is .
- public static AIContent? ToAIContent(this ContentBlock content)
+ public static AIContent? ToAIContent(this ContentBlock content, JsonSerializerOptions? options = null)
{
Throw.IfNull(content);
+ options ??= McpJsonUtilities.DefaultOptions;
+
AIContent? ac = content switch
{
TextContentBlock textContent => new TextContent(textContent.Text),
@@ -271,11 +284,11 @@ public static IList ToPromptMessages(this ChatMessage chatMessage
EmbeddedResourceBlock resourceContent => resourceContent.Resource.ToAIContent(),
ToolUseContentBlock toolUse => FunctionCallContent.CreateFromParsedArguments(toolUse.Input, toolUse.Id, toolUse.Name,
- static json => JsonSerializer.Deserialize(json, McpJsonUtilities.JsonContext.Default.IDictionaryStringObject)),
+ json => JsonSerializer.Deserialize(json, options.GetTypeInfo>())),
ToolResultContentBlock toolResult => new FunctionResultContent(
toolResult.ToolUseId,
- toolResult.Content.Count == 1 ? toolResult.Content[0].ToAIContent() : toolResult.Content.Select(c => c.ToAIContent()).OfType().ToList())
+ toolResult.Content.Count == 1 ? toolResult.Content[0].ToAIContent(options) : toolResult.Content.Select(c => c.ToAIContent(options)).OfType().ToList())
{
Exception = toolResult.IsError is true ? new() : null,
},
@@ -320,6 +333,7 @@ public static AIContent ToAIContent(this ResourceContents content)
/// Creates a list of from a sequence of .
/// The instances to convert.
+ /// The to use for deserialization. If , is used.
/// The created instances.
///
///
@@ -328,16 +342,16 @@ public static AIContent ToAIContent(this ResourceContents content)
/// when processing the contents of a message or response.
///
///
- /// Each object is converted using ,
+ /// Each object is converted using ,
/// preserving the type-specific conversion logic for text, images, audio, and resources.
///
///
/// is .
- public static IList ToAIContents(this IEnumerable contents)
+ public static IList ToAIContents(this IEnumerable contents, JsonSerializerOptions? options = null)
{
Throw.IfNull(contents);
- return [.. contents.Select(ToAIContent).OfType()];
+ return [.. contents.Select(c => c.ToAIContent(options)).OfType()];
}
/// Creates a list of from a sequence of .
@@ -365,12 +379,15 @@ public static IList ToAIContents(this IEnumerable c
/// Creates a new from the content of an .
/// The to convert.
+ /// The to use for serialization. If , is used.
/// The created .
/// is .
- public static ContentBlock ToContentBlock(this AIContent content)
+ public static ContentBlock ToContentBlock(this AIContent content, JsonSerializerOptions? options = null)
{
Throw.IfNull(content);
+ options ??= McpJsonUtilities.DefaultOptions;
+
ContentBlock contentBlock = content switch
{
TextContent textContent => new TextContentBlock
@@ -404,7 +421,7 @@ public static ContentBlock ToContentBlock(this AIContent content)
{
Id = callContent.CallId,
Name = callContent.Name,
- Input = JsonSerializer.SerializeToElement(callContent.Arguments, McpJsonUtilities.DefaultOptions.GetTypeInfo>()!),
+ Input = JsonSerializer.SerializeToElement(callContent.Arguments, options.GetTypeInfo>()!),
},
FunctionResultContent resultContent => new ToolResultContentBlock()
@@ -412,19 +429,19 @@ public static ContentBlock ToContentBlock(this AIContent content)
ToolUseId = resultContent.CallId,
IsError = resultContent.Exception is not null,
Content =
- resultContent.Result is AIContent c ? [c.ToContentBlock()] :
- resultContent.Result is IEnumerable ec ? [.. ec.Select(c => c.ToContentBlock())] :
- [new TextContentBlock { Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo