Skip to content

Commit 73df0d7

Browse files
rogerbarretoCopilot
authored andcommitted
Add C# toolbox-auth-paths hosted-agent sample (#402)
* Add C# toolbox auth-paths hosted-agent sample and wire cloud E2E Add samples/csharp/hosted-agents/agent-framework/toolbox-auth-paths, an Agent Framework hosted agent that loads a multi-tool Foundry Toolbox demonstrating the authentication paths a toolbox MCP tool can use to reach its upstream server: key-based (CustomKeys connection) and public no-auth. Foundry resolves each tool credential server-side, so the agent code carries no auth logic. Wire the sample into hosted-agents-cloud-e2e.yml as a self-provisioning cell. A guarded self_provision flag (set only for this sample) forces a single matrix cell instead of the toolbox cartesian product, runs azd provision so the connection and toolbox are created from the manifest resources, sources the provisioning PAT from the GH_PAT secret, and fails fast when no real PAT is configured. Teardown stays gated on SKIP_PROVISION to match the shared project status quo. Add the CI invoke payload under internal/tools/samples-hosted-agents and list the sample in the agent-framework README sample index. * Drop self-provision CI path and remove no-auth toolbox entry Revert hosted-agents-cloud-e2e.yml to origin/main: the self_provision flag was a net-new strategy with no precedent in CI. Toolbox samples run with SKIP_PROVISION=true and consume a toolbox pre-registered in the TOOLBOX_ENDPOINT repo variable (like langgraph-toolbox), so no PAT is provisioned in CI. Remove the public no-auth path from toolbox-auth-paths: an auth sample should only demonstrate authenticated paths. Toolbox now exposes only the key-based CustomKeys GitHub tool; Entra agent identity stays documented. Rework the sample README to be developer-runnable (azd provision or manual toolbox creation in your own project) and document the out-of-band CI registration without naming individuals. Interacting section now uses azd ai agent invoke --local. Trim payloads to the github prompts. * Remove redundant test-payload.txt from toolbox-auth-paths sample CI reads invoke prompts from internal/tools/samples-hosted-agents, and the build context strips test-payload.txt, so the sample-dir copy is unused. Drop it and fix the dangling README link. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rename github_pat secret parameter to gh_pat in toolbox-auth-paths GitHub forbids creating secrets/variables whose names start with the GITHUB_ prefix (case-insensitive), and azd resolves a manifest secret parameter from an env var of the same name. The github_pat parameter therefore could not be wired in the e2e Foundry project. Rename the parameter (and its {{ }} placeholder) to gh_pat, matching the existing GH_PAT secret already used by the cloud-e2e workflow. The scaffold step now exports both github_pat (other toolbox samples) and gh_pat. Docs updated; token-format mentions (github_pat_...) left intact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix gh_pat secret resolution in cloud-e2e scaffold (uppercase env var) azd ai agent init --no-prompt resolves a manifest secret parameter from an env var named after the parameter UPPERCASED (gh_pat -> GH_PAT). The scaffold step set a lowercase gh_pat env var, so azd could not find it and failed with 'unable to prompt for secret parameter gh_pat in no-prompt mode'. Set GH_PAT (uppercase) instead and correct the explanatory comment. The manifest parameter stays lowercase gh_pat; azd uppercases it to find GH_PAT. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use map form for gh_pat secret parameter so azd resolves it azd ai agent init --no-prompt only binds a manifest secret parameter to an env var when parameters use the map form (parameters: <name>:). The toolbox-auth-paths manifest used the array form (parameters: properties: - name:), where 'secret' is not a schema field, so azd recognized the parameter name but never looked up its env var — failing with 'unable to prompt for secret parameter gh_pat in no-prompt mode' regardless of the env var set. Switch to the map form (matching the browser-automation/langgraph-toolbox samples). Also export a lowercase gh_pat env var alongside GH_PAT so azd resolves the value whether it uppercases the parameter name or not. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Drop lowercase gh_pat env var (case-collides with GH_PAT) GitHub rejects a workflow whose env block defines names differing only by case (GH_PAT vs gh_pat) -> 'workflow file issue', no jobs run. Keep only the uppercase GH_PAT, which is what azd resolves the gh_pat secret parameter from (azd uppercases the parameter name). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Exclude toolbox-auth-paths x code-interpreter combo (known SDK bug) The csharp code-interpreter combo triggers an 'invalid_payload' on /tools/0/container (container serialized as null) — a known .NET Agent Framework SDK bug in Microsoft.Agents.AI.Foundry.Hosting that cannot be fixed from this repo. The matrix already excludes this combo for the sibling foundry-toolbox-server-side sample; add the new toolbox-auth-paths sample to the same exclusion so the matrix doesn't gate on the known defect. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rework toolbox-auth-paths to AddFoundryToolboxes (latest Hosting SDK) The sample pinned Microsoft.Agents.AI.Foundry.Hosting 1.3.0-preview and used GetToolboxToolsAsync() to pass the toolbox's tools to the agent as server-side MCP tools. Those tools default to AlwaysRequire approval, so the model's tool call returned an unhandled approval request and the hosted handler dropped it — the agent responded with an empty output (silent), even though CI marked it 'completed'. Bump to the latest 1.9.0-preview and adopt the AddFoundryToolboxes pattern (matching the AF repo Hosted-Toolbox sample). The hosting layer now connects to the toolbox MCP proxy at startup and exposes its tools as host-executed functions — no server-side approval gate — so the agent actually calls the tool and returns a real answer. Verified locally against a Foundry project: POST /responses now returns function_call + function_call_output + a text message (was output: []). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Derive toolset endpoint from project endpoint when not injected The previous run revealed the CI hosted container does NOT inject FOUNDRY_AGENT_TOOLSET_ENDPOINT, so AddFoundryToolboxes disabled toolbox support and the agent answered from the model alone (a hallucinated, toolless response that still passed CI). Derive FOUNDRY_AGENT_TOOLSET_ENDPOINT from FOUNDRY_PROJECT_ENDPOINT (<project>/toolboxes) when the platform doesn't set it, so the toolbox actually loads. Verified locally: with only FOUNDRY_PROJECT_ENDPOINT set, the host derives the toolset endpoint, discovers the toolbox tools, and POST /responses returns function_call + function_call_output + a text message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Work around toolbox token audience mismatch (cognitiveservices vs ai.azure.com) CI showed the toolbox failing to connect with 401 'audience is incorrect (https://ai.azure.com)'. The Hosting SDK's toolbox token handler requests a https://cognitiveservices.azure.com token, but the Foundry toolbox MCP proxy (both the test project and the CI project) requires https://ai.azure.com — so the toolbox silently failed to load and the agent answered from the model alone. Register a TokenCredential for the toolbox connection that maps the cognitiveservices.azure.com audience to ai.azure.com. Verified locally against a Foundry project: the toolbox now discovers its tools and POST /responses returns function_call + function_call_output + a text message grounded in the tool. This is a workaround for an SDK audience bug; the proper fix belongs in Microsoft.Agents.AI.Foundry.Hosting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump MAF Hosting to 1.10.0 and drop toolbox audience workaround Microsoft.Agents.AI.Foundry.Hosting 1.10.0-preview.260610.1 requests the correct https://ai.azure.com token audience for the Foundry toolbox MCP proxy, so the FoundryToolsetCredential audience-rewrite workaround is no longer needed. Removed the credential, its DI registration, and the now unused Azure.Core using. Verified locally against tao-foundry-prj: the toolbox discovers 51 tools and the agent answers a GitHub search prompt end-to-end (function_call + function_call_output + message), with no workaround. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9194f85 commit 73df0d7

9 files changed

Lines changed: 345 additions & 2 deletions

File tree

samples/csharp/hosted-agents/agent-framework/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@ This directory contains samples that demonstrate how to use the [Agent Framework
1515
| 5 | [text-search-rag](text-search-rag/) | A support agent with RAG capabilities using `TextSearchProvider`. |
1616
| 6 | [workflows](workflows/) | A multi-agent translation pipeline using `WorkflowBuilder`. |
1717
| 7 | [foundry-toolbox-server-side](foundry-toolbox-server-side/) | An agent that loads a Foundry Toolbox with `GetToolboxToolsAsync()` and passes its tools as server-side tools — Foundry executes them on the agent's behalf. |
18-
| 8 | [a2a/01-delegation](a2a/01-delegation/) | Two hosted agents — a math-expert executor exposed over A2A and a concierge caller that delegates to it through a Foundry Toolbox A2A connection. |
19-
| 9 | [foundry-toolbox-mcp-skills](foundry-toolbox-mcp-skills/) | An agent that discovers MCP-based skills from a Foundry Toolbox and exposes them to the agent via `AgentSkillsProvider` with progressive disclosure. |
18+
| 8 | [toolbox-auth-paths](toolbox-auth-paths/) | A multi-tool Foundry Toolbox demonstrating the authentication paths an MCP tool can use (key-based `CustomKeys`, public no-auth, and optional Entra agent identity) — all resolved server-side. |
19+
| 9 | [azure-search-rag](azure-search-rag/) | A support agent with RAG grounded in an Azure AI Search keyword index via `TextSearchProvider` over `Azure.Search.Documents`. |
20+
| 10 | [foundry-memory-rag](foundry-memory-rag/) | A personal-coach agent with persistent per-user memory that survives across requests and sessions using `FoundryMemoryProvider`. |
21+
| 11 | [file-tools](file-tools/) | An agent that answers questions over both image-baked bundled files and per-session uploaded files through scoped, security-hardened tools. |
22+
| 12 | [agent-skills](agent-skills/) | An agent that loads its behavioral guidelines from Foundry Skills (`SKILL.md`) at startup, so updates ship without code changes. |
23+
| 13 | [observability](observability/) | An instrumented agent demonstrating OpenTelemetry tracing, metrics, and logging for a hosted agent. |
24+
| 14 | [teams-activity](teams-activity/) | A hosted agent that can be deployed to Foundry and published to Teams, handling messages with file attachments and Teams/calendar questions. |
25+
| 15 | [a2a/01-delegation](a2a/01-delegation/) | Two hosted agents — a math-expert executor exposed over A2A and a concierge caller that delegates to it through a Foundry Toolbox A2A connection. |
26+
| 16 | [foundry-toolbox-mcp-skills](foundry-toolbox-mcp-skills/) | An agent that discovers MCP-based skills from a Foundry Toolbox and exposes them to the agent via `AgentSkillsProvider` with progressive disclosure. |
2027

2128
### Invocations API
2229

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
**/bin
2+
**/obj
3+
**/.vs
4+
**/.vscode
5+
**/.idea
6+
**/*.user
7+
**/.env
8+
**/.env.*
9+
!**/.env.example
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Foundry project endpoint — auto-injected in hosted containers.
2+
# Only set manually if running without `azd ai agent run`.
3+
# FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
4+
5+
# Model deployment name — must match a deployment in your Foundry project.
6+
AZURE_AI_MODEL_DEPLOYMENT_NAME=
7+
8+
# Toolbox name — must match a toolbox in your Foundry project.
9+
# When deploying with `azd provision`, the toolbox is created automatically
10+
# from the resources declared in agent.manifest.yaml.
11+
TOOLBOX_NAME=auth-paths-tools
12+
13+
# Note: the GitHub PAT for the key-based auth path is NOT a container env var.
14+
# It is declared as the `gh_pat` secret parameter in agent.manifest.yaml and
15+
# prompted by `azd ai agent init`; azd stores it in the Foundry connection only.
16+
17+
# Application Insights — auto-injected in hosted containers.
18+
# Set for local telemetry (optional but recommended).
19+
# APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
2+
WORKDIR /src
3+
COPY . .
4+
RUN dotnet restore
5+
RUN dotnet build -c Release --no-restore
6+
RUN dotnet publish -c Release --no-build -o /app
7+
8+
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
9+
WORKDIR /app
10+
COPY --from=build /app .
11+
EXPOSE 8088
12+
ENTRYPOINT ["dotnet", "toolbox-auth-paths.dll"]
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
/*
4+
* Foundry Toolbox — Auth Paths — Agent Framework Responses agent for C#
5+
*
6+
* Hosted agent that loads a Foundry Toolbox via AddFoundryToolboxes. The hosting
7+
* layer connects to the Foundry Toolboxes MCP proxy at startup, discovers the
8+
* toolbox's tools, and makes them available to the agent. The toolbox's MCP tools
9+
* authenticate to their upstream servers using different paths:
10+
*
11+
* 1. Key-based — a CustomKeys project connection injects a Bearer token (GitHub MCP).
12+
*
13+
* (Path 2 — Microsoft Entra agent identity — is documented in README.md as an optional
14+
* additive toolbox entry, because it requires a post-deploy RBAC grant on the agent's
15+
* managed identity before the toolbox can enumerate it.)
16+
*
17+
* The agent process carries NO auth logic. Foundry's toolbox proxy resolves each tool's
18+
* credential when it proxies the MCP call, so this code is identical regardless of which
19+
* auth path a given tool uses — the difference lives entirely in agent.manifest.yaml.
20+
*
21+
* Required environment variables:
22+
* FOUNDRY_PROJECT_ENDPOINT — Foundry project endpoint (auto-injected in hosted containers)
23+
* AZURE_AI_MODEL_DEPLOYMENT_NAME — Model deployment name (declared in agent.manifest.yaml)
24+
* TOOLBOX_NAME — Name of the Foundry Toolbox to load
25+
* FOUNDRY_AGENT_TOOLSET_ENDPOINT — Toolbox MCP proxy base URL (auto-injected in hosted containers;
26+
* set locally to "<project-endpoint>/toolboxes")
27+
*/
28+
29+
#pragma warning disable OPENAI001 // Foundry hosting APIs are experimental
30+
31+
using Azure.AI.AgentServer.Core;
32+
using Azure.AI.Projects;
33+
using Azure.Identity;
34+
using DotNetEnv;
35+
using Microsoft.Agents.AI;
36+
using Microsoft.Agents.AI.Foundry.Hosting;
37+
using Microsoft.Extensions.DependencyInjection;
38+
39+
// Load .env file if present (for local development)
40+
Env.TraversePath().Load();
41+
42+
var projectEndpoint = new Uri(Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
43+
?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT environment variable is not set."));
44+
45+
var deployment = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")
46+
?? throw new InvalidOperationException("AZURE_AI_MODEL_DEPLOYMENT_NAME environment variable is not set.");
47+
48+
var toolboxName = Environment.GetEnvironmentVariable("TOOLBOX_NAME")
49+
?? throw new InvalidOperationException("TOOLBOX_NAME environment variable is not set.");
50+
51+
// The agent is backed by the project's Responses API. It carries no tools directly —
52+
// the toolbox's tools are registered below via AddFoundryToolboxes and injected by the
53+
// hosting layer at request time as host-executed MCP tools.
54+
var projectClient = new AIProjectClient(projectEndpoint, new DefaultAzureCredential());
55+
56+
// Hosted containers inject FOUNDRY_AGENT_TOOLSET_ENDPOINT (the toolbox MCP proxy base).
57+
// When it is absent (local dev, or environments that don't inject it), derive it from the
58+
// project endpoint so AddFoundryToolboxes can still reach the toolbox proxy.
59+
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FOUNDRY_AGENT_TOOLSET_ENDPOINT")))
60+
{
61+
Environment.SetEnvironmentVariable(
62+
"FOUNDRY_AGENT_TOOLSET_ENDPOINT",
63+
$"{projectEndpoint.ToString().TrimEnd('/')}/toolboxes");
64+
}
65+
66+
AIAgent agent = projectClient
67+
.AsAIAgent(
68+
model: deployment,
69+
instructions: "You are a developer assistant with access to Foundry toolbox tools that reach "
70+
+ "external services over authenticated paths. Use the GitHub tools to "
71+
+ "search issues and pull requests. Be concise and cite the tool you used.",
72+
name: "toolbox-auth-paths",
73+
description: "Agent with an authenticated Foundry Toolbox using server-side tools.");
74+
75+
var builder = AgentHost.CreateBuilder(args);
76+
77+
builder.Services.AddFoundryResponses(agent);
78+
79+
// Register the Foundry Toolbox. The hosting layer connects to the toolbox MCP proxy at
80+
// startup (FOUNDRY_AGENT_TOOLSET_ENDPOINT, auto-injected by the platform), discovers the
81+
// toolbox's tools, and injects them into the agent. The platform proxy resolves each
82+
// tool's connection credential, so the agent runs under its own identity.
83+
builder.Services.AddFoundryToolboxes(options => options.ApiVersion = "v1", toolboxName);
84+
85+
builder.RegisterProtocol("responses", endpoints => endpoints.MapFoundryResponses());
86+
87+
var app = builder.Build();
88+
app.Run();
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Foundry Toolbox — Auth Paths
2+
3+
A Foundry Toolbox that demonstrates the **authentication paths** a toolbox MCP tool can use to reach its upstream server. Like [`foundry-toolbox-server-side`](../foundry-toolbox-server-side/), the agent loads the toolbox with `GetToolboxToolsAsync()` and passes the tools to `AsAIAgent(..., tools: ...)` as **server-side tools** — Foundry executes them on the agent's behalf.
4+
5+
The key idea: **the agent code carries no auth logic**. Foundry resolves each tool's credential server-side when it proxies the MCP call, so `Program.cs` is identical regardless of which path a tool uses. The difference lives entirely in [`agent.manifest.yaml`](agent.manifest.yaml).
6+
7+
## Auth path matrix
8+
9+
| # | Path | `authType` | Where the secret lives | Wired by default? |
10+
|---|------|-----------|------------------------|-------------------|
11+
| 1 | Key-based | `CustomKeys` | `gh_pat` secret parameter → Foundry connection secret store, injected as `Authorization: Bearer …` | ✅ Yes (GitHub MCP) |
12+
| 2 | Microsoft Entra agent identity | `AgenticIdentity` | No secret — the agent's managed identity gets an Entra token for the target `audience` | ➕ Optional (see below) |
13+
14+
> A third variant — embedding an `Authorization` header **inline** in the manifest — is an anti-pattern: the token is committed in plain text. Always prefer a `CustomKeys` connection (path 1) so the secret stays in the Foundry connection store.
15+
16+
## How auth is exercised
17+
18+
There is nothing auth-specific to run. Each prompt drives a GitHub tool, which drives the key-based path end to end:
19+
20+
- A GitHub answer means the **CustomKeys** PAT (path 1) resolved correctly.
21+
22+
A `401`/`403` from a tool means that path's credential did not resolve. Send any prompt that triggers a GitHub tool (e.g. *"What tools do you have available?"*) to exercise the path.
23+
24+
## All-or-nothing enumeration
25+
26+
`GetToolboxToolsAsync()` fetches **all** tool definitions at startup. If any configured source is misconfigured (bad PAT, missing RBAC, unreachable server), the fetch fails and the host does not start. When adding a new auth path, **validate one source at a time** — comment out the others in the manifest until each one enumerates cleanly.
27+
28+
## Adding auth path 2 — Microsoft Entra agent identity
29+
30+
Path 2 is documented rather than wired by default because it needs a post-deploy RBAC grant before the toolbox can enumerate it. To add it:
31+
32+
1. Add a parameter and an `AgenticIdentity` connection, and reference it from a third toolbox tool, in [`agent.manifest.yaml`](agent.manifest.yaml):
33+
34+
```yaml
35+
parameters:
36+
properties:
37+
- name: entra_audience
38+
secret: false
39+
description: Entra ID token audience for the target MCP server (e.g. https://cognitiveservices.azure.com).
40+
- name: entra_mcp_target
41+
secret: false
42+
description: URL of the Entra-protected MCP server that accepts agent-identity tokens.
43+
resources:
44+
- kind: connection
45+
name: entra-agent-conn
46+
category: RemoteTool
47+
authType: AgenticIdentity
48+
audience: "{{ entra_audience }}"
49+
target: "{{ entra_mcp_target }}"
50+
- kind: toolbox
51+
name: auth-paths-tools
52+
tools:
53+
# … existing github entry …
54+
- type: mcp
55+
server_label: entra
56+
project_connection_id: entra-agent-conn
57+
```
58+
59+
2. **Grant RBAC on the target before the first invoke.** After deploy, the agent gets a managed identity (its principal id changes each time the agent is recreated). Assign that identity the role the target MCP server requires (for an Azure Cognitive Services target: **Cognitive Services User** at the resource scope). Until this lands, the path-2 source fails to enumerate and bricks the whole toolbox.
60+
61+
## Provisioning the toolbox in your environment
62+
63+
The agent reads its toolbox from your Foundry project at startup, so the `auth-paths-tools` toolbox (and the `github-mcp-conn` connection that backs the key-based path) must exist in the project before you run. You have two ways to create them.
64+
65+
### Option 1 — `azd provision` (recommended)
66+
67+
`azd provision` reads [`agent.manifest.yaml`](agent.manifest.yaml) and creates the connection and toolbox for you:
68+
69+
```bash
70+
azd ai agent init # prompts once for the gh_pat secret parameter
71+
azd provision # creates github-mcp-conn (CustomKeys) + auth-paths-tools toolbox
72+
```
73+
74+
The `gh_pat` value is stored only in the Foundry connection secret store. It is never written to disk or passed to the container as an env var. Use a GitHub PAT (classic `ghp_...` or fine-grained `github_pat_...`) scoped to read the repositories your prompts ask about. Public-repo read is enough for the sample prompts.
75+
76+
### Option 2 — create the toolbox yourself
77+
78+
Create the same two resources in the [Foundry portal](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/toolbox) or in code with the [Foundry Toolbox CRUD sample](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/hosted_agents/sample_toolboxes_crud.py):
79+
80+
1. A `CustomKeys` connection named `github-mcp-conn`, target `https://api.githubcopilot.com/mcp`, key `Authorization` = `Bearer <your-pat>`.
81+
2. A toolbox named `auth-paths-tools` with one MCP tool: `github` (referencing `github-mcp-conn`).
82+
83+
Once the toolbox exists, set `TOOLBOX_NAME=auth-paths-tools` (already the manifest default) and run the agent.
84+
85+
## Continuous integration
86+
87+
The `hosted-agents-cloud-e2e` workflow treats this as a **toolbox sample** (its directory name contains `toolbox`) and runs with `SKIP_PROVISION=true`, so it does **not** run `azd provision` and never receives a PAT. Instead it consumes a toolbox that already exists in a shared Foundry project, the same way [`langgraph-toolbox`](../../../python/hosted-agents/bring-your-own/responses/langgraph-toolbox/) does. To enable it there, register an `auth-paths-tools` toolbox in that project (Option 2 above) and add one `label=url|query` line to the `TOOLBOX_ENDPOINT` repository variable:
88+
89+
```
90+
auth-paths=https://<account>.services.ai.azure.com/api/projects/<project>/toolboxes/auth-paths-tools/mcp?api-version=v1|<query that exercises the github tool>
91+
```
92+
93+
The workflow derives `TOOLBOX_NAME` from the URL slug (`.../toolboxes/auth-paths-tools/mcp`) and drives the toolbox with that query. Until the toolbox is registered, add a `.ci-skip` file in this directory to keep the sample out of the gated set.
94+
95+
## Running the Agent Host
96+
97+
Follow the instructions in the [Running the Agent Host Locally](../README.md#running-the-agent-host-locally) section of the parent README to run the agent host.
98+
99+
## Interacting with the agent
100+
101+
With the agent host running locally on `http://localhost:8088/`, use `azd ai agent invoke --local` to send a prompt:
102+
103+
```bash
104+
azd ai agent invoke --local "What tools do you have available?"
105+
azd ai agent invoke --local "Search the microsoft/agent-framework repo for open issues that mention hosted agents."
106+
```
107+
108+
A GitHub answer means the key-based **CustomKeys** path (path 1) resolved its PAT correctly. A `401`/`403` means the connection credential did not resolve.
109+
110+
> You can also invoke with `curl` (`Invoke-WebRequest` in PowerShell) against `http://localhost:8088/responses`, or use the **Agent Inspector** in the Foundry Toolkit VS Code extension. See the [parent README](../README.md) for details.
111+
112+
## Deploying the Agent to Foundry
113+
114+
To deploy the agent to Foundry, follow the instructions in the [Deploying the Agent to Foundry](../README.md#deploying-the-agent-to-foundry) section of the parent README.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/AgentManifest.yaml
2+
name: toolbox-auth-paths-dotnet-agent-framework
3+
displayName: "Foundry Toolbox — Auth Paths (.NET, Agent Framework)"
4+
description: >
5+
Agent Framework agent that loads a Foundry Toolbox demonstrating the
6+
authentication paths a toolbox MCP tool can use to reach its upstream server:
7+
key-based (CustomKeys connection) by default, with Microsoft Entra agent
8+
identity documented in the README as an optional additive toolbox entry. The
9+
Foundry platform resolves each tool's credential server-side when it proxies
10+
the MCP call, so the agent code carries no auth logic.
11+
metadata:
12+
tags:
13+
- AI Agent Hosting
14+
- Responses Protocol
15+
- Agent Framework
16+
- Toolbox
17+
- MCP
18+
- Authentication
19+
- .NET
20+
template:
21+
name: toolbox-auth-paths-dotnet-agent-framework
22+
kind: hosted
23+
protocols:
24+
- protocol: responses
25+
version: 1.0.0
26+
environment_variables:
27+
# FOUNDRY_PROJECT_ENDPOINT and APPLICATIONINSIGHTS_CONNECTION_STRING
28+
# are injected by the platform (hosted) and translated by azd (local)
29+
# — do NOT declare them here.
30+
- name: AZURE_AI_MODEL_DEPLOYMENT_NAME
31+
value: "{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}"
32+
- name: TOOLBOX_NAME
33+
value: "auth-paths-tools"
34+
parameters:
35+
gh_pat:
36+
secret: true
37+
description: GitHub Personal Access Token for the key-based (CustomKeys) auth path (classic ghp_... or fine-grained github_pat_...).
38+
resources:
39+
- kind: model
40+
id: gpt-4.1
41+
name: AZURE_AI_MODEL_DEPLOYMENT_NAME
42+
# Auth path 1 — key-based (CustomKeys). Foundry injects the PAT as a Bearer token when it
43+
# proxies the GitHub MCP call. The token lives only in this connection's secret store and
44+
# is never exposed to the agent container.
45+
- kind: connection
46+
name: github-mcp-conn
47+
category: RemoteTool
48+
authType: CustomKeys
49+
target: https://api.githubcopilot.com/mcp
50+
credentials:
51+
type: CustomKeys
52+
keys:
53+
Authorization: "Bearer {{ gh_pat }}"
54+
- kind: toolbox
55+
name: auth-paths-tools
56+
tools:
57+
# Auth path 1 — key-based via the CustomKeys connection above.
58+
- type: mcp
59+
server_label: github
60+
project_connection_id: github-mcp-conn
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
2+
kind: hosted
3+
name: toolbox-auth-paths-dotnet-agent-framework
4+
protocols:
5+
- protocol: responses
6+
version: 1.0.0
7+
resources:
8+
cpu: "0.25"
9+
memory: 0.5Gi
10+
environment_variables:
11+
- name: AZURE_AI_MODEL_DEPLOYMENT_NAME
12+
value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
13+
- name: TOOLBOX_NAME
14+
value: ${TOOLBOX_NAME}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<RootNamespace>ToolboxAuthPaths</RootNamespace>
6+
<AssemblyName>toolbox-auth-paths</AssemblyName>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
<NoWarn>$(NoWarn);NU1903;NU1605</NoWarn>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<!-- Agent Framework hosting — provides AgentHost, AddFoundryResponses, MapFoundryResponses,
14+
and AddFoundryToolboxes for registering Foundry Toolboxes as host-side MCP tools. -->
15+
<PackageReference Include="Microsoft.Agents.AI.Foundry.Hosting" Version="1.10.0-preview.260610.1" />
16+
<!-- .env file support for local development. -->
17+
<PackageReference Include="DotNetEnv" Version="3.1.1" />
18+
</ItemGroup>
19+
20+
</Project>

0 commit comments

Comments
 (0)