From 4b9d848541ec462a0a8cb4b14ff4eeb696e79aed Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Mon, 13 Apr 2026 16:59:18 +0100
Subject: [PATCH] Adding LocalTools + Workflow samples
---
dotnet/agent-framework-dotnet.slnx | 6 +
.../Hosted-LocalTools/.env.local | 4 +
.../Hosted-LocalTools/Dockerfile | 17 ++
.../Hosted-LocalTools/Dockerfile.contributor | 19 ++
.../Hosted-LocalTools/HostedLocalTools.csproj | 30 ++++
.../Hosted-LocalTools/Program.cs | 164 ++++++++++++++++++
.../Properties/launchSettings.json | 11 ++
.../Hosted-LocalTools/README.md | 113 ++++++++++++
.../Hosted-LocalTools/agent.manifest.yaml | 29 ++++
.../Hosted-LocalTools/agent.yaml | 9 +
.../Hosted-Workflows/.env.local | 4 +
.../Hosted-Workflows/Dockerfile | 17 ++
.../Hosted-Workflows/Dockerfile.contributor | 18 ++
.../Hosted-Workflows/HostedWorkflows.csproj | 34 ++++
.../Hosted-Workflows/Program.cs | 97 +++++++++++
.../Properties/launchSettings.json | 11 ++
.../HostedAgentsV2/Hosted-Workflows/README.md | 109 ++++++++++++
.../Hosted-Workflows/agent.manifest.yaml | 29 ++++
.../Hosted-Workflows/agent.yaml | 9 +
19 files changed, 730 insertions(+)
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/.env.local
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile.contributor
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/HostedLocalTools.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Properties/launchSettings.json
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/README.md
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.manifest.yaml
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.yaml
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/.env.local
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile.contributor
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/HostedWorkflows.csproj
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Program.cs
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Properties/launchSettings.json
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/README.md
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.manifest.yaml
create mode 100644 dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.yaml
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 8de8933bea..c065430b54 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -283,6 +283,12 @@
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/.env.local b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/.env.local
new file mode 100644
index 0000000000..6d7831229d
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/.env.local
@@ -0,0 +1,4 @@
+AZURE_AI_PROJECT_ENDPOINT=
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile
new file mode 100644
index 0000000000..1b72fcd93f
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile
@@ -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", "HostedLocalTools.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile.contributor b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile.contributor
new file mode 100644
index 0000000000..65f920824a
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Dockerfile.contributor
@@ -0,0 +1,19 @@
+# Dockerfile for contributors building from the agent-framework repository source.
+#
+# This project uses ProjectReference to the local Microsoft.Agents.AI.Foundry source,
+# which means a standard multi-stage Docker build cannot resolve dependencies outside
+# this folder. Instead, pre-publish the app targeting the container runtime and copy
+# the output into the container:
+#
+# dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
+# docker build -f Dockerfile.contributor -t hosted-local-tools .
+# docker run --rm -p 8088:8088 -e AGENT_NAME=hosted-local-tools -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN --env-file .env hosted-local-tools
+#
+# 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", "HostedLocalTools.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/HostedLocalTools.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/HostedLocalTools.csproj
new file mode 100644
index 0000000000..b0d39d8cee
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/HostedLocalTools.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ HostedLocalTools
+ HostedLocalTools
+ $(NoWarn);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Program.cs
new file mode 100644
index 0000000000..f1b2f7e3bd
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Program.cs
@@ -0,0 +1,164 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// Seattle Hotel Agent - A hosted agent with local C# function tools.
+// Demonstrates how to define and wire local tools that the LLM can invoke,
+// a key advantage of code-based hosted agents over prompt agents.
+
+using System.ComponentModel;
+using System.Globalization;
+using System.Text;
+using Azure.AI.Projects;
+using Azure.Core;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry.Hosting;
+using Microsoft.Extensions.AI;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
+ ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
+
+// Use a chained credential: try a temporary dev token first (for local Docker debugging),
+// then fall back to DefaultAzureCredential (for local dev via dotnet run / managed identity in production).
+TokenCredential credential = new ChainedTokenCredential(
+ new DevTemporaryTokenCredential(),
+ new DefaultAzureCredential());
+
+// ── Hotel data ───────────────────────────────────────────────────────────────
+
+Hotel[] seattleHotels =
+[
+ new("Contoso Suites", 189, 4.5, "Downtown"),
+ new("Fabrikam Residences", 159, 4.2, "Pike Place Market"),
+ new("Alpine Ski House", 249, 4.7, "Seattle Center"),
+ new("Margie's Travel Lodge", 219, 4.4, "Waterfront"),
+ new("Northwind Inn", 139, 4.0, "Capitol Hill"),
+ new("Relecloud Hotel", 99, 3.8, "University District"),
+];
+
+// ── Tool: GetAvailableHotels ─────────────────────────────────────────────────
+
+[Description("Get available hotels in Seattle for the specified dates.")]
+string GetAvailableHotels(
+ [Description("Check-in date in YYYY-MM-DD format")] string checkInDate,
+ [Description("Check-out date in YYYY-MM-DD format")] string checkOutDate,
+ [Description("Maximum price per night in USD (optional, defaults to 500)")] int maxPrice = 500)
+{
+ if (!DateTime.TryParseExact(checkInDate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var checkIn))
+ {
+ return "Error parsing check-in date. Please use YYYY-MM-DD format.";
+ }
+
+ if (!DateTime.TryParseExact(checkOutDate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var checkOut))
+ {
+ return "Error parsing check-out date. Please use YYYY-MM-DD format.";
+ }
+
+ if (checkOut <= checkIn)
+ {
+ return "Error: Check-out date must be after check-in date.";
+ }
+
+ int nights = (checkOut - checkIn).Days;
+ List availableHotels = seattleHotels.Where(h => h.PricePerNight <= maxPrice).ToList();
+
+ if (availableHotels.Count == 0)
+ {
+ return $"No hotels found in Seattle within your budget of ${maxPrice}/night.";
+ }
+
+ StringBuilder result = new();
+ result.AppendLine($"Available hotels in Seattle from {checkInDate} to {checkOutDate} ({nights} nights):");
+ result.AppendLine();
+
+ foreach (Hotel hotel in availableHotels)
+ {
+ int totalCost = hotel.PricePerNight * nights;
+ result.AppendLine($"**{hotel.Name}**");
+ result.AppendLine($" Location: {hotel.Location}");
+ result.AppendLine($" Rating: {hotel.Rating}/5");
+ result.AppendLine($" ${hotel.PricePerNight}/night (Total: ${totalCost})");
+ result.AppendLine();
+ }
+
+ return result.ToString();
+}
+
+// ── Create and host the agent ────────────────────────────────────────────────
+
+AIAgent agent = new AIProjectClient(new Uri(endpoint), credential)
+ .AsAIAgent(
+ model: deploymentName,
+ instructions: """
+ You are a helpful travel assistant specializing in finding hotels in Seattle, Washington.
+
+ When a user asks about hotels in Seattle:
+ 1. Ask for their check-in and check-out dates if not provided
+ 2. Ask about their budget preferences if not mentioned
+ 3. Use the GetAvailableHotels tool to find available options
+ 4. Present the results in a friendly, informative way
+ 5. Offer to help with additional questions about the hotels or Seattle
+
+ Be conversational and helpful. If users ask about things outside of Seattle hotels,
+ politely let them know you specialize in Seattle hotel recommendations.
+ """,
+ name: Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-local-tools",
+ description: "Seattle hotel search agent with local function tools",
+ tools: [AIFunctionFactory.Create(GetAvailableHotels)]);
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddFoundryResponses(agent);
+
+var app = builder.Build();
+app.MapFoundryResponses();
+
+if (app.Environment.IsDevelopment())
+{
+ app.MapFoundryResponses("openai/v1");
+}
+
+app.Run();
+
+// ── Types ────────────────────────────────────────────────────────────────────
+
+internal sealed record Hotel(string Name, int PricePerNight, double Rating, string Location);
+
+///
+/// A for local Docker debugging only.
+/// Reads a pre-fetched bearer token from the AZURE_BEARER_TOKEN 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 ...
+///
+internal sealed class DevTemporaryTokenCredential : TokenCredential
+{
+ private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
+ private readonly string? _token;
+
+ public DevTemporaryTokenCredential()
+ {
+ _token = Environment.GetEnvironmentVariable(EnvironmentVariable);
+ }
+
+ public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => GetAccessToken();
+
+ public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => new(GetAccessToken());
+
+ private AccessToken GetAccessToken()
+ {
+ if (string.IsNullOrEmpty(_token))
+ {
+ throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
+ }
+
+ return new AccessToken(_token, DateTimeOffset.UtcNow.AddHours(1));
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Properties/launchSettings.json b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Properties/launchSettings.json
new file mode 100644
index 0000000000..ae1bb80b7d
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "HostedLocalTools": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:8088"
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/README.md b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/README.md
new file mode 100644
index 0000000000..3c41803b95
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/README.md
@@ -0,0 +1,113 @@
+# Hosted-LocalTools
+
+A hosted agent with **local C# function tools** for hotel search. Demonstrates how to define and wire local tools that the LLM can invoke — a key advantage of code-based hosted agents over prompt agents.
+
+The agent specializes in finding hotels in Seattle, with a `GetAvailableHotels` tool that searches a mock hotel database by dates and budget.
+
+## 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`)
+
+## Configuration
+
+Copy the template and fill in your project endpoint:
+
+```bash
+cp .env.local .env
+```
+
+Edit `.env` and set your Azure AI Foundry project endpoint:
+
+```env
+AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
+```
+
+> **Note:** `.env` is gitignored. The `.env.local` template is checked in as a reference.
+
+## Running directly (contributors)
+
+This project uses `ProjectReference` to build against the local Agent Framework source.
+
+```bash
+cd dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools
+AGENT_NAME=hosted-local-tools dotnet run
+```
+
+The agent will start on `http://localhost:8088`.
+
+### Test it
+
+Using the Azure Developer CLI:
+
+```bash
+azd ai agent invoke --local "Find me a hotel in Seattle for Dec 20-25 under $200/night"
+```
+
+Or with curl:
+
+```bash
+curl -X POST http://localhost:8088/responses \
+ -H "Content-Type: application/json" \
+ -d '{"input": "Find me a hotel in Seattle for Dec 20-25 under $200/night", "model": "hosted-local-tools"}'
+```
+
+## Running with Docker
+
+Since this project uses `ProjectReference`, use `Dockerfile.contributor` which takes a pre-published output.
+
+### 1. Publish for the container runtime (Linux Alpine)
+
+```bash
+dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
+```
+
+### 2. Build the Docker image
+
+```bash
+docker build -f Dockerfile.contributor -t hosted-local-tools .
+```
+
+### 3. Run the container
+
+Generate a bearer token on your host and pass it to the container:
+
+```bash
+# Generate token (expires in ~1 hour)
+export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
+
+# Run with token
+docker run --rm -p 8088:8088 \
+ -e AGENT_NAME=hosted-local-tools \
+ -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \
+ --env-file .env \
+ hosted-local-tools
+```
+
+### 4. Test it
+
+Using the Azure Developer CLI:
+
+```bash
+azd ai agent invoke --local "What hotels are available in Seattle for next weekend?"
+```
+
+## How local tools work
+
+The agent has a single tool `GetAvailableHotels` defined as a C# method with `[Description]` attributes. The LLM decides when to call it based on the user's request:
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `checkInDate` | string | Check-in date (YYYY-MM-DD) |
+| `checkOutDate` | string | Check-out date (YYYY-MM-DD) |
+| `maxPrice` | int | Max price per night in USD (default: 500) |
+
+The tool searches a mock database of 6 Seattle hotels and returns formatted results with name, location, rating, and pricing.
+
+## NuGet package users
+
+If you are consuming the Agent Framework as a NuGet package (not building from source), use the standard `Dockerfile` instead of `Dockerfile.contributor`. See the commented section in `HostedLocalTools.csproj` for the `PackageReference` alternative.
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.manifest.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.manifest.yaml
new file mode 100644
index 0000000000..a056b51649
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.manifest.yaml
@@ -0,0 +1,29 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/AgentManifest.yaml
+name: hosted-local-tools
+displayName: "Seattle Hotel Agent with Local Tools"
+
+description: >
+ A travel assistant agent that helps users find hotels in Seattle.
+ Demonstrates local C# tool execution — a key advantage of code-based
+ hosted agents over prompt agents.
+
+metadata:
+ tags:
+ - AI Agent Hosting
+ - Azure AI AgentServer
+ - Responses Protocol
+ - Local Tools
+ - Agent Framework
+
+template:
+ name: hosted-local-tools
+ kind: hosted
+ protocols:
+ - protocol: responses
+ version: 1.0.0
+ resources:
+ cpu: "0.25"
+ memory: 0.5Gi
+parameters:
+ properties: []
+resources: []
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.yaml
new file mode 100644
index 0000000000..18ecc4a9f7
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-LocalTools/agent.yaml
@@ -0,0 +1,9 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
+kind: hosted
+name: hosted-local-tools
+protocols:
+ - protocol: responses
+ version: 1.0.0
+resources:
+ cpu: "0.25"
+ memory: 0.5Gi
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/.env.local b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/.env.local
new file mode 100644
index 0000000000..6d7831229d
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/.env.local
@@ -0,0 +1,4 @@
+AZURE_AI_PROJECT_ENDPOINT=
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile
new file mode 100644
index 0000000000..e770ec172b
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile
@@ -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", "HostedWorkflows.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile.contributor b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile.contributor
new file mode 100644
index 0000000000..b8dae44c2b
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Dockerfile.contributor
@@ -0,0 +1,18 @@
+# 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-workflows .
+# docker run --rm -p 8088:8088 -e AGENT_NAME=hosted-workflows -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN --env-file .env hosted-workflows
+#
+# 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", "HostedWorkflows.dll"]
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/HostedWorkflows.csproj b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/HostedWorkflows.csproj
new file mode 100644
index 0000000000..2f210a18d8
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/HostedWorkflows.csproj
@@ -0,0 +1,34 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ HostedWorkflows
+ HostedWorkflows
+ $(NoWarn);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Program.cs b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Program.cs
new file mode 100644
index 0000000000..6288e3cf5f
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Program.cs
@@ -0,0 +1,97 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// Translation Chain Workflow Agent — demonstrates how to compose multiple AI agents
+// into a sequential workflow pipeline. Three translation agents are connected:
+// English → French → Spanish → English, showing how agents can be orchestrated
+// as workflow executors in a hosted agent.
+
+using Azure.AI.Projects;
+using Azure.Core;
+using Azure.Identity;
+using DotNetEnv;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry.Hosting;
+using Microsoft.Agents.AI.Workflows;
+using Microsoft.Extensions.AI;
+
+// Load .env file if present (for local development)
+Env.TraversePath().Load();
+
+string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT")
+ ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o";
+
+// Use a chained credential: try a temporary dev token first (for local Docker debugging),
+// then fall back to DefaultAzureCredential (for local dev via dotnet run / managed identity in production).
+TokenCredential credential = new ChainedTokenCredential(
+ new DevTemporaryTokenCredential(),
+ new DefaultAzureCredential());
+
+// Create a chat client from the Foundry project
+IChatClient chatClient = new AIProjectClient(new Uri(endpoint), credential)
+ .GetProjectOpenAIClient()
+ .GetChatClient(deploymentName)
+ .AsIChatClient();
+
+// Create translation agents
+AIAgent frenchAgent = chatClient.AsAIAgent("You are a translation assistant that translates the provided text to French.");
+AIAgent spanishAgent = chatClient.AsAIAgent("You are a translation assistant that translates the provided text to Spanish.");
+AIAgent englishAgent = chatClient.AsAIAgent("You are a translation assistant that translates the provided text to English.");
+
+// Build the sequential workflow: French → Spanish → English
+AIAgent agent = new WorkflowBuilder(frenchAgent)
+ .AddEdge(frenchAgent, spanishAgent)
+ .AddEdge(spanishAgent, englishAgent)
+ .Build()
+ .AsAIAgent(
+ name: Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-workflows");
+
+// Host the workflow agent as a Foundry Hosted Agent using the Responses API.
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddFoundryResponses(agent);
+
+var app = builder.Build();
+app.MapFoundryResponses();
+
+if (app.Environment.IsDevelopment())
+{
+ app.MapFoundryResponses("openai/v1");
+}
+
+app.Run();
+
+///
+/// A for local Docker debugging only.
+/// Reads a pre-fetched bearer token from the AZURE_BEARER_TOKEN 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 ...
+///
+internal sealed class DevTemporaryTokenCredential : TokenCredential
+{
+ private const string EnvironmentVariable = "AZURE_BEARER_TOKEN";
+ private readonly string? _token;
+
+ public DevTemporaryTokenCredential()
+ {
+ _token = Environment.GetEnvironmentVariable(EnvironmentVariable);
+ }
+
+ public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => GetAccessToken();
+
+ public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
+ => new(GetAccessToken());
+
+ private AccessToken GetAccessToken()
+ {
+ if (string.IsNullOrEmpty(_token))
+ {
+ throw new CredentialUnavailableException($"{EnvironmentVariable} environment variable is not set.");
+ }
+
+ return new AccessToken(_token, DateTimeOffset.UtcNow.AddHours(1));
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Properties/launchSettings.json b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Properties/launchSettings.json
new file mode 100644
index 0000000000..0e2908985a
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "HostedWorkflows": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:8088"
+ }
+ }
+}
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/README.md b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/README.md
new file mode 100644
index 0000000000..0bb000aaa1
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/README.md
@@ -0,0 +1,109 @@
+# Hosted-Workflows
+
+A hosted agent that demonstrates **multi-agent workflow orchestration**. Three translation agents are composed into a sequential pipeline: English → French → Spanish → English, showing how agents can be chained as workflow executors using `WorkflowBuilder`.
+
+## 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`)
+
+## Configuration
+
+Copy the template and fill in your project endpoint:
+
+```bash
+cp .env.local .env
+```
+
+Edit `.env` and set your Azure AI Foundry project endpoint:
+
+```env
+AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/
+ASPNETCORE_URLS=http://+:8088
+ASPNETCORE_ENVIRONMENT=Development
+AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
+```
+
+> **Note:** `.env` is gitignored. The `.env.local` template is checked in as a reference.
+
+## Running directly (contributors)
+
+```bash
+cd dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows
+AGENT_NAME=hosted-workflows dotnet run
+```
+
+The agent will start on `http://localhost:8088`.
+
+### Test it
+
+Using the Azure Developer CLI:
+
+```bash
+azd ai agent invoke --local "The quick brown fox jumps over the lazy dog"
+```
+
+Or with curl:
+
+```bash
+curl -X POST http://localhost:8088/responses \
+ -H "Content-Type: application/json" \
+ -d '{"input": "The quick brown fox jumps over the lazy dog", "model": "hosted-workflows"}'
+```
+
+The text will be translated through the chain: English → French → Spanish → English.
+
+## Running with Docker
+
+### 1. Publish for the container runtime
+
+```bash
+dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
+```
+
+### 2. Build the Docker image
+
+```bash
+docker build -f Dockerfile.contributor -t hosted-workflows .
+```
+
+### 3. Run the container
+
+```bash
+export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
+
+docker run --rm -p 8088:8088 \
+ -e AGENT_NAME=hosted-workflows \
+ -e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \
+ --env-file .env \
+ hosted-workflows
+```
+
+### 4. Test it
+
+```bash
+azd ai agent invoke --local "Hello, how are you today?"
+```
+
+## How the workflow works
+
+```
+Input text
+ │
+ ▼
+┌─────────────┐ ┌──────────────┐ ┌──────────────┐
+│ French Agent │ → │ Spanish Agent │ → │ English Agent │
+│ (translate) │ │ (translate) │ │ (translate) │
+└─────────────┘ └──────────────┘ └──────────────┘
+ │
+ ▼
+ Final output
+ (back in English)
+```
+
+Each agent in the chain receives the output of the previous agent. The final result demonstrates how meaning is preserved (or subtly shifted) through multiple translation hops.
+
+## NuGet package users
+
+Use the standard `Dockerfile` instead of `Dockerfile.contributor`. See the commented section in `HostedWorkflows.csproj` for the `PackageReference` alternative.
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.manifest.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.manifest.yaml
new file mode 100644
index 0000000000..e902b6232f
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.manifest.yaml
@@ -0,0 +1,29 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/AgentManifest.yaml
+name: hosted-workflows
+displayName: "Translation Chain Workflow Agent"
+
+description: >
+ A workflow agent that performs sequential translation through multiple languages.
+ Translates text from English to French, then to Spanish, and finally back to English,
+ demonstrating how AI agents can be composed as workflow executors.
+
+metadata:
+ tags:
+ - AI Agent Hosting
+ - Azure AI AgentServer
+ - Responses Protocol
+ - Workflows
+ - Agent Framework
+
+template:
+ name: hosted-workflows
+ kind: hosted
+ protocols:
+ - protocol: responses
+ version: 1.0.0
+ resources:
+ cpu: "0.25"
+ memory: 0.5Gi
+parameters:
+ properties: []
+resources: []
diff --git a/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.yaml b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.yaml
new file mode 100644
index 0000000000..ab138939b4
--- /dev/null
+++ b/dotnet/samples/04-hosting/FoundryHostedAgents/HostedAgentsV2/Hosted-Workflows/agent.yaml
@@ -0,0 +1,9 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
+kind: hosted
+name: hosted-workflows
+protocols:
+ - protocol: responses
+ version: 1.0.0
+resources:
+ cpu: "0.25"
+ memory: 0.5Gi