Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions servers/Azure.Mcp.Server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The Azure MCP Server updates automatically by default whenever a new release com

### Bugs Fixed

- Fixed a bug where agents connect command result including file search result fail with serialization error. [[#1205](https://github.com/microsoft/mcp/pull/1205)]

### Other Changes

- Begin capturing information for the MCP client request's `_meta` store. [[#1154](https://github.com/microsoft/mcp/pull/1154)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ namespace Azure.Mcp.Tools.Foundry.Commands;
[JsonSerializable(typeof(ThreadCreateResult))]
[JsonSerializable(typeof(ThreadListResult))]
[JsonSerializable(typeof(ThreadGetMessagesResult))]
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AgentFileSearchResult type should be added to the JSON serialization context. While List<AgentFileSearchResult> is registered on line 67, the individual type needs to be registered as well for proper AOT serialization. Add [JsonSerializable(typeof(AgentFileSearchResult))] before line 67.

Suggested change
[JsonSerializable(typeof(ThreadGetMessagesResult))]
[JsonSerializable(typeof(ThreadGetMessagesResult))]
[JsonSerializable(typeof(AgentFileSearchResult))]

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Copilot right? I don't think it's necessary.

[JsonSerializable(typeof(List<AgentFileSearchResult>))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault)]
internal sealed partial class FoundryJsonContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.AI.Agents.Persistent;

namespace Azure.Mcp.Tools.Foundry.Models;

internal class AgentFileSearchResult
{
[JsonPropertyName("fileId")]
public string? FileId { get; set; }

[JsonPropertyName("fileName")]
public string? FileName { get; set; }

[JsonPropertyName("score")]
public float Score { get; set; }

[JsonPropertyName("content")]
public IReadOnlyList<FileSearchToolCallContent>? Content { get; set; }
}
101 changes: 68 additions & 33 deletions tools/Azure.Mcp.Tools.Foundry/src/Services/FoundryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,26 +1257,10 @@ private List<ToolDefinitionAIFunction> ConvertToolDefinitionsFromString(string?
{
foreach (RunStepToolCall toolCall in details.ToolCalls)
{
Microsoft.Extensions.AI.ChatMessage CreateRequestMessage(string name, Dictionary<string, object?> arguments) =>
new(ChatRole.Assistant, [new FunctionCallContent(toolCall.Id, name, arguments)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};

Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(object result) =>
new(ChatRole.Tool, [new FunctionResultContent(toolCall.Id, result)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};

switch (toolCall)
{
case RunStepFunctionToolCall function:
yield return CreateRequestMessage(function.Name, Parse(function.Arguments) ?? []);
yield return CreateRequestMessage(toolCall.Id, function.Name, Parse(function.Arguments) ?? [], step);
// TODO: output doesn't appear to be available in the API

static Dictionary<string, object?>? Parse(string arguments)
Expand All @@ -1288,43 +1272,44 @@ Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(object result) =>
break;

case RunStepCodeInterpreterToolCall code:
yield return CreateRequestMessage("code_interpreter", new() { ["input"] = code.Input });
yield return CreateResponseMessage(string.Concat(code.Outputs.OfType<RunStepCodeInterpreterLogOutput>().Select(o => o.Logs)));
yield return CreateRequestMessage(toolCall.Id, "code_interpreter", new() { ["input"] = code.Input }, step);
yield return CreateResponseMessage(toolCall.Id, string.Concat(code.Outputs.OfType<RunStepCodeInterpreterLogOutput>().Select(o => o.Logs)), step);
break;

case RunStepBingGroundingToolCall bing:
yield return CreateRequestMessage("bing_grounding", new() { ["requesturl"] = bing.BingGrounding["requesturl"] });
yield return CreateRequestMessage(toolCall.Id, "bing_grounding", new() { ["requesturl"] = bing.BingGrounding["requesturl"] }, step);
break;

case RunStepFileSearchToolCall fileSearch:
yield return CreateRequestMessage("file_search", new()
yield return CreateRequestMessage(toolCall.Id, "file_search", new()
{
["ranking_options"] = JsonSerializer.SerializeToElement(new()
{
["ranker"] = fileSearch.FileSearch.RankingOptions.Ranker,
["score_threshold"] = fileSearch.FileSearch.RankingOptions.ScoreThreshold,
}, DictionaryTypeInfo)
});
yield return CreateResponseMessage(fileSearch.FileSearch.Results.Select(r => new
}, step);
List<AgentFileSearchResult> fileSearchResults = fileSearch.FileSearch.Results.Select(r => new AgentFileSearchResult
{
file_id = r.FileId,
file_name = r.FileName,
score = r.Score,
content = r.Content,
}).ToList());
FileId = r.FileId,
FileName = r.FileName,
Score = r.Score,
Content = r.Content,
}).ToList();
yield return CreateResponseMessage(toolCall.Id, fileSearchResults, step);
break;

case RunStepAzureAISearchToolCall aiSearch:
yield return CreateRequestMessage("azure_ai_search", new() { ["input"] = aiSearch.AzureAISearch["input"] });
yield return CreateResponseMessage(new Dictionary<string, object?>
yield return CreateRequestMessage(toolCall.Id, "azure_ai_search", new() { ["input"] = aiSearch.AzureAISearch["input"] }, step);
yield return CreateResponseMessage(toolCall.Id, new Dictionary<string, string?>
{
["output"] = aiSearch.AzureAISearch["output"]
});
}, step);
break;

case RunStepMicrosoftFabricToolCall fabric:
yield return CreateRequestMessage("fabric_dataagent", new() { ["input"] = fabric.MicrosoftFabric["input"] });
yield return CreateResponseMessage(fabric.MicrosoftFabric["output"]);
yield return CreateRequestMessage(toolCall.Id, "fabric_dataagent", new() { ["input"] = fabric.MicrosoftFabric["input"] }, step);
yield return CreateResponseMessage(toolCall.Id, fabric.MicrosoftFabric["output"], step);
break;
}
}
Expand Down Expand Up @@ -1688,4 +1673,54 @@ public string SerializeToolDefinition()
return Encoding.UTF8.GetString(bytes.GetBuffer(), 0, (int)bytes.Length);
}
}

internal static Microsoft.Extensions.AI.ChatMessage CreateRequestMessage(string toolCallId, string name, Dictionary<string, object?> arguments, RunStep step)
{
return new(ChatRole.Assistant, [new FunctionCallContent(toolCallId, name, arguments)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};
}

internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, List<AgentFileSearchResult> result, RunStep step)
{
return new(ChatRole.Tool, [new FunctionResultContent(toolCallId, result)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};
}

internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, JsonElement result, RunStep step)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all these different static methods given FunctionResultContent's constructor is string callId, object? result). Cant we just have:

Suggested change
internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, JsonElement result, RunStep step)
internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, object? result, RunStep step)

{
return new(ChatRole.Tool, [new FunctionResultContent(toolCallId, result)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};
}

internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, string result, RunStep step)
{
return new(ChatRole.Tool, [new FunctionResultContent(toolCallId, result)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};
}

internal static Microsoft.Extensions.AI.ChatMessage CreateResponseMessage(string toolCallId, IDictionary<string, string?> result, RunStep step)
{
return new(ChatRole.Tool, [new FunctionResultContent(toolCallId, result)])
{
AuthorName = step.AssistantId,
MessageId = step.Id,
RawRepresentation = step,
};
}
}