diff --git a/tools/Azure.Mcp.Tools.Foundry/src/Commands/ThreadCreateCommand.cs b/tools/Azure.Mcp.Tools.Foundry/src/Commands/ThreadCreateCommand.cs index 312a9eaca..aa43c694e 100644 --- a/tools/Azure.Mcp.Tools.Foundry/src/Commands/ThreadCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Foundry/src/Commands/ThreadCreateCommand.cs @@ -40,8 +40,8 @@ protected override void RegisterOptions(Command command) protected override ThreadCreateOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - options.Endpoint = parseResult.GetValueOrDefault(FoundryOptionDefinitions.EndpointOption); - options.UserMessage = parseResult.GetValueOrDefault(FoundryOptionDefinitions.UserMessage); + options.Endpoint = parseResult.GetValueOrDefault(FoundryOptionDefinitions.EndpointOption); + options.UserMessage = parseResult.GetValueOrDefault(FoundryOptionDefinitions.UserMessageOption); return options; } diff --git a/tools/Azure.Mcp.Tools.Foundry/src/Services/FoundryService.cs b/tools/Azure.Mcp.Tools.Foundry/src/Services/FoundryService.cs index 97fa18c26..98e4b35b0 100644 --- a/tools/Azure.Mcp.Tools.Foundry/src/Services/FoundryService.cs +++ b/tools/Azure.Mcp.Tools.Foundry/src/Services/FoundryService.cs @@ -1141,7 +1141,8 @@ public async Task CreateThread( var thread = await CreateThreadCore(projectEndpoint, userMessage, credential, cancellationToken: cancellationToken); return new ThreadCreateResult() { - ThreadId = thread.Id + ThreadId = thread.Id, + ProjectEndpoint = projectEndpoint }; } catch (Exception ex) diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.LiveTests/FoundryCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.LiveTests/FoundryCommandTests.cs index 3d8d69fa1..b46378d2d 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.LiveTests/FoundryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.LiveTests/FoundryCommandTests.cs @@ -425,6 +425,37 @@ public async Task Should_list_openai_models() Assert.Equal(resourceName, commandResourceName.GetString()); } + [Fact] + [Trait("Category", "Live")] + public async Task Should_create_agent() + { + var projectName = $"{Settings.ResourceBaseName}-ai-projects"; + var accounts = Settings.ResourceBaseName; + var agentName = $"test-agent-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; + var endpoint = $"https://{accounts}.services.ai.azure.com/api/projects/{projectName}"; + // Model deployment name hardcoded in the test-resources.bicep + var modelDeploymentName = "gpt-4o"; + var systemInstruction = "Help user with your knowledge"; + var result = await CallToolAsync( + "foundry_agents_create", + new() + { + { "endpoint", endpoint }, + { "model-deployment", modelDeploymentName }, + { "agent-name", agentName }, + { "system-instruction", systemInstruction } + }); + + var agentIdResult = result.AssertProperty("agentId"); + var agentNameResult = result.AssertProperty("agentName"); + var projectEndpointResult = result.AssertProperty("projectEndpoint"); + var modelDeploymentNameResult = result.AssertProperty("modelDeploymentName"); + Assert.Equal(JsonValueKind.String, agentIdResult.ValueKind); + Assert.Equal(JsonValueKind.String, agentNameResult.ValueKind); + Assert.Equal(JsonValueKind.String, projectEndpointResult.ValueKind); + Assert.Equal(JsonValueKind.String, modelDeploymentNameResult.ValueKind); + } + [Fact] [Trait("Category", "Live")] public async Task Should_connect_agent() @@ -1008,6 +1039,65 @@ public async Task Should_get_foundry_resource_using_static_resources() } } + [Fact] + [Trait("Category", "Live")] + public async Task Should_create_thread() + { + var projectName = $"{Settings.ResourceBaseName}-ai-projects"; + var accounts = Settings.ResourceBaseName; + var endpoint = $"https://{accounts}.services.ai.azure.com/api/projects/{projectName}"; + var userMessage = "Message from user"; + var result = await CallToolAsync( + "foundry_threads_create", + new() + { + { "endpoint", endpoint }, + { "user-message", userMessage } + }); + var threadIdResult = result.AssertProperty("threadId"); + var projectEndpointResult = result.AssertProperty("projectEndpoint"); + Assert.Equal(JsonValueKind.String, threadIdResult.ValueKind); + Assert.Equal(JsonValueKind.String, projectEndpointResult.ValueKind); + } + + [Fact] + [Trait("Category", "Live")] + public async Task Should_list_threads() + { + var projectName = $"{Settings.ResourceBaseName}-ai-projects"; + var accounts = Settings.ResourceBaseName; + var endpoint = $"https://{accounts}.services.ai.azure.com/api/projects/{projectName}"; + var result = await CallToolAsync( + "foundry_threads_list", + new() + { + { "endpoint", endpoint } + }); + var threads = result.AssertProperty("threads"); + Assert.Equal(JsonValueKind.Array, threads.ValueKind); + } + + [Fact] + [Trait("Category", "Live")] + public async Task Should_get_messages() + { + var projectName = $"{Settings.ResourceBaseName}-ai-projects"; + var accounts = Settings.ResourceBaseName; + var endpoint = $"https://{accounts}.services.ai.azure.com/api/projects/{projectName}"; + var threadId = await CreateThread("Hello from user", endpoint); + var result = await CallToolAsync( + "foundry_threads_get-messages", + new() + { + { "endpoint", endpoint }, + { "thread-id", threadId } + }); + var threadIdResult = result.AssertProperty("threadId"); + var messagesResult = result.AssertProperty("messages"); + Assert.Equal(JsonValueKind.String, threadIdResult.ValueKind); + Assert.Equal(JsonValueKind.Array, messagesResult.ValueKind); + } + private async Task CreateAgent(string agentName, string projectEndpoint, string deploymentName) { var tokenProvider = new SingleIdentityTokenCredentialProvider(NullLoggerFactory.Instance); @@ -1029,4 +1119,17 @@ [new BingGroundingSearchConfiguration(bingConnectionId)] tools: [new BingGroundingToolDefinition(bingGroundingToolParameters)]); return agent.Id; } + + private async Task CreateThread(string userMessage, string projectEndpoint) + { + var tokenProvider = new SingleIdentityTokenCredentialProvider(NullLoggerFactory.Instance); + + var client = new PersistentAgentsClient( + projectEndpoint, + await tokenProvider.GetTokenCredentialAsync(default, default)); + + PersistentAgentThread thread = await client.Threads.CreateThreadAsync([ + new(MessageRole.User, userMessage)]); + return thread.Id; + } } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/AgentsGetSdkCodeSampleCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/AgentsGetSdkCodeSampleCommandTests.cs index 5ddd19810..18cef73a0 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/AgentsGetSdkCodeSampleCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/AgentsGetSdkCodeSampleCommandTests.cs @@ -3,8 +3,10 @@ using System.Net; using Azure.Mcp.Core.Models.Command; +using Azure.Mcp.Core.Services.Azure.Subscription; +using Azure.Mcp.Core.Services.Azure.Tenant; +using Azure.Mcp.Core.Services.Http; using Azure.Mcp.Tools.Foundry.Commands; -using Azure.Mcp.Tools.Foundry.Models; using Azure.Mcp.Tools.Foundry.Options; using Azure.Mcp.Tools.Foundry.Services; using Microsoft.Extensions.DependencyInjection; @@ -16,14 +18,17 @@ namespace Azure.Mcp.Tools.Foundry.UnitTests; public class AgentsGetSdkCodeSampleCommandTests { private readonly IServiceProvider _serviceProvider; - private readonly IFoundryService _foundryService; public AgentsGetSdkCodeSampleCommandTests() { - _foundryService = Substitute.For(); - var collection = new ServiceCollection(); - collection.AddSingleton(_foundryService); + var httpClientService = Substitute.For(); + var subscriptionService = Substitute.For(); + var tenantService = Substitute.For(); + collection.AddSingleton(httpClientService); + collection.AddSingleton(subscriptionService); + collection.AddSingleton(tenantService); + collection.AddSingleton(); _serviceProvider = collection.BuildServiceProvider(); } @@ -49,16 +54,6 @@ public async Task ExecuteAsync_Fails_WhenMissingRequiredParameter(string argsStr public async Task ExecuteAsync_ReturnsSdkCodeSample(string programmingLanguage) { - var expectedResult = new AgentsGetSdkCodeSampleResult() - { - CodeSampleText = "code sample text" - }; - - _foundryService.GetSdkCodeSample( - Arg.Is(programmingLanguage) - ) - .Returns(expectedResult); - var command = new AgentsGetSdkSampleCommand(); var args = command.GetCommand().Parse(["--programming-language", programmingLanguage]); var context = new CommandContext(_serviceProvider);