O Microsoft Foundry Agent Service é uma plataforma gerenciada da Microsoft para criação, implantação e escalabilidade de agentes de IA. Um agente combina três componentes essenciais: um modelo do catálogo do Foundry, instruções que definem o comportamento e ferramentas que dão acesso a dados ou ações externas. O Foundry cuida de toda a infraestrutura, incluindo identidade gerenciada, escalabilidade automática e observabilidade, para que o desenvolvedor foque na lógica do agente.
Um ponto de entrada central para interagir com esses agentes é a Responses API, que unifica recursos das APIs de chat completions e Assistants em uma única interface stateful. Ela permite criar respostas, encadear turnos de conversa via previous_response_id ou conversations, e integrar ferramentas como MCP servers, Web Search e Code Interpreter diretamente no ciclo de inferência.
Com o pacote Azure.AI.Projects.OpenAI, é possível consumir essa API em .NET com suporte nativo a autenticação via DefaultAzureCredential.
À medida que o número de agentes em uma organização cresce, cada equipe tende a reimplementar as mesmas ferramentas de forma independente, duplicando credenciais e tornando a governança inconsistente. É aqui que os Toolboxes entram. Um Toolbox é um recurso gerenciado no Foundry que agrupa ferramentas variadas, como MCP servers, Web Search, Azure AI Search e Code Interpreter, em um único endpoint MCP-compatível. Qualquer agente ou runtime MCP pode consumir esse endpoint, independentemente do framework utilizado.
O Toolbox expõe as ferramentas por meio de um endpoint no formato {project_endpoint}/toolboxes/{toolbox_name}/mcp?api-version=v1. O versionamento é explícito: ao criar uma nova versão do toolbox, a versão anterior continua sendo servida até que você promova a nova como padrão. Isso dá controle total sobre quando as mudanças entram em produção, sem necessidade de redeployment nos agentes consumidores.
Neste artigo, construiremos uma .NET Web API que hospeda um agente programaticamente usando o Foundry SDK. O agente será configurado com dois tipos de ferramenta: o Microsoft Learn MCP Server, que dá ao agente acesso à documentação oficial da Microsoft em tempo real, e a Web Search tool, que permite buscas na internet com grounding via Bing . Todo o ciclo de criação do toolbox, configuração da project connection e criação do agente acontecerá automaticamente no primeiro acesso à nossa API.
Para executar o projeto, você precisará de uma assinatura Azure ativa, ter o .NET 10 SDK instalado e possuir um projeto no Microsoft Foundry configurado. Na página principal do projeto no portal, localize o endpoint de conexão da API, que será usado na configuração da aplicação.

Para criar o projeto .NET, execute o seguinte comando no terminal:
dotnet new webapi -n ToolboxesAgent.Api
cd ToolboxesAgent.Api
Com o projeto criado, instale os pacotes NuGet necessários. O Azure.AI.Projects fornece o AIProjectClient e as operações administrativas de criação de agentes e toolboxes. O Azure.AI.Projects.Agents disponibiliza as APIs como AgentToolboxes, DeclarativeAgentDefinition e ProjectsAgentTool. O Azure.AI.Projects.OpenAI expõe a Responses API com suporte a conversações e envio de mensagens via ProjectResponsesClient. O Azure.Identity provê o DefaultAzureCredential, que resolve automaticamente a identidade via Azure CLI em desenvolvimento ou Managed Identity em produção.
dotnet add package Azure.AI.Projects --prerelease
dotnet add package Azure.AI.Projects.Agents --prerelease
dotnet add package Azure.AI.Projects.OpenAI --prerelease
dotnet add package Azure.IdentityO arquivo de projeto final ficará assim:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>AAIP001;OPENAI001</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.Projects" Version="*-*" />
<PackageReference Include="Azure.AI.Projects.Agents" Version="*-*" />
<PackageReference Include="Azure.AI.Projects.OpenAI" Version="*-*" />
<PackageReference Include="Azure.Identity" Version="*" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
</ItemGroup>
</Project>Os warnings AAIP001 e OPENAI001 são suprimidos porque os pacotes ainda estão em preview e emitem avisos por padrão. A versão `*-*` nos três pacotes Azure AI garante que sempre seja utilizada a versão mais recente de preview disponível no NuGet.
Com os pacotes instalados, configure o appsettings.json. Cada propriedade será lida pela classe FoundryOptions e tem um papel específico no ciclo de vida do agente.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Foundry": {
"ProjectEndpoint": "<your-project-endpoint>",
"ProjectResourceId": "<your-project-arm-resource-id>",
"ModelDeploymentName": "<your-model-deployment-name>",
"ToolboxName": "<your-toolbox-name>",
"ToolboxDescription": "<your-toolbox-description>",
"ToolboxMcpServerLabel": "<your-toolbox-mcp-label>",
"ToolboxMcpServerUrl": "<your-toolbox-mcp-url>",
"AgentName": "<your-agent-name>",
"AgentInstructions": "<your-agent-instructions>",
"AgentMcpServerLabel": "<your-agent-mcp-label>",
"ProjectConnectionName": "<your-project-connection-name>",
"ProjectConnectionAudience": "https://ai.azure.com"
}
}
ProjectEndpoint: endpoint do projeto no Foundry, no formatohttps://<account>.services.ai.azure.com/api/projects/<project>.ProjectResourceId: ARM Resource ID completo do projeto, seguindo o padrão/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.CognitiveServices/accounts/<accountName>/projects/<projectName>. É utilizado pela REST API de gerenciamento do Azure para criar a project connection.ModelDeploymentName: nome do deployment do modelo, comogpt-5-mini.ToolboxName: nome do toolbox que será criado programaticamente.ToolboxDescription: descrição do toolbox que será criado programaticamente.ToolboxMcpServerLabel: label do MCP server externo que o toolbox irá expor.ToolboxMcpServerUrl: URL do MCP server externo que o toolbox irá expor. Para o Microsoft Learn MCP, a URL éhttps://learn.microsoft.com/api/mcp.AgentName: nome do agente.AgentInstructions: instruções que definem o comportamento do agente.AgentMcpServerLabel: label MCP pelo qual o agente se conectará ao endpoint do toolbox.ProjectConnectionName: nome da connection que o Foundry criará para autenticar o acesso do agente ao toolbox.ProjectConnectionAudience: escopo de autenticação da connection. Para recursos do Foundry, o valor padrão éhttps://ai.azure.com.
Crie a classe FoundryOptions no diretório Services:
namespace ToolboxesAgent.Api.Services;
public class FoundryOptions
{
public string ProjectEndpoint { get; set; } = string.Empty;
public string ProjectResourceId { get; set; } = string.Empty;
public string ModelDeploymentName { get; set; } = string.Empty;
public string ToolboxName { get; set; } = string.Empty;
public string ToolboxDescription { get; set; } = string.Empty;
public string ToolboxMcpServerLabel { get; set; } = string.Empty;
public string ToolboxMcpServerUrl { get; set; } = string.Empty;
public string AgentName { get; set; } = string.Empty;
public string AgentInstructions { get; set; } = string.Empty;
public string AgentMcpServerLabel { get; set; } = string.Empty;
public string ProjectConnectionName { get; set; } = string.Empty;
public string ProjectConnectionAudience { get; set; } = string.Empty;
}Cada propriedade mapeia diretamente para uma chave da seção Foundry no appsettings.json. O padrão Options do ASP.NET Core fará a vinculação automaticamente via Configure<FoundryOptions> no Program.cs.
Antes de implementar a lógica principal, crie os DTOs no diretório Dtos. Eles definem os contratos de entrada e saída dos endpoints:
namespace ToolboxesAgent.Api.Dtos;
public record CreateConversationResponse(string ConversationId);namespace ToolboxesAgent.Api.Dtos;
public record SendMessageRequest(string ConversationId, string Message);namespace ToolboxesAgent.Api.Dtos;
public record SendMessageResponse(string Response);CreateConversationResponse retorna o ID da conversa criada no Foundry. SendMessageRequest carrega o ID da conversa e a mensagem do usuário. SendMessageResponse encapsula o texto de resposta gerado pelo agente.
Agora chegamos ao núcleo da solução: a classe AgentService. Ela implementa toda a orquestração com o Foundry, desde a criação do toolbox até o envio e recebimento de mensagens.
Crie o arquivo AgentService.cs no diretório Services:
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Core;
using Microsoft.Extensions.Options;
using OpenAI.Responses;
namespace ToolboxesAgent.Api.Services;
public class AgentService(
IOptions<FoundryOptions> options,
TokenCredential credential,
IHttpClientFactory httpClientFactory)
{
private const string ManagementScope = "https://management.azure.com/.default";
private readonly FoundryOptions _options = options.Value;
private readonly SemaphoreSlim _initializationLock = new(1, 1);
private ProjectsAgentVersion? _agentVersion;
public async Task<string> CreateConversationAsync(CancellationToken cancellationToken = default)
{
await EnsureFoundryResourcesAsync(cancellationToken);
AIProjectClient projectClient = CreateProjectClient();
Azure.AI.Extensions.OpenAI.ProjectConversation conversation = await projectClient.ProjectOpenAIClient
.GetProjectConversationsClient()
.CreateProjectConversationAsync(
new Azure.AI.Extensions.OpenAI.ProjectConversationCreationOptions(),
cancellationToken);
return conversation.Id;
}
public async Task<string> SendMessageAsync(
string conversationId,
string userMessage,
CancellationToken cancellationToken = default)
{
await EnsureFoundryResourcesAsync(cancellationToken);
AIProjectClient projectClient = CreateProjectClient();
Azure.AI.Extensions.OpenAI.AgentReference agentReference = new(name: _options.AgentName, version: null);
Azure.AI.Extensions.OpenAI.ProjectResponsesClient responsesClient = projectClient.ProjectOpenAIClient
.GetProjectResponsesClientForAgent(
defaultAgent: agentReference,
defaultConversationId: conversationId);
ResponseResult response = await responsesClient.CreateResponseAsync(
userInputText: userMessage,
previousResponseId: null,
cancellationToken: cancellationToken);
return response.GetOutputText();
}
private async Task EnsureFoundryResourcesAsync(CancellationToken cancellationToken)
{
if (_agentVersion is not null)
return;
await _initializationLock.WaitAsync(cancellationToken);
try
{
if (_agentVersion is not null)
return;
AIProjectClient projectClient = CreateProjectClient();
ToolboxVersion toolboxVersion = await CreateToolboxVersionAsync(projectClient, cancellationToken);
string toolboxMcpEndpoint = BuildToolboxMcpEndpoint();
await CreateProjectConnectionAsync(toolboxMcpEndpoint, cancellationToken);
_agentVersion = CreateAgentVersion(projectClient, toolboxMcpEndpoint);
Console.WriteLine($"Toolbox: {toolboxVersion.Name} v{toolboxVersion.Version}");
Console.WriteLine($"Toolbox MCP endpoint: {toolboxMcpEndpoint}");
Console.WriteLine($"Project connection: {_options.ProjectConnectionName}");
Console.WriteLine($"Agent: {_agentVersion.Name} v{_agentVersion.Version}");
}
finally
{
_initializationLock.Release();
}
}
private AIProjectClient CreateProjectClient() =>
new(
endpoint: new Uri(_options.ProjectEndpoint),
tokenProvider: credential);
private async Task<ToolboxVersion> CreateToolboxVersionAsync(
AIProjectClient projectClient,
CancellationToken cancellationToken)
{
AgentToolboxes toolboxClient =
projectClient.AgentAdministrationClient.GetAgentToolboxes();
McpTool mcpTool = ResponseTool.CreateMcpTool(
serverLabel: _options.ToolboxMcpServerLabel,
serverUri: new Uri(_options.ToolboxMcpServerUrl),
toolCallApprovalPolicy: new McpToolCallApprovalPolicy(
GlobalMcpToolCallApprovalPolicy.NeverRequireApproval));
ProjectsAgentTool mcpProjectTool = ProjectsAgentTool.AsProjectTool(mcpTool);
ProjectsAgentTool webSearchProjectTool = ProjectsAgentTool.AsProjectTool(
ResponseTool.CreateWebSearchTool());
return await toolboxClient.CreateToolboxVersionAsync(
name: _options.ToolboxName,
tools: [mcpProjectTool, webSearchProjectTool],
description: _options.ToolboxDescription,
cancellationToken: cancellationToken);
}
private async Task CreateProjectConnectionAsync(
string toolboxMcpEndpoint,
CancellationToken cancellationToken)
{
AccessToken managementToken = await credential.GetTokenAsync(
new TokenRequestContext([ManagementScope]),
cancellationToken);
string requestUri =
$"https://management.azure.com{_options.ProjectResourceId.TrimEnd('/')}/connections/{_options.ProjectConnectionName}?api-version=2025-10-01-preview";
using HttpClient httpClient = httpClientFactory.CreateClient();
using HttpRequestMessage request = new(HttpMethod.Put, requestUri);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", managementToken.Token);
request.Content = JsonContent.Create(new
{
name = _options.ProjectConnectionName,
type = "Microsoft.MachineLearningServices/workspaces/connections",
properties = new
{
authType = "ProjectManagedIdentity",
category = "RemoteTool",
target = toolboxMcpEndpoint,
isSharedToAll = true,
audience = _options.ProjectConnectionAudience,
metadata = new
{
ApiType = "Azure"
}
}
});
using HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
string body = await response.Content.ReadAsStringAsync(cancellationToken);
throw new InvalidOperationException(
$"Failed to create Foundry project connection. Status: {(int)response.StatusCode} {response.ReasonPhrase}. Body: {body}");
}
}
private ProjectsAgentVersion CreateAgentVersion(
AIProjectClient projectClient,
string toolboxMcpEndpoint)
{
McpTool toolboxMcpTool = ResponseTool.CreateMcpTool(
serverLabel: _options.AgentMcpServerLabel,
serverUri: new Uri(toolboxMcpEndpoint),
toolCallApprovalPolicy: new McpToolCallApprovalPolicy(
GlobalMcpToolCallApprovalPolicy.NeverRequireApproval));
toolboxMcpTool.ProjectConnectionId = _options.ProjectConnectionName;
DeclarativeAgentDefinition agentDefinition = new(_options.ModelDeploymentName)
{
Instructions = _options.AgentInstructions,
Tools =
{
toolboxMcpTool
}
};
return projectClient.AgentAdministrationClient.CreateAgentVersion(
agentName: _options.AgentName,
options: new(agentDefinition));
}
private string BuildToolboxMcpEndpoint() =>
$"{_options.ProjectEndpoint.TrimEnd('/')}/toolboxes/{_options.ToolboxName}/mcp?api-version=v1";
}
O AgentService recebe três dependências via Primary Constructor. IOptions<FoundryOptions> carrega as configurações do appsettings.json. TokenCredential é o DefaultAzureCredential registrado como singleton, utilizado tanto para autenticar com o Foundry SDK quanto para obter tokens da Azure Management API. IHttpClientFactory é usado para chamar a REST API de gerenciamento do Azure ao criar a project connection. O campo _agentVersion guarda a referência do agente após a inicialização, evitando que ele seja recriado a cada requisição.
CreateConversationAsync() Garante que o agente e o toolbox existam chamando EnsureFoundryResourcesAsync, cria um AIProjectClient e usa o cliente de conversations para criar uma nova conversa no Foundry, retornando seu ID.
SendMessageAsync() cria um AgentReference com o nome do agente configurado e obtém um ProjectResponsesClient associado ao agente e à conversa. A chamada CreateResponseAsync envia a mensagem ao agente, que usa as ferramentas do toolbox para elaborar a resposta, retornando o texto final via GetOutputText().
O método EnsureFoundryResourcesAsync é o ponto central da inicialização lazy. Ele usa double-checked locking com SemaphoreSlim para garantir que a criação dos recursos aconteça exatamente uma vez, mesmo sob carga paralela.
EnsureFoundryResourcesAsync() Implementa o padrão double-checked locking verificando _agentVersion antes e depois de adquirir o semáforo. Coordena toda a sequência de inicialização: criação do toolbox, construção do endpoint MCP, criação da project connection e, por fim, registro do agente.
CreateProjectClient() instancia um AIProjectClient com o endpoint do projeto e o TokenCredential injetado.
CreateToolboxVersionAsync() Obtém o cliente AgentToolboxes a partir do AgentAdministrationClient. Cria um McpTool com NeverRequireApproval apontando para o Microsoft Learn MCP Server e um WebSearchTool. Ambos são convertidos para ProjectsAgentTool via AsProjectTool antes de serem passados ao CreateToolboxVersionAsync, que cria ou atualiza a versão do toolbox no Foundry.
Após a criação do toolbox, o endpoint MCP consumer pode ser construído localmente:
private string BuildToolboxMcpEndpoint() =>
$"{_options.ProjectEndpoint.TrimEnd('/')}/toolboxes/{_options.ToolboxName}/mcp?api-version=v1";BuildToolboxMcpEndpoint() compõe a URL do consumer endpoint do toolbox, que sempre aponta para a default_version. Esse é o endereço que o agente usará para invocar as ferramentas em tempo de execução.
O passo seguinte é criar a project connection, que autentica o acesso do agente ao endpoint MCP do toolbox. Essa operação é feita via REST API de gerenciamento do Azure, pois o SDK ainda não expõe essa funcionalidade diretamente.
CreateProjectConnectionAsync() obtém um token de acesso com o escopo https://management.azure.com/.default e faz um HTTP PUT para a REST API de gerenciamento do Azure, criando uma connection do tipo RemoteTool com autenticação via ProjectManagedIdentity. O campo target aponta para o endpoint MCP do toolbox e isSharedToAll garante que a connection seja acessível a todos os agentes do projeto.
CreateAgentVersion() cria um McpTool cujo serverUri é o endpoint MCP do toolbox e define ProjectConnectionId com o nome da connection criada anteriormente, essa vinculação é o que permite ao agente se autenticar ao invocar as ferramentas em runtime. O DeclarativeAgentDefinition combina o modelo, as instruções e a ferramenta MCP para definir o comportamento do agente.
O método CreateAgentVersion registra o agente no Foundry e retorna um ProjectsAgentVersion com nome e versão.
Agora crie os endpoints no diretório Endpoints. O primeiro gerencia a criação de conversas:
using ToolboxesAgent.Api.Dtos;
using ToolboxesAgent.Api.Services;
namespace ToolboxesAgent.Api.Endpoints;
public static partial class ConversationEndpoints
{
public static void MapConversationEndpoints(this WebApplication app)
{
app.MapPost("/conversations", CreateConversationAsync);
}
private static async Task<IResult> CreateConversationAsync(AgentService service)
{
var conversationId = await service.CreateConversationAsync();
return Results.Ok(new CreateConversationResponse(conversationId));
}
}MapConversationEndpoints() registra o endpoint `POST /conversations` como extension method em WebApplication.
CreateConversationAsync() delega a criação da conversa ao AgentService e retorna um CreateConversationResponse com o ID gerado pelo Foundry.
O segundo endpoint gerencia o envio de mensagens:
using ToolboxesAgent.Api.Dtos;
using ToolboxesAgent.Api.Services;
namespace ToolboxesAgent.Api.Endpoints;
public static partial class MessageEndpoints
{
public static void MapMessageEndpoints(this WebApplication app)
{
app.MapPost("/messages", SendMessageAsync);
}
private static async Task<IResult> SendMessageAsync(
SendMessageRequest request,
AgentService service)
{
var response = await service.SendMessageAsync(request.ConversationId, request.Message);
return Results.Ok(new SendMessageResponse(response));
}
}MapMessageEndpoints() registra o endpoint POST /messages como extension method em WebApplication.
SendMessageAsync() recebe o SendMessageRequest com o ID da conversa e a mensagem do usuário, delega ao AgentService e retorna um SendMessageResponse com o texto gerado pelo agente.
Com os endpoints definidos, configure o Program.cs para registrar todos os serviços e mapear as rotas:
using Azure.Core;
using Azure.Identity;
using ToolboxesAgent.Api.Endpoints;
using ToolboxesAgent.Api.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddHttpClient();
builder.Services.Configure<FoundryOptions>(
builder.Configuration.GetSection("Foundry"));
builder.Services.AddSingleton<TokenCredential>(new DefaultAzureCredential());
builder.Services.AddSingleton<AgentService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.MapConversationEndpoints();
app.MapMessageEndpoints();
app.Run();AddOpenApi() adiciona suporte ao OpenAPI para documentação dos endpoints. AddHttpClient() registra o IHttpClientFactory utilizado pelo AgentService para chamar a REST API do Azure. Configure<FoundryOptions> vincula a seção Foundry do appsettings.json ao tipo FoundryOptions. O DefaultAzureCredential é registrado como singleton do tipo TokenCredential, ele tenta resolver a identidade automaticamente via Azure CLI em desenvolvimento e via Managed Identity em produção.
O AgentService também é singleton para que a inicialização lazy ocorra exatamente uma vez durante o ciclo de vida da aplicação.
Para testar a API, crie o arquivo ToolboxesAgent.Api.http na raiz do projeto:
@host = http://localhost:5011
### Create Conversation
POST {{host}}/conversations
Content-Type: application/json
### Send Message
POST {{host}}/messages
Content-Type: application/json
{
"conversationId": "<conversation-id>",
"message": "<user-message>"
}Antes de executar a API, autentique-se no Azure via CLI. O DefaultAzureCredential usará essa sessão automaticamente em ambiente de desenvolvimento:
az login
Com a autenticação configurada, inicie a aplicação:
dotnet runAgora execute o primeiro request chamando POST /conversations. O response terá o seguinte formato:
{
"conversationId": "conv_339afda4b162a68a00eeXUo4lXo8yPgHSTpduWTmq3ZXUcZ4jh"
}Obs: o id retornado será diferente a cada execução, pois é gerado dinamicamente pelo Foundry.
Copie o conversationId retornado e substitua o valor no request de envio de mensagem.
Note que na primeira chamada, o AgentService irá criar o toolbox, a project connection e o agente no Foundry antes de criar a conversa. Nas chamadas subsequentes, esses recursos já existirão e a inicialização será ignorada.



Para o primeiro teste, envie a mensagem: "Explain how Azure Functions TimerTrigger works and provide a short C# example. It is required to consume this info using the MCP. If no answer is received, respond 'I could not find the answer to your question.'"
O agente irá consultar o Microsoft Learn MCP Server, buscar a documentação oficial sobre Azure Functions TimerTrigger e retornar uma resposta fundamentada com exemplo de código.

Para o segundo teste, envie a mensagem: "Explain me https://devblogs.microsoft.com/foundry/introducing-toolboxes-in-foundry. Use the MCP. If no answer is received, respond 'I could not find the answer to your question.'"
Nesse caso, o agente usará as ferramentas disponíveis (web search) no toolbox para buscar e sintetizar as informações do blog post sobre Toolboxes no Foundry.

Ao longo deste artigo, construímos uma .NET Web API que orquestra agentes com Microsoft Foundry Toolboxes.
A arquitetura inicializa programaticamente o toolbox com o Microsoft Learn MCP Server e o Web Search, registra a project connection para autenticação via Managed Identity, cria o agente por meio da Responses API e expõe dois endpoints simples para gerenciar conversas e mensagens.
Você já pode baixar o projeto por esse link, e não esquece de me seguir no LinkedIn!
Até a próxima, abraços!