diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 5e83e0d577..037e61ab3d 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -33,14 +33,15 @@
-
+
+
-
+
@@ -101,10 +102,10 @@
-
-
+
+
-
+
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMetadataExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMetadataExtensions.cs
deleted file mode 100644
index 3c81c6abe8..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMetadataExtensions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Extensions.AI;
-
-namespace A2A;
-
-///
-/// Extension methods for A2A metadata dictionary.
-///
-internal static class A2AMetadataExtensions
-{
- ///
- /// Converts a dictionary of metadata to an .
- ///
- ///
- /// This method can be replaced by the one from A2A SDK once it is public.
- ///
- /// The metadata dictionary to convert.
- /// The converted , or null if the input is null or empty.
- internal static AdditionalPropertiesDictionary? ToAdditionalProperties(this Dictionary? metadata)
- {
- if (metadata is not { Count: > 0 })
- {
- return null;
- }
-
- var additionalProperties = new AdditionalPropertiesDictionary();
- foreach (var kvp in metadata)
- {
- additionalProperties[kvp.Key] = kvp.Value;
- }
- return additionalProperties;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/AdditionalPropertiesDictionaryExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/AdditionalPropertiesDictionaryExtensions.cs
deleted file mode 100644
index a3340d2ca8..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/AdditionalPropertiesDictionaryExtensions.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Agents.AI;
-
-namespace Microsoft.Extensions.AI;
-
-///
-/// Extension methods for AdditionalPropertiesDictionary.
-///
-internal static class AdditionalPropertiesDictionaryExtensions
-{
- ///
- /// Converts an to a dictionary of values suitable for A2A metadata.
- ///
- ///
- /// This method can be replaced by the one from A2A SDK once it is available.
- ///
- /// The additional properties dictionary to convert, or null.
- /// A dictionary of JSON elements representing the metadata, or null if the input is null or empty.
- internal static Dictionary? ToA2AMetadata(this AdditionalPropertiesDictionary? additionalProperties)
- {
- if (additionalProperties is not { Count: > 0 })
- {
- return null;
- }
-
- var metadata = new Dictionary();
-
- foreach (var kvp in additionalProperties)
- {
- if (kvp.Value is JsonElement)
- {
- metadata[kvp.Key] = (JsonElement)kvp.Value!;
- continue;
- }
-
- metadata[kvp.Key] = JsonSerializer.SerializeToElement(kvp.Value, A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)));
- }
-
- return metadata;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/A2AMetadataExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/A2AMetadataExtensions.cs
deleted file mode 100644
index 010264bb65..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/A2AMetadataExtensions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.Hosting.A2A.Converters;
-
-///
-/// Extension methods for A2A metadata dictionary.
-///
-internal static class A2AMetadataExtensions
-{
- ///
- /// Converts a dictionary of metadata to an .
- ///
- ///
- /// This method can be replaced by the one from A2A SDK once it is public.
- ///
- /// The metadata dictionary to convert.
- /// The converted , or null if the input is null or empty.
- internal static AdditionalPropertiesDictionary? ToAdditionalProperties(this Dictionary? metadata)
- {
- if (metadata is not { Count: > 0 })
- {
- return null;
- }
-
- var additionalProperties = new AdditionalPropertiesDictionary();
- foreach (var kvp in metadata)
- {
- additionalProperties[kvp.Key] = kvp.Value;
- }
- return additionalProperties;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/AdditionalPropertiesDictionaryExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/AdditionalPropertiesDictionaryExtensions.cs
deleted file mode 100644
index e557ff4e07..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/AdditionalPropertiesDictionaryExtensions.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.Hosting.A2A.Converters;
-
-///
-/// Extension methods for AdditionalPropertiesDictionary.
-///
-internal static class AdditionalPropertiesDictionaryExtensions
-{
- ///
- /// Converts an to a dictionary of values suitable for A2A metadata.
- ///
- ///
- /// This method can be replaced by the one from A2A SDK once it is available.
- ///
- /// The additional properties dictionary to convert, or null.
- /// A dictionary of JSON elements representing the metadata, or null if the input is null or empty.
- internal static Dictionary? ToA2AMetadata(this AdditionalPropertiesDictionary? additionalProperties)
- {
- if (additionalProperties is not { Count: > 0 })
- {
- return null;
- }
-
- var metadata = new Dictionary();
-
- foreach (var kvp in additionalProperties)
- {
- if (kvp.Value is JsonElement)
- {
- metadata[kvp.Key] = (JsonElement)kvp.Value!;
- continue;
- }
-
- metadata[kvp.Key] = JsonSerializer.SerializeToElement(kvp.Value, A2AHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)));
- }
-
- return metadata;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs
index 751f518277..107f4f0260 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs
@@ -5,6 +5,7 @@
using System.Globalization;
using System.Linq;
using System.Net.Http;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -222,31 +223,36 @@ private static void PopulateResultContent(McpServerToolResultContent resultConte
}
}
- private static AIContent ConvertContentBlock(ContentBlock block)
+ internal static AIContent ConvertContentBlock(ContentBlock block)
{
return block switch
{
TextContentBlock text => new TextContent(text.Text),
- ImageContentBlock image => CreateDataContentFromBase64(image.Data, image.MimeType ?? "image/*"),
- AudioContentBlock audio => CreateDataContentFromBase64(audio.Data, audio.MimeType ?? "audio/*"),
+ ImageContentBlock image => CreateDataContent(image.Data, image.MimeType ?? "image/*"),
+ AudioContentBlock audio => CreateDataContent(audio.Data, audio.MimeType ?? "audio/*"),
_ => new TextContent(block.ToString() ?? string.Empty),
};
}
- private static DataContent CreateDataContentFromBase64(string? base64Data, string mediaType)
+ private static DataContent CreateDataContent(ReadOnlyMemory base64Utf8Data, string mediaType)
{
- if (string.IsNullOrEmpty(base64Data))
+ if (base64Utf8Data.IsEmpty)
{
return new DataContent($"data:{mediaType};base64,", mediaType);
}
+#if NET8_0_OR_GREATER
+ string base64 = Encoding.UTF8.GetString(base64Utf8Data.Span);
+#else
+ string base64 = Encoding.UTF8.GetString(base64Utf8Data.ToArray());
+#endif
+
// If it's already a data URI, use it directly
- if (base64Data.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
+ if (base64.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
- return new DataContent(base64Data, mediaType);
+ return new DataContent(base64, mediaType);
}
- // Otherwise, construct a data URI from the base64 data
- return new DataContent($"data:{mediaType};base64,{base64Data}", mediaType);
+ return new DataContent($"data:{mediaType};base64,{base64}", mediaType);
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMetadataExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMetadataExtensionsTests.cs
deleted file mode 100644
index 1307b9f4b6..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMetadataExtensionsTests.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using A2A;
-
-namespace Microsoft.Agents.AI.A2A.UnitTests;
-
-///
-/// Unit tests for the class.
-///
-public sealed class A2AMetadataExtensionsTests
-{
- [Fact]
- public void ToAdditionalProperties_WithNullMetadata_ReturnsNull()
- {
- // Arrange
- Dictionary? metadata = null;
-
- // Act
- var result = metadata.ToAdditionalProperties();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToAdditionalProperties_WithEmptyMetadata_ReturnsNull()
- {
- // Arrange
- var metadata = new Dictionary();
-
- // Act
- var result = metadata.ToAdditionalProperties();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToAdditionalProperties_WithMultipleProperties_ReturnsAdditionalPropertiesDictionaryWithAllProperties()
- {
- // Arrange
- var metadata = new Dictionary
- {
- { "stringKey", JsonSerializer.SerializeToElement("stringValue") },
- { "numberKey", JsonSerializer.SerializeToElement(42) },
- { "booleanKey", JsonSerializer.SerializeToElement(true) }
- };
-
- // Act
- var result = metadata.ToAdditionalProperties();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(3, result.Count);
-
- Assert.True(result.ContainsKey("stringKey"));
- Assert.Equal("stringValue", ((JsonElement)result["stringKey"]!).GetString());
-
- Assert.True(result.ContainsKey("numberKey"));
- Assert.Equal(42, ((JsonElement)result["numberKey"]!).GetInt32());
-
- Assert.True(result.ContainsKey("booleanKey"));
- Assert.True(((JsonElement)result["booleanKey"]!).GetBoolean());
- }
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/AdditionalPropertiesDictionaryExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/AdditionalPropertiesDictionaryExtensionsTests.cs
deleted file mode 100644
index 4972b8857f..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/AdditionalPropertiesDictionaryExtensionsTests.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.A2A.UnitTests;
-
-///
-/// Unit tests for the class.
-///
-public sealed class AdditionalPropertiesDictionaryExtensionsTests
-{
- [Fact]
- public void ToA2AMetadata_WithNullAdditionalProperties_ReturnsNull()
- {
- // Arrange
- AdditionalPropertiesDictionary? additionalProperties = null;
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToA2AMetadata_WithEmptyAdditionalProperties_ReturnsNull()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = [];
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToA2AMetadata_WithStringValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "stringKey", "stringValue" }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("stringKey"));
- Assert.Equal("stringValue", result["stringKey"].GetString());
- }
-
- [Fact]
- public void ToA2AMetadata_WithNumericValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "numberKey", 42 }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("numberKey"));
- Assert.Equal(42, result["numberKey"].GetInt32());
- }
-
- [Fact]
- public void ToA2AMetadata_WithBooleanValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "booleanKey", true }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("booleanKey"));
- Assert.True(result["booleanKey"].GetBoolean());
- }
-
- [Fact]
- public void ToA2AMetadata_WithMultipleProperties_ReturnsMetadataWithAllProperties()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "stringKey", "stringValue" },
- { "numberKey", 42 },
- { "booleanKey", true }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(3, result.Count);
-
- Assert.True(result.ContainsKey("stringKey"));
- Assert.Equal("stringValue", result["stringKey"].GetString());
-
- Assert.True(result.ContainsKey("numberKey"));
- Assert.Equal(42, result["numberKey"].GetInt32());
-
- Assert.True(result.ContainsKey("booleanKey"));
- Assert.True(result["booleanKey"].GetBoolean());
- }
-
- [Fact]
- public void ToA2AMetadata_WithArrayValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- int[] arrayValue = [1, 2, 3];
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "arrayKey", arrayValue }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("arrayKey"));
- Assert.Equal(JsonValueKind.Array, result["arrayKey"].ValueKind);
- Assert.Equal(3, result["arrayKey"].GetArrayLength());
- }
-
- [Fact]
- public void ToA2AMetadata_WithNullValue_ReturnsMetadataWithNullJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "nullKey", null! }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("nullKey"));
- Assert.Equal(JsonValueKind.Null, result["nullKey"].ValueKind);
- }
-
- [Fact]
- public void ToA2AMetadata_WithJsonElementValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- JsonElement jsonElement = JsonSerializer.SerializeToElement(new { name = "test", value = 123 });
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "jsonElementKey", jsonElement }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("jsonElementKey"));
- Assert.Equal(JsonValueKind.Object, result["jsonElementKey"].ValueKind);
- Assert.Equal("test", result["jsonElementKey"].GetProperty("name").GetString());
- Assert.Equal(123, result["jsonElementKey"].GetProperty("value").GetInt32());
- }
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Converters/AdditionalPropertiesDictionaryExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Converters/AdditionalPropertiesDictionaryExtensionsTests.cs
deleted file mode 100644
index e0c8c4e96b..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Converters/AdditionalPropertiesDictionaryExtensionsTests.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using Microsoft.Agents.AI.Hosting.A2A.Converters;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.Hosting.A2A.UnitTests.Converters;
-
-///
-/// Unit tests for the class.
-///
-public sealed class AdditionalPropertiesDictionaryExtensionsTests
-{
- [Fact]
- public void ToA2AMetadata_WithNullAdditionalProperties_ReturnsNull()
- {
- // Arrange
- AdditionalPropertiesDictionary? additionalProperties = null;
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToA2AMetadata_WithEmptyAdditionalProperties_ReturnsNull()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = [];
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void ToA2AMetadata_WithStringValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "stringKey", "stringValue" }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("stringKey"));
- Assert.Equal("stringValue", result["stringKey"].GetString());
- }
-
- [Fact]
- public void ToA2AMetadata_WithNumericValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "numberKey", 42 }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("numberKey"));
- Assert.Equal(42, result["numberKey"].GetInt32());
- }
-
- [Fact]
- public void ToA2AMetadata_WithBooleanValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "booleanKey", true }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("booleanKey"));
- Assert.True(result["booleanKey"].GetBoolean());
- }
-
- [Fact]
- public void ToA2AMetadata_WithMultipleProperties_ReturnsMetadataWithAllProperties()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "stringKey", "stringValue" },
- { "numberKey", 42 },
- { "booleanKey", true }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(3, result.Count);
-
- Assert.True(result.ContainsKey("stringKey"));
- Assert.Equal("stringValue", result["stringKey"].GetString());
-
- Assert.True(result.ContainsKey("numberKey"));
- Assert.Equal(42, result["numberKey"].GetInt32());
-
- Assert.True(result.ContainsKey("booleanKey"));
- Assert.True(result["booleanKey"].GetBoolean());
- }
-
- [Fact]
- public void ToA2AMetadata_WithArrayValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- int[] arrayValue = [1, 2, 3];
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "arrayKey", arrayValue }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("arrayKey"));
- Assert.Equal(JsonValueKind.Array, result["arrayKey"].ValueKind);
- Assert.Equal(3, result["arrayKey"].GetArrayLength());
- }
-
- [Fact]
- public void ToA2AMetadata_WithNullValue_ReturnsMetadataWithNullJsonElement()
- {
- // Arrange
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "nullKey", null! }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("nullKey"));
- Assert.Equal(JsonValueKind.Null, result["nullKey"].ValueKind);
- }
-
- [Fact]
- public void ToA2AMetadata_WithJsonElementValue_ReturnsMetadataWithJsonElement()
- {
- // Arrange
- JsonElement jsonElement = JsonSerializer.SerializeToElement(new { name = "test", value = 123 });
- AdditionalPropertiesDictionary additionalProperties = new()
- {
- { "jsonElementKey", jsonElement }
- };
-
- // Act
- Dictionary? result = additionalProperties.ToA2AMetadata();
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result);
- Assert.True(result.ContainsKey("jsonElementKey"));
- Assert.Equal(JsonValueKind.Object, result["jsonElementKey"].ValueKind);
- Assert.Equal("test", result["jsonElementKey"].GetProperty("name").GetString());
- Assert.Equal(123, result["jsonElementKey"].GetProperty("value").GetInt32());
- }
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs
index 858ea9db14..abfa95cc36 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs
@@ -3,9 +3,12 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
+using Microsoft.Extensions.AI;
+using ModelContextProtocol.Protocol;
namespace Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests;
@@ -342,4 +345,148 @@ public async Task DefaultMcpToolHandler_ShouldImplementIAsyncDisposableAsync()
}
#endregion
+
+ #region ConvertContentBlock Tests
+
+ [Fact]
+ public void ConvertContentBlock_TextContentBlock_ShouldReturnTextContent()
+ {
+ // Arrange
+ TextContentBlock block = new() { Text = "hello world" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ result.Should().BeOfType()
+ .Which.Text.Should().Be("hello world");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_ImageContentBlock_WithEmptyData_ShouldReturnDataContentWithEmptyUri()
+ {
+ // Arrange
+ ImageContentBlock block = new() { Data = ReadOnlyMemory.Empty, MimeType = "image/png" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("image/png");
+ dataContent.Uri.Should().Be("data:image/png;base64,");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_ImageContentBlock_WithBase64Payload_ShouldReturnDataContent()
+ {
+ // Arrange
+ byte[] base64Bytes = Encoding.UTF8.GetBytes("iVBORw0KGgo=");
+ ImageContentBlock block = new() { Data = new ReadOnlyMemory(base64Bytes), MimeType = "image/png" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("image/png");
+ dataContent.Uri.Should().Be("data:image/png;base64,iVBORw0KGgo=");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_ImageContentBlock_WithDataUri_ShouldReturnDataContentDirectly()
+ {
+ // Arrange
+ const string DataUri = "data:image/jpeg;base64,/9j/4AAQ";
+ byte[] dataUriBytes = Encoding.UTF8.GetBytes(DataUri);
+ ImageContentBlock block = new() { Data = new ReadOnlyMemory(dataUriBytes), MimeType = "image/jpeg" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("image/jpeg");
+ dataContent.Uri.Should().Be(DataUri);
+ }
+
+ [Fact]
+ public void ConvertContentBlock_ImageContentBlock_WithNullMimeType_ShouldDefaultToImageWildcard()
+ {
+ // Arrange
+ byte[] base64Bytes = Encoding.UTF8.GetBytes("iVBORw0KGgo=");
+ ImageContentBlock block = new() { Data = new ReadOnlyMemory(base64Bytes), MimeType = null! };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("image/*");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_AudioContentBlock_WithEmptyData_ShouldReturnDataContentWithEmptyUri()
+ {
+ // Arrange
+ AudioContentBlock block = new() { Data = ReadOnlyMemory.Empty, MimeType = "audio/wav" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("audio/wav");
+ dataContent.Uri.Should().Be("data:audio/wav;base64,");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_AudioContentBlock_WithBase64Payload_ShouldReturnDataContent()
+ {
+ // Arrange
+ byte[] base64Bytes = Encoding.UTF8.GetBytes("UklGRiQA");
+ AudioContentBlock block = new() { Data = new ReadOnlyMemory(base64Bytes), MimeType = "audio/wav" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("audio/wav");
+ dataContent.Uri.Should().Be("data:audio/wav;base64,UklGRiQA");
+ }
+
+ [Fact]
+ public void ConvertContentBlock_AudioContentBlock_WithDataUri_ShouldReturnDataContentDirectly()
+ {
+ // Arrange
+ const string DataUri = "data:audio/mp3;base64,//uQxAAA";
+ byte[] dataUriBytes = Encoding.UTF8.GetBytes(DataUri);
+ AudioContentBlock block = new() { Data = new ReadOnlyMemory(dataUriBytes), MimeType = "audio/mp3" };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("audio/mp3");
+ dataContent.Uri.Should().Be(DataUri);
+ }
+
+ [Fact]
+ public void ConvertContentBlock_AudioContentBlock_WithNullMimeType_ShouldDefaultToAudioWildcard()
+ {
+ // Arrange
+ byte[] base64Bytes = Encoding.UTF8.GetBytes("UklGRiQA");
+ AudioContentBlock block = new() { Data = new ReadOnlyMemory(base64Bytes), MimeType = null! };
+
+ // Act
+ AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
+
+ // Assert
+ DataContent dataContent = result.Should().BeOfType().Subject;
+ dataContent.MediaType.Should().Be("audio/*");
+ }
+
+ #endregion
}