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 <bentho@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Ben Thomas
2026-04-21 09:34:45 -07:00
committed by GitHub
Unverified
parent f062ed4f63
commit 4a7f04157a
10 changed files with 15 additions and 18 deletions
@@ -8,7 +8,7 @@
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<RootNamespace>SimpleInvocationsAgentClient</RootNamespace>
<AssemblyName>simple-invocations-agent-client</AssemblyName>
<NoWarn>$(NoWarn);NU1903;NU1605</NoWarn>
<NoWarn>$(NoWarn);NU1605</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -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://<your-account>.services.ai.azure.com/api/projects/<your-project>
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
@@ -8,7 +8,7 @@
<RootNamespace>HostedWorkflowHandoff</RootNamespace>
<AssemblyName>HostedWorkflowHandoff</AssemblyName>
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<NoWarn>$(NoWarn);NU1903;NU1605;MAAIW001</NoWarn>
<NoWarn>$(NoWarn);NU1605;MAAIW001</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -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"]
@@ -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`.
@@ -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
@@ -8,7 +8,7 @@
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<RootNamespace>SimpleAgentClient</RootNamespace>
<AssemblyName>simple-agent-client</AssemblyName>
<NoWarn>$(NoWarn);NU1903;NU1605;OPENAI001</NoWarn>
<NoWarn>$(NoWarn);NU1605;OPENAI001</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -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<ChatClientAgent>();
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);
}
@@ -3,7 +3,7 @@
<PropertyGroup>
<IsReleased>true</IsReleased>
<InjectSharedThrow>true</InjectSharedThrow>
<NoWarn>$(NoWarn);OPENAI001;MEAI001;NU1903</NoWarn>
<NoWarn>$(NoWarn);OPENAI001;MEAI001;NU1903</NoWarn> <!-- NU1903: Microsoft.Bcl.Memory 9.0.4 transitive vulnerability via Azure SDK; awaiting upstream fix -->
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
@@ -2,7 +2,7 @@
<PropertyGroup>
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<NoWarn>$(NoWarn);NU1903;NU1605</NoWarn>
<NoWarn>$(NoWarn);NU1605</NoWarn>
</PropertyGroup>
<ItemGroup>