From 4a7f04157a0aca45e9b2972a57fc692dc233eed8 Mon Sep 17 00:00:00 2001 From: Ben Thomas Date: Tue, 21 Apr 2026 09:34:45 -0700 Subject: [PATCH] Hosted agent adapter (#5408) * Bump preview version to 260420.1 and fix AgentServer package deps - Bump PackageVersion to 0.0.1-preview.260420.1 - Bump Azure.AI.AgentServer.Core beta.21 -> beta.22 (required by Azure.AI.AgentServer.Responses beta.3) - Replace AgentHostTelemetry.ResponsesSourceName with local constant (type made internal in AgentServer.Core beta.22) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CA1873: guard LogError with IsEnabled check in FoundryToolboxService Wrap the LogError call with an IsEnabled(LogLevel.Error) guard to satisfy the CA1873 analyzer rule which flags potentially expensive argument evaluation when logging is disabled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bumping NuGet version * Restore conditional versioning, remove dev feed, bump Azure.AI.Projects to beta.1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR #5312 review comments - Add comment explaining NU1903 suppression (Microsoft.Bcl.Memory transitive vuln) - Remove NU1903 from sample/test projects where not needed - Fix Dockerfile ENTRYPOINT mismatch in Hosted-Workflow-Simple - Align agent name to 'hosted-workflow-simple' in agent.yaml and README - Fix Hosted-McpTools README: replace GitHub PAT refs with Microsoft Learn - Fix session persistence: only persist when client provides conversation ID - Upgrade IsNullOrEmpty to IsNullOrWhiteSpace for session ID checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: alliscode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SimpleInvocationsAgent/SimpleInvocationsAgent.csproj | 2 +- .../responses/Hosted-McpTools/README.md | 9 +++------ .../Hosted-Workflow-Handoff/HostedWorkflowHandoff.csproj | 2 +- .../responses/Hosted-Workflow-Simple/Dockerfile | 2 +- .../responses/Hosted-Workflow-Simple/README.md | 2 +- .../responses/Hosted-Workflow-Simple/agent.yaml | 2 +- .../Using-Samples/SimpleAgent/SimpleAgent.csproj | 2 +- .../Hosting/AgentFrameworkResponseHandler.cs | 8 ++++---- .../Microsoft.Agents.AI.Foundry.csproj | 2 +- .../Microsoft.Agents.AI.Foundry.UnitTests.csproj | 2 +- 10 files changed, 15 insertions(+), 18 deletions(-) diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/invocations/Using-Samples/SimpleInvocationsAgent/SimpleInvocationsAgent.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/invocations/Using-Samples/SimpleInvocationsAgent/SimpleInvocationsAgent.csproj index d509bd2c70..126bfef63c 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/invocations/Using-Samples/SimpleInvocationsAgent/SimpleInvocationsAgent.csproj +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/invocations/Using-Samples/SimpleInvocationsAgent/SimpleInvocationsAgent.csproj @@ -8,7 +8,7 @@ false SimpleInvocationsAgentClient simple-invocations-agent-client - $(NoWarn);NU1903;NU1605 + $(NoWarn);NU1605 diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-McpTools/README.md b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-McpTools/README.md index 3313633b0f..3773d9760d 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-McpTools/README.md +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-McpTools/README.md @@ -2,7 +2,7 @@ A hosted agent demonstrating **two layers of MCP (Model Context Protocol) tool integration**: -1. **Client-side MCP (GitHub)** — The agent connects directly to the GitHub MCP server via `McpClient`, discovers tools, and handles tool invocations locally within the agent process. +1. **Client-side MCP (Microsoft Learn)** — The agent connects directly to the Microsoft Learn MCP server via `McpClient`, discovers tools, and handles tool invocations locally within the agent process. 2. **Server-side MCP (Microsoft Learn)** — The agent declares a `HostedMcpServerTool` which delegates tool discovery and invocation to the LLM provider (Azure OpenAI Responses API). The provider calls the MCP server on behalf of the agent with no local connection needed. @@ -12,16 +12,15 @@ A hosted agent demonstrating **two layers of MCP (Model Context Protocol) tool i |---|---|---| | **Connection** | Agent connects to MCP server directly | LLM provider connects to MCP server | | **Tool invocation** | Handled by the agent process | Handled by the Responses API | -| **Auth** | Agent manages credentials (e.g., GitHub PAT) | Provider manages credentials | +| **Auth** | Agent manages credentials | Provider manages credentials | | **Use case** | Custom/private MCP servers, fine-grained control | Public MCP servers, simpler setup | -| **Example** | GitHub (`McpClient` + `HttpClientTransport`) | Microsoft Learn (`HostedMcpServerTool`) | +| **Example** | Microsoft Learn (`McpClient` + `HttpClientTransport`) | Microsoft Learn (`HostedMcpServerTool`) | ## Prerequisites - [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) - An Azure AI Foundry project with a deployed model (e.g., `gpt-4o`) - Azure CLI logged in (`az login`) -- A **GitHub Personal Access Token** (create at https://github.com/settings/tokens) ## Configuration @@ -36,7 +35,6 @@ Edit `.env`: ```env AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/ AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o -GITHUB_PAT=ghp_your_token_here ``` ## Running directly (contributors) @@ -75,7 +73,6 @@ export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.az docker run --rm -p 8088:8088 \ -e AGENT_NAME=mcp-tools \ - -e GITHUB_PAT=$GITHUB_PAT \ -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \ --env-file .env \ hosted-mcp-tools diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Handoff/HostedWorkflowHandoff.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Handoff/HostedWorkflowHandoff.csproj index 3c0df909c9..625aac0329 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Handoff/HostedWorkflowHandoff.csproj +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Handoff/HostedWorkflowHandoff.csproj @@ -8,7 +8,7 @@ HostedWorkflowHandoff HostedWorkflowHandoff false - $(NoWarn);NU1903;NU1605;MAAIW001 + $(NoWarn);NU1605;MAAIW001 diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Dockerfile b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Dockerfile index e770ec172b..5d6888e222 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Dockerfile +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Dockerfile @@ -14,4 +14,4 @@ WORKDIR /app COPY --from=build /app/publish . EXPOSE 8088 ENV ASPNETCORE_URLS=http://+:8088 -ENTRYPOINT ["dotnet", "HostedWorkflows.dll"] +ENTRYPOINT ["dotnet", "HostedWorkflowSimple.dll"] diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/README.md b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/README.md index 0cd438f5f1..d91d27445d 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/README.md +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/README.md @@ -31,7 +31,7 @@ AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o ```bash cd dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple -AGENT_NAME=hosted-workflows dotnet run +AGENT_NAME=hosted-workflow-simple dotnet run ``` The agent will start on `http://localhost:8088`. diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/agent.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/agent.yaml index ab138939b4..c9c0386cf1 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/agent.yaml +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/agent.yaml @@ -1,6 +1,6 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml kind: hosted -name: hosted-workflows +name: hosted-workflow-simple protocols: - protocol: responses version: 1.0.0 diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SimpleAgent/SimpleAgent.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SimpleAgent/SimpleAgent.csproj index 05e150880e..3c739b96d0 100644 --- a/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SimpleAgent/SimpleAgent.csproj +++ b/dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SimpleAgent/SimpleAgent.csproj @@ -8,7 +8,7 @@ false SimpleAgentClient simple-agent-client - $(NoWarn);NU1903;NU1605;OPENAI001 + $(NoWarn);NU1605;OPENAI001 diff --git a/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/AgentFrameworkResponseHandler.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/AgentFrameworkResponseHandler.cs index b9a0ebeb0f..d55dd02812 100644 --- a/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/AgentFrameworkResponseHandler.cs +++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/AgentFrameworkResponseHandler.cs @@ -54,14 +54,14 @@ public class AgentFrameworkResponseHandler : ResponseHandler var sessionStore = this.ResolveSessionStore(request); // 2. Load or create a new session from the interaction - var sessionConversationId = request.GetConversationId() ?? Guid.NewGuid().ToString(); + var sessionConversationId = request.GetConversationId(); var chatClientAgent = agent.GetService(); - AgentSession? session = !string.IsNullOrEmpty(sessionConversationId) + AgentSession? session = !string.IsNullOrWhiteSpace(sessionConversationId) ? await sessionStore.GetSessionAsync(agent, sessionConversationId, cancellationToken).ConfigureAwait(false) : chatClientAgent is not null - ? await chatClientAgent.CreateSessionAsync(sessionConversationId, cancellationToken).ConfigureAwait(false) + ? await chatClientAgent.CreateSessionAsync(cancellationToken).ConfigureAwait(false) : await agent.CreateSessionAsync(cancellationToken).ConfigureAwait(false); // 3. Create the SDK event stream builder @@ -273,7 +273,7 @@ public class AgentFrameworkResponseHandler : ResponseHandler await enumerator.DisposeAsync().ConfigureAwait(false); // Persist session after streaming completes (successful or not) - if (session is not null && !string.IsNullOrEmpty(sessionConversationId)) + if (session is not null && !string.IsNullOrWhiteSpace(sessionConversationId)) { await sessionStore.SaveSessionAsync(agent, sessionConversationId, session, CancellationToken.None).ConfigureAwait(false); } diff --git a/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj b/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj index 13f21cdc52..63deb7a0e3 100644 --- a/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj +++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj @@ -3,7 +3,7 @@ true true - $(NoWarn);OPENAI001;MEAI001;NU1903 + $(NoWarn);OPENAI001;MEAI001;NU1903 false diff --git a/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj b/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj index 52a1cc0393..fa0cc64c1e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj @@ -2,7 +2,7 @@ false - $(NoWarn);NU1903;NU1605 + $(NoWarn);NU1605