Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
64c8614
.NET: Add Hosted-Toolbox-AuthPaths sample and auto-map /readiness wit…
rogerbarreto May 21, 2026
ad3894b
.NET: Align FoundryToolboxService with tools-integration-spec (#5777 …
rogerbarreto May 21, 2026
527fd19
.NET: Drop ACA prereq from Hosted-Toolbox-AuthPaths README (#5777 Par…
rogerbarreto May 21, 2026
d14629e
.NET: Document correct connection shape for Hosted-Toolbox-AuthPaths …
rogerbarreto May 21, 2026
b09bd4c
.NET: Drop Path 3 (project MI) and align with new agent model in Host…
rogerbarreto May 21, 2026
929cafb
.NET: Restore Path 3 (project MI) to Hosted-Toolbox-AuthPaths matrix …
rogerbarreto May 21, 2026
fae1b1d
.NET: Fix duplicate line in Hosted-Toolbox-AuthPaths README (#5777)
rogerbarreto May 21, 2026
636d99d
.NET: Fix broken markdown link to ToolCallingApprovalHostedAgentFixtu…
rogerbarreto May 21, 2026
b8f84ac
.NET: Fix relative path depth in markdown link (#5777)
rogerbarreto May 21, 2026
274421f
.NET: Address Copilot review feedback for #5777
rogerbarreto May 21, 2026
5e32775
.NET: Address Copilot review round 2 — fallback env + dedup + naming …
rogerbarreto May 21, 2026
4f41b89
.NET: Fix stale NoEndpoint XML doc and misleading test comment (#5777)
rogerbarreto May 22, 2026
c3e204d
Remove OAuth consent path from AuthPaths sample, keep four working au…
rogerbarreto May 29, 2026
25457cb
.NET: Drop project MI auth path and dedicated client from Hosted-Tool…
rogerbarreto May 29, 2026
b3e1c0f
Merge branch 'main' into issues/5777-net-hosted-agent-sample-toolbox-…
rogerbarreto Jun 5, 2026
6602060
Merge branch 'main' into issues/5777-net-hosted-agent-sample-toolbox-…
rogerbarreto Jun 8, 2026
ce743ff
Merge branch 'main' into issues/5777-net-hosted-agent-sample-toolbox-…
rogerbarreto Jun 8, 2026
915c8d2
.NET: Address PR review nits for Hosted-Toolbox-AuthPaths (#5777)
rogerbarreto Jun 10, 2026
167ff8b
.NET: Address toolbox startup-status review feedback (#5777)
rogerbarreto Jun 10, 2026
a143d7a
.NET: Reword toolbox-wiring comment to avoid hosting-layer internals …
rogerbarreto Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox/HostedToolbox.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox-AuthPaths/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox-AuthPaths/Hosted-Toolbox-AuthPaths.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-ToolboxMcpSkills/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-ToolboxMcpSkills/HostedToolboxMcpSkills.csproj" />
</Folder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,9 @@ docker run --rm -p 8088:8088 \
## NuGet package users

Use the standard `Dockerfile` instead of `Dockerfile.contributor`. See the commented section in `HostedMcpTools.csproj` for the `PackageReference` alternative.

## Related samples

- [`Hosted-Toolbox/`](../Hosted-Toolbox/) — connects to a single Foundry Toolbox via the AF Foundry hosting bridge (`AddFoundryToolboxes` + `FoundryAITool.CreateHostedMcpToolbox`).
- [`Hosted-Toolbox-AuthPaths/`](../Hosted-Toolbox-AuthPaths/) — same hosting bones as `Hosted-Toolbox/`, but the toolbox bundles three MCP tools each authenticated differently (key, Entra agent identity, inline `Authorization`), driven by the shared `Using-Samples/SimpleAgent/` REPL.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Azure AI Foundry project endpoint (auto-injected in hosted containers).
AZURE_AI_PROJECT_ENDPOINT=https://<your-foundry-account>.services.ai.azure.com/api/projects/<your-project>

# Model deployment name. Must exist in the Foundry project above.
AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o

# Name of the Foundry Toolbox you provisioned in the portal (see README.md).
TOOLBOX_NAME=auth-paths-toolbox

# Agent name advertised over the wire. Must be unique if running side-by-side with
# other Hosted-* samples (e.g. Hosted-Toolbox), otherwise the REPL client cannot
# disambiguate which agent to chat with.
AGENT_NAME=hosted-toolbox-auth-paths-agent

# Application Insights connection string (auto-injected in hosted containers; optional locally).
# APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Use the official .NET 10.0 ASP.NET runtime as a parent image
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish

# Final stage
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8088
ENV ASPNETCORE_URLS=http://+:8088
ENTRYPOINT ["dotnet", "HostedToolboxAuthPaths.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Dockerfile for contributors building from the agent-framework repository source.
#
# This project uses ProjectReference to the local source, which means a standard
# multi-stage Docker build cannot resolve dependencies outside this folder.
# Pre-publish the app targeting the container runtime and copy the output:
#
# dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
# docker build -f Dockerfile.contributor -t hosted-toolbox-auth-paths .
# docker run --rm -p 8088:8088 \
# -e AGENT_NAME=hosted-toolbox-auth-paths-agent \
# -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \
# --env-file .env hosted-toolbox-auth-paths
#
# For end-users consuming the NuGet package (not ProjectReference), use the standard
# Dockerfile which performs a full dotnet restore + publish inside the container.
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app
COPY out/ .
EXPOSE 8088
ENV ASPNETCORE_URLS=http://+:8088
ENTRYPOINT ["dotnet", "HostedToolboxAuthPaths.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
<RootNamespace>HostedToolboxAuthPaths</RootNamespace>
<AssemblyName>HostedToolboxAuthPaths</AssemblyName>
<NoWarn>$(NoWarn);OPENAI001</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.Projects" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="DotNetEnv" />
</ItemGroup>

<!-- For contributors: uses ProjectReference to build against local source -->
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Foundry\Microsoft.Agents.AI.Foundry.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Foundry.Hosting\Microsoft.Agents.AI.Foundry.Hosting.csproj" />
<ProjectReference Include="..\Hosted_Shared_Contributor_Setup\Hosted_Shared_Contributor_Setup.csproj" />
</ItemGroup>

<!-- For end-users: uncomment the PackageReference below and remove the ProjectReference above
<ItemGroup>
<PackageReference Include="Microsoft.Agents.AI.Foundry" Version="1.0.0" />
<PackageReference Include="Microsoft.Agents.AI.Foundry.Hosting" Version="1.0.0" />
</ItemGroup>
-->

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Microsoft. All rights reserved.

// Foundry Toolbox Auth Paths Agent — A hosted agent backed by a single Foundry Toolbox
// that bundles MCP tools using THREE different authentication paths.
//
// This sample demonstrates the same hosting bones as Hosted-Toolbox/, but the toolbox
// (provisioned by the user out-of-band) contains three MCP tool entries each authenticated
// differently. The agent code itself is agnostic to authentication — the educational
// surface lives in the toolbox configuration in the Foundry portal and in this sample's
// README.md.
//
// Required environment variables:
// AZURE_AI_PROJECT_ENDPOINT (local-dev) OR FOUNDRY_PROJECT_ENDPOINT (hosted runtime)
// - Azure AI Foundry project endpoint. The Foundry hosted
// runtime auto-injects FOUNDRY_PROJECT_ENDPOINT; locally
// set AZURE_AI_PROJECT_ENDPOINT (the AF-repo convention).
// TOOLBOX_NAME - Name of the Foundry Toolbox to load
// (default: auth-paths-toolbox)
//
// Optional:
// AZURE_AI_MODEL_DEPLOYMENT_NAME - Model deployment name (default: gpt-4o)
// AGENT_NAME - Defaults to "hosted-toolbox-auth-paths-agent".
//
// The Foundry.Hosting package builds the toolbox proxy URL from FOUNDRY_PROJECT_ENDPOINT
// per tools-integration-spec.md §2–§3, so the sample does not need to plumb any
// toolbox-specific URL env var.
//
// NOTE: All FOUNDRY_* and AGENT_* env-var prefixes (other than the platform-injected ones
// listed above) are reserved by the Foundry container platform and rejected by the
// agent-create API. Use TOOLBOX_NAME, not FOUNDRY_TOOLBOX_NAME, for sample-owned config.

#pragma warning disable OPENAI001 // FoundryAITool.CreateHostedMcpToolbox is experimental

using Azure.AI.Projects;
using Azure.Core;
using Azure.Identity;
using DotNetEnv;
using Hosted_Shared_Contributor_Setup;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;

// Load .env file if present (for local development)
Env.TraversePath().Load();

// Project endpoint resolution order:
// 1. FOUNDRY_PROJECT_ENDPOINT — auto-injected by the Foundry hosted runtime.
// 2. AZURE_AI_PROJECT_ENDPOINT — the convention developers set locally for `dotnet run`.
// When deployed, only (1) is available; the AF-repo sample convention to set (2) at
// deploy time fails silently because the platform reserves all FOUNDRY_* env-var names
// and rejects them at agent-create time. Read both, prefer the platform-injected one.
string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
?? Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
?? throw new InvalidOperationException(
"Neither FOUNDRY_PROJECT_ENDPOINT (platform-injected in hosted runtime) " +
"nor AZURE_AI_PROJECT_ENDPOINT (local-dev convention) is set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
string toolboxName = Environment.GetEnvironmentVariable("TOOLBOX_NAME") ?? "auth-paths-toolbox";
string agentName = Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-toolbox-auth-paths-agent";

TokenCredential credential = new ChainedTokenCredential(
new DevTemporaryTokenCredential(),
new DefaultAzureCredential());

// Notes on toolbox wiring — there are two ways to attach a Foundry Toolbox to an agent:
// - Server-side "baked-in" (what this sample uses): calling AddFoundryToolboxes(name)
// below registers the toolbox with the Foundry.Hosting layer, which resolves that
// toolbox's MCP tools once at startup and automatically makes them available to the
// agent on every request. The agent code does nothing per request.
// - Per-request / caller-driven (NOT used here): a client can attach a toolbox for a
// single call by placing a FoundryAITool.CreateHostedMcpToolbox(name) marker in the
// request body's tool list.
// Because this sample bakes the toolbox in on the server, it uses AddFoundryToolboxes and
// does NOT put the CreateHostedMcpToolbox marker in the agent's `tools:` array.
AIAgent agent = new AIProjectClient(new Uri(endpoint), credential)
.AsAIAgent(
model: deploymentName,
instructions: """
You are a helpful assistant with access to several tools, each provided by a different
upstream service authenticated through a distinct mechanism (API key, agent managed
identity, and a literal token
shipped with the tool definition). Pick the tool that best fits the user's question
and explain which upstream service answered when you respond.
""",
name: agentName,
description: "Hosted agent demonstrating three MCP-tool authentication paths via a Foundry Toolbox.");

// Tier 3 spine (WebApplication.CreateBuilder + AddFoundryResponses + MapFoundryResponses):
// the Foundry.Hosting package auto-maps the spec-required GET /readiness probe inside
// MapFoundryResponses (idempotent — skipped when AgentHost or the developer already
// mapped it), so the sample stays free of platform plumbing.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddFoundryResponses(agent);
// Pre-register the toolbox name so FoundryToolboxService resolves the foundry-toolbox://
// marker at request time. With FOUNDRY_PROJECT_ENDPOINT injected by the platform, startup
// MCP tools/list against the toolbox proxy is typically <100ms in-region.
builder.Services.AddFoundryToolboxes(toolboxName);

var app = builder.Build();
app.MapFoundryResponses();

// Contributor-only: in Development, also map the per-agent OpenAI route shape that live Foundry
// uses so a local REPL client can target this server via AIProjectClient.AsAIAgent(Uri agentEndpoint).
// Do not use this in production. Hosted Foundry agents only support the agent-endpoint path.
app.MapDevTemporaryLocalAgentEndpoint();

app.Run();

// ── DevTemporaryTokenCredential ───────────────────────────────────────────────

/// <summary>
/// A <see cref="TokenCredential"/> for local Docker debugging only.
/// Reads a pre-fetched bearer token from the <c>AZURE_BEARER_TOKEN</c> environment variable
/// once at startup. This should NOT be used in production.
///
/// Generate a token on your host and pass it to the container:
/// export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
/// docker run -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN ...
/// </summary>
internal sealed class DevTemporaryTokenCredential : TokenCredential
{
private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
private readonly string? _token;

public DevTemporaryTokenCredential()
{
this._token = Environment.GetEnvironmentVariable(EnvironmentVariable);
}

public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
=> this.GetAccessToken();

public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
=> new(this.GetAccessToken());

private AccessToken GetAccessToken()
{
if (string.IsNullOrEmpty(this._token) || this._token == "DefaultAzureCredential")
{
throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
}

return new AccessToken(this._token, DateTimeOffset.MaxValue);
}
}
Loading
Loading