diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
index f31c570508..0d3639b614 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
@@ -64,13 +64,27 @@ internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentRecord a
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentVersion agentVersion, ChatOptions? chatOptions)
: this(
aiProjectClient,
- new AgentReference(Throw.IfNull(agentVersion).Name, agentVersion.Version),
+ CreateAgentReference(Throw.IfNull(agentVersion)),
(agentVersion.Definition as PromptAgentDefinition)?.Model,
chatOptions)
{
this._agentVersion = agentVersion;
}
+ ///
+ /// Creates an from an .
+ /// Uses the agent version's version if available, otherwise defaults to "latest".
+ ///
+ /// The agent version to create a reference from.
+ /// An for the specified agent version.
+ private static AgentReference CreateAgentReference(AgentVersion agentVersion)
+ {
+ // If the version is null, empty, or whitespace, use "latest" as the default.
+ // This handles cases where hosted agents (like MCP agents) may not have a version assigned.
+ var version = string.IsNullOrWhiteSpace(agentVersion.Version) ? "latest" : agentVersion.Version;
+ return new AgentReference(agentVersion.Name, version);
+ }
+
///
public override object? GetService(Type serviceType, object? serviceKey = null)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
index 37ee7fa82c..18a5ba3b72 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
@@ -543,9 +543,16 @@ private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion
}
}
+ // Use the agent version's ID if available, otherwise generate one from name and version.
+ // This handles cases where hosted agents (like MCP agents) may not have an ID assigned.
+ var version = string.IsNullOrWhiteSpace(agentVersion.Version) ? "latest" : agentVersion.Version;
+ var agentId = string.IsNullOrWhiteSpace(agentVersion.Id)
+ ? $"{agentVersion.Name}:{version}"
+ : agentVersion.Id;
+
var agentOptions = new ChatClientAgentOptions()
{
- Id = agentVersion.Id,
+ Id = agentId,
Name = agentVersion.Name,
Description = agentVersion.Description,
};
diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
index da65d53c30..447c195c83 100644
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
@@ -2384,6 +2384,134 @@ public async Task GetAIAgentAsync_WithUseProvidedChatClientAsIs_PreservesSetting
#endregion
+ #region Empty Version and ID Handling Tests
+
+ ///
+ /// Verify that GetAIAgentAsync handles an agent with empty version by using "latest" as fallback.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithEmptyVersion_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ // Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ ///
+ /// Verify that AsAIAgent with AgentRecord handles empty version by using "latest" as fallback.
+ ///
+ [Fact]
+ public void AsAIAgent_WithAgentRecordEmptyVersion_CreatesAgentWithGeneratedId()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
+ AgentRecord agentRecord = this.CreateTestAgentRecordWithEmptyVersion();
+
+ // Act
+ var agent = client.AsAIAgent(agentRecord);
+
+ // Assert
+ Assert.NotNull(agent);
+ // Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ ///
+ /// Verify that AsAIAgent with AgentVersion handles empty version by using "latest" as fallback.
+ ///
+ [Fact]
+ public void AsAIAgent_WithAgentVersionEmptyVersion_CreatesAgentWithGeneratedId()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
+ AgentVersion agentVersion = this.CreateTestAgentVersionWithEmptyVersion();
+
+ // Act
+ var agent = client.AsAIAgent(agentVersion);
+
+ // Assert
+ Assert.NotNull(agent);
+ // Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ ///
+ /// Verify that GetAIAgentAsync handles an agent with whitespace-only version by using "latest" as fallback.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithWhitespaceVersion_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ // Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ ///
+ /// Verify that AsAIAgent with AgentRecord handles whitespace-only version by using "latest" as fallback.
+ ///
+ [Fact]
+ public void AsAIAgent_WithAgentRecordWhitespaceVersion_CreatesAgentWithGeneratedId()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
+ AgentRecord agentRecord = this.CreateTestAgentRecordWithWhitespaceVersion();
+
+ // Act
+ var agent = client.AsAIAgent(agentRecord);
+
+ // Assert
+ Assert.NotNull(agent);
+ // Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ ///
+ /// Verify that AsAIAgent with AgentVersion handles whitespace-only version by using "latest" as fallback.
+ ///
+ [Fact]
+ public void AsAIAgent_WithAgentVersionWhitespaceVersion_CreatesAgentWithGeneratedId()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
+ AgentVersion agentVersion = this.CreateTestAgentVersionWithWhitespaceVersion();
+
+ // Act
+ var agent = client.AsAIAgent(agentVersion);
+
+ // Assert
+ Assert.NotNull(agent);
+ // Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
+ Assert.Equal("agent_abc123:latest", agent.Id);
+ }
+
+ #endregion
+
#region ApplyToolsToAgentDefinition Tests
///
@@ -2678,6 +2806,54 @@ private AgentRecord CreateTestAgentRecord(AgentDefinition? agentDefinition = nul
return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!;
}
+ ///
+ /// Creates a test AIProjectClient with empty version fields for testing hosted MCP agents.
+ ///
+ private FakeAgentClient CreateTestAgentClientWithEmptyVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
+ {
+ return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, useEmptyVersion: true);
+ }
+
+ ///
+ /// Creates a test AgentRecord with empty version for testing hosted MCP agents.
+ ///
+ private AgentRecord CreateTestAgentRecordWithEmptyVersion(AgentDefinition? agentDefinition = null)
+ {
+ return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithEmptyVersion(agentDefinition: agentDefinition)))!;
+ }
+
+ ///
+ /// Creates a test AgentVersion with empty version for testing hosted MCP agents.
+ ///
+ private AgentVersion CreateTestAgentVersionWithEmptyVersion()
+ {
+ return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion()))!;
+ }
+
+ ///
+ /// Creates a test AIProjectClient with whitespace-only version fields for testing hosted MCP agents.
+ ///
+ private FakeAgentClient CreateTestAgentClientWithWhitespaceVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
+ {
+ return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, versionMode: VersionMode.Whitespace);
+ }
+
+ ///
+ /// Creates a test AgentRecord with whitespace-only version for testing hosted MCP agents.
+ ///
+ private AgentRecord CreateTestAgentRecordWithWhitespaceVersion(AgentDefinition? agentDefinition = null)
+ {
+ return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(agentDefinition: agentDefinition)))!;
+ }
+
+ ///
+ /// Creates a test AgentVersion with whitespace-only version for testing hosted MCP agents.
+ ///
+ private AgentVersion CreateTestAgentVersionWithWhitespaceVersion()
+ {
+ return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion()))!;
+ }
+
private const string OpenAPISpec = """
{
"openapi": "3.0.3",
@@ -2716,14 +2892,26 @@ private AgentVersion CreateTestAgentVersion()
return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!;
}
+ ///
+ /// Specifies the version mode for test data generation.
+ ///
+ private enum VersionMode
+ {
+ Normal,
+ Empty,
+ Whitespace
+ }
+
///
/// Fake AIProjectClient for testing.
///
private sealed class FakeAgentClient : AIProjectClient
{
- public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
+ public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, bool useEmptyVersion = false, VersionMode versionMode = VersionMode.Normal)
{
- this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse);
+ // Handle backward compatibility with bool parameter
+ var effectiveVersionMode = useEmptyVersion ? VersionMode.Empty : versionMode;
+ this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode);
}
public override ClientConnection GetConnection(string connectionId)
@@ -2739,60 +2927,82 @@ private sealed class FakeAIProjectAgentsOperations : AIProjectAgentsOperations
private readonly string? _instructions;
private readonly string? _description;
private readonly AgentDefinition? _agentDefinition;
+ private readonly VersionMode _versionMode;
- public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
+ public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal)
{
this._agentName = agentName;
this._instructions = instructions;
this._description = description;
this._agentDefinition = agentDefinitionResponse;
+ this._versionMode = versionMode;
+ }
+
+ private string GetAgentResponseJson()
+ {
+ return this._versionMode switch
+ {
+ VersionMode.Empty => TestDataUtil.GetAgentResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
+ VersionMode.Whitespace => TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
+ _ => TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
+ };
+ }
+
+ private string GetAgentVersionResponseJson()
+ {
+ return this._versionMode switch
+ {
+ VersionMode.Empty => TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
+ VersionMode.Whitespace => TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
+ _ => TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
+ };
}
public override ClientResult GetAgent(string agentName, RequestOptions options)
{
- var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
}
public override ClientResult GetAgent(string agentName, CancellationToken cancellationToken = default)
{
- var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task GetAgentAsync(string agentName, RequestOptions options)
{
- var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
}
public override Task> GetAgentAsync(string agentName, CancellationToken cancellationToken = default)
{
- var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
public override ClientResult CreateAgentVersion(string agentName, BinaryContent content, RequestOptions? options = null)
{
- var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentVersionResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
}
public override ClientResult CreateAgentVersion(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default)
{
- var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentVersionResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task CreateAgentVersionAsync(string agentName, BinaryContent content, RequestOptions? options = null)
{
- var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentVersionResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
}
public override Task> CreateAgentVersionAsync(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default)
{
- var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
+ var responseJson = this.GetAgentVersionResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs
index c65d10de43..8471ddbcf1 100644
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs
@@ -52,6 +52,70 @@ public static string GetAgentVersionResponseJson(string? agentName = null, Agent
return json;
}
+ ///
+ /// Gets the agent version response JSON with empty version and ID fields for testing hosted agents like MCP agents.
+ ///
+ public static string GetAgentVersionResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
+ {
+ var json = s_agentVersionResponseJson;
+ json = ApplyAgentName(json, agentName);
+ json = ApplyAgentDefinition(json, agentDefinition);
+ json = ApplyInstructions(json, instructions);
+ json = ApplyDescription(json, description);
+ // Remove the version and id fields to simulate hosted agents without version
+ json = json.Replace("\"version\": \"1\",", "\"version\": \"\",");
+ json = json.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \"\",");
+ return json;
+ }
+
+ ///
+ /// Gets the agent response JSON with empty version and ID fields in the latest version for testing hosted agents like MCP agents.
+ ///
+ public static string GetAgentResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
+ {
+ var json = s_agentResponseJson;
+ json = ApplyAgentName(json, agentName);
+ json = ApplyAgentDefinition(json, agentDefinition);
+ json = ApplyInstructions(json, instructions);
+ json = ApplyDescription(json, description);
+ // Remove the version and id fields to simulate hosted agents without version
+ json = json.Replace("\"version\": \"1\",", "\"version\": \"\",");
+ json = json.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \"\",");
+ return json;
+ }
+
+ ///
+ /// Gets the agent version response JSON with whitespace-only version and ID fields for testing hosted agents like MCP agents.
+ ///
+ public static string GetAgentVersionResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
+ {
+ var json = s_agentVersionResponseJson;
+ json = ApplyAgentName(json, agentName);
+ json = ApplyAgentDefinition(json, agentDefinition);
+ json = ApplyInstructions(json, instructions);
+ json = ApplyDescription(json, description);
+ // Use whitespace-only version and id fields to simulate hosted agents without version
+ return json
+ .Replace("\"version\": \"1\",", "\"version\": \" \",")
+ .Replace("\"id\": \"agent_abc123:1\",", "\"id\": \" \",");
+ }
+
+ ///
+ /// Gets the agent response JSON with whitespace-only version and ID fields in the latest version for testing hosted agents like MCP agents.
+ ///
+ public static string GetAgentResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
+ {
+ var json = s_agentResponseJson;
+ json = ApplyAgentName(json, agentName);
+ json = ApplyAgentDefinition(json, agentDefinition);
+ json = ApplyInstructions(json, instructions);
+ json = ApplyDescription(json, description);
+ // Use whitespace-only version and id fields to simulate hosted agents without version
+ return json
+ .Replace("\"version\": \"1\",", "\"version\": \" \",")
+ .Replace("\"id\": \"agent_abc123:1\",", "\"id\": \" \",");
+ }
+
///
/// Gets the OpenAI default response JSON with optional placeholder replacements applied.
///