Addressed additional properties metadata in the conversion fallback.

This commit is contained in:
Peter Ibekwe
2026-05-15 12:38:49 -07:00
Unverified
parent 8b1028e5d8
commit 3f51100b2f
2 changed files with 81 additions and 2 deletions
@@ -28,6 +28,8 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.Mcp;
/// </remarks>
public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable
{
private const string FilenameAdditionalPropertyName = "filename";
/// <summary>
/// Reserved <c>toolName</c> value that maps an <see cref="IMcpToolHandler.InvokeToolAsync"/> request
/// to the MCP protocol <c>tools/list</c> discovery operation.
@@ -280,11 +282,41 @@ public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable
// UriContent here so callers always receive a usable AIContent.
return block.ToAIContent() ?? block switch
{
ResourceLinkBlock link => new UriContent(link.Uri, link.MimeType ?? "application/octet-stream") { RawRepresentation = link },
_ => new TextContent(block.ToString() ?? string.Empty) { RawRepresentation = block },
ResourceLinkBlock link => new UriContent(link.Uri, link.MimeType ?? "application/octet-stream")
{
RawRepresentation = link,
AdditionalProperties = CreateAdditionalProperties(link),
},
_ => new TextContent(block.ToString() ?? string.Empty)
{
RawRepresentation = block,
AdditionalProperties = CreateAdditionalProperties(block),
},
};
}
private static AdditionalPropertiesDictionary? CreateAdditionalProperties(ContentBlock block)
{
AdditionalPropertiesDictionary? properties = null;
if (block.Meta is not null)
{
foreach (var property in block.Meta)
{
properties ??= new AdditionalPropertiesDictionary();
properties.Add(property.Key, property.Value);
}
}
if (block is ResourceLinkBlock { Name: { Length: > 0 } name })
{
properties ??= new AdditionalPropertiesDictionary();
properties.TryAdd(FilenameAdditionalPropertyName, name);
}
return properties;
}
private static string SerializeToolsList(IEnumerable<Tool> tools)
{
using MemoryStream stream = new();
@@ -616,6 +616,53 @@ public sealed class DefaultMcpToolHandlerTests
uriContent.MediaType.Should().Be("application/octet-stream");
}
[Fact]
public void ConvertContentBlock_ResourceLinkBlock_WithMeta_ShouldPropagateToAdditionalProperties()
{
// Arrange
ResourceLinkBlock block = new()
{
Uri = "https://example.com/resource.bin",
Name = string.Empty,
MimeType = "application/zip",
Meta = new System.Text.Json.Nodes.JsonObject
{
["traceId"] = "abc-123",
["priority"] = 7,
},
};
// Act
AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
// Assert
UriContent uriContent = result.Should().BeOfType<UriContent>().Subject;
uriContent.AdditionalProperties.Should().NotBeNull();
uriContent.AdditionalProperties!.Should().HaveCount(2);
uriContent.AdditionalProperties["traceId"].Should().BeSameAs(block.Meta!["traceId"]);
uriContent.AdditionalProperties["priority"].Should().BeSameAs(block.Meta["priority"]);
}
[Fact]
public void ConvertContentBlock_ResourceLinkBlock_WithName_ShouldMapNameToFilenameAdditionalProperty()
{
// Arrange
ResourceLinkBlock block = new()
{
Uri = "https://example.com/resource.bin",
Name = "resource.bin",
MimeType = "application/zip",
};
// Act
AIContent result = DefaultMcpToolHandler.ConvertContentBlock(block);
// Assert
UriContent uriContent = result.Should().BeOfType<UriContent>().Subject;
uriContent.AdditionalProperties.Should().NotBeNull();
uriContent.AdditionalProperties!["filename"].Should().Be("resource.bin");
}
[Fact]
public void ConvertContentBlock_ToolUseContentBlock_ShouldReturnFunctionCallContent()
{