using Microsoft.Extensions.AI; using ModelContextProtocol.Protocol; #if !NET using System.Runtime.InteropServices; #endif using System.Text.Json; namespace ModelContextProtocol; /// /// Provides extension methods for converting between Model Context Protocol (MCP) types and Microsoft.Extensions.AI types. /// /// /// This class serves as an adapter layer between Model Context Protocol (MCP) types and the model types /// from the Microsoft.Extensions.AI namespace. /// public static class AIContentExtensions { /// /// Converts a to a object. /// /// The prompt message to convert. /// 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. /// public static ChatMessage ToChatMessage(this PromptMessage promptMessage) { Throw.IfNull(promptMessage); return new() { RawRepresentation = promptMessage, Role = promptMessage.Role == Role.User ? ChatRole.User : ChatRole.Assistant, Contents = [ToAIContent(promptMessage.Content)] }; } /// /// Converts a to a list of objects. /// /// The prompt result containing messages to convert. /// A list of objects created from the prompt messages. /// /// This method transforms protocol-specific objects from a Model Context Protocol /// prompt result into standard objects that can be used with AI client libraries. /// public static IList ToChatMessages(this GetPromptResult promptResult) { Throw.IfNull(promptResult); return promptResult.Messages.Select(m => m.ToChatMessage()).ToList(); } /// /// Converts a to a list of objects. /// /// The chat message to convert. /// A list of objects created from the chat message's contents. /// /// This method transforms standard objects used with AI client libraries into /// protocol-specific objects for the Model Context Protocol system. /// Only representable content items are processed. /// public static IList ToPromptMessages(this ChatMessage chatMessage) { Throw.IfNull(chatMessage); Role r = chatMessage.Role == ChatRole.User ? Role.User : Role.Assistant; List messages = []; foreach (var content in chatMessage.Contents) { if (content is TextContent or DataContent) { messages.Add(new PromptMessage { Role = r, Content = content.ToContent() }); } } return messages; } /// Creates a new from the content of a . /// The to convert. /// The created . /// /// This method converts Model Context Protocol content types to the equivalent Microsoft.Extensions.AI /// content types, enabling seamless integration between the protocol and AI client libraries. /// public static AIContent ToAIContent(this Content content) { Throw.IfNull(content); AIContent ac; if (content is { Type: "image" or "audio", MimeType: not null, Data: not null }) { ac = new DataContent(Convert.FromBase64String(content.Data), content.MimeType); } else if (content is { Type: "resource" } && content.Resource is { } resourceContents) { ac = resourceContents.ToAIContent(); } else { ac = new TextContent(content.Text); } ac.RawRepresentation = content; return ac; } /// Creates a new from the content of a . /// The to convert. /// The created . /// /// This method converts Model Context Protocol resource types to the equivalent Microsoft.Extensions.AI /// content types, enabling seamless integration between the protocol and AI client libraries. /// public static AIContent ToAIContent(this ResourceContents content) { Throw.IfNull(content); AIContent ac = content switch { BlobResourceContents blobResource => new DataContent(Convert.FromBase64String(blobResource.Blob), blobResource.MimeType ?? "application/octet-stream"), TextResourceContents textResource => new TextContent(textResource.Text), _ => throw new NotSupportedException($"Resource type '{content.GetType().Name}' is not supported.") }; (ac.AdditionalProperties ??= [])["uri"] = content.Uri; ac.RawRepresentation = content; return ac; } /// Creates a list of from a sequence of . /// The instances to convert. /// The created instances. /// /// /// This method converts a collection of Model Context Protocol content objects into a collection of /// Microsoft.Extensions.AI content objects. It's useful when working with multiple content items, such as /// when processing the contents of a message or response. /// /// /// Each object is converted using , /// preserving the type-specific conversion logic for text, images, audio, and resources. /// /// public static IList ToAIContents(this IEnumerable contents) { Throw.IfNull(contents); return [.. contents.Select(ToAIContent)]; } /// Creates a list of from a sequence of . /// The instances to convert. /// A list of objects created from the resource contents. /// /// /// This method converts a collection of Model Context Protocol resource objects into a collection of /// Microsoft.Extensions.AI content objects. It's useful when working with multiple resources, such as /// when processing the contents of a . /// /// /// Each object is converted using , /// preserving the type-specific conversion logic: text resources become objects and /// binary resources become objects. /// /// public static IList ToAIContents(this IEnumerable contents) { Throw.IfNull(contents); return [.. contents.Select(ToAIContent)]; } /// Extracts the data from a as a Base64 string. internal static string GetBase64Data(this DataContent dataContent) { #if NET return Convert.ToBase64String(dataContent.Data.Span); #else return MemoryMarshal.TryGetArray(dataContent.Data, out ArraySegment segment) ? Convert.ToBase64String(segment.Array!, segment.Offset, segment.Count) : Convert.ToBase64String(dataContent.Data.ToArray()); #endif } internal static Content ToContent(this AIContent content) => content switch { TextContent textContent => new() { Text = textContent.Text, Type = "text", }, DataContent dataContent => new() { Data = dataContent.GetBase64Data(), MimeType = dataContent.MediaType, Type = dataContent.HasTopLevelMediaType("image") ? "image" : dataContent.HasTopLevelMediaType("audio") ? "audio" : "resource", }, _ => new() { Text = JsonSerializer.Serialize(content, McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))), Type = "text", } }; }