Python: Add Foundry Toolbox MCP skills hosted agent sample (#6363)

* Add 12_foundry_toolbox_mcp_skills hosted agent sample

Demonstrates using MCPSkillsSource with a Foundry Toolbox MCP endpoint
to discover and serve skills via SkillsProvider (progressive disclosure).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix env var reference in README and reuse local var in main.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Require AZURE_AI_MODEL_DEPLOYMENT_NAME and use placeholder in .env.example

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document Toolbox MCP skills vs Foundry Skills in sample README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Reference 12_foundry_toolbox_mcp_skills in parent README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: SergeyMenshykh <SergeMenshikh@outlook.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
SergeyMenshykh
2026-06-09 09:38:19 +01:00
committed by GitHub
Unverified
parent cfb033e5d4
commit caa75f7cdd
9 changed files with 264 additions and 1 deletions
@@ -19,7 +19,8 @@ This directory contains samples that demonstrate how to use hosted [Agent Framew
| 9 | [Foundry Skills](responses/09_foundry_skills/) | An agent that uploads `SKILL.md` files to the Foundry Skills REST API and downloads them at startup, decoupling tone/policy guidelines from agent code. |
| 10 | [Foundry Memory](responses/10_foundry_memory/) | An agent with persistent semantic memory backed by an Azure AI Foundry Memory Store, using `FoundryMemoryProvider` to remember user facts across sessions. |
| 11 | [Monty CodeAct](responses/11_monty_codeact/) | An agent with a Monty-backed CodeAct context provider, exposing a single `execute_code` tool that runs Python in a [pydantic-monty](https://github.com/pydantic/monty) interpreter and invokes typed host tools (`compute`, `fetch_data`) from inside the sandbox. Uses the alpha `agent-framework-monty` package. |
| 12 | [Using deployed agent](responses/using_deployed_agent.py) | A sample demonstrating how to invoke an agent that has already been deployed to Foundry, showing how to interact with a hosted agent in code. |
| 12 | [Foundry Toolbox MCP Skills](responses/12_foundry_toolbox_mcp_skills/) | An agent that discovers MCP-based skills attached to a Foundry Toolbox and serves them via `SkillsProvider(MCPSkillsSource(...))`, fetching `SKILL.md` bodies and supplementary resources on demand. |
| 13 | [Using deployed agent](responses/using_deployed_agent.py) | A sample demonstrating how to invoke an agent that has already been deployed to Foundry, showing how to interact with a hosted agent in code. |
### Invocations API
@@ -0,0 +1,7 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
.env
@@ -0,0 +1,3 @@
FOUNDRY_PROJECT_ENDPOINT="..."
AZURE_AI_MODEL_DEPLOYMENT_NAME="..."
TOOLBOX_NAME="..."
@@ -0,0 +1,18 @@
FROM python:3.12-slim
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . user_agent/
WORKDIR /app/user_agent
RUN if [ -f requirements.txt ]; then \
pip install -r requirements.txt; \
else \
echo "No requirements.txt found"; \
fi
EXPOSE 8088
CMD ["python", "main.py"]
@@ -0,0 +1,80 @@
# What this sample demonstrates
An [Agent Framework](https://github.com/microsoft/agent-framework) agent that discovers **MCP-based skills from a Foundry Toolbox** and makes them available via `SkillsProvider(MCPSkillsSource(...))`, hosted using the **Responses protocol**.
The `SkillsProvider` is attached to the agent as a context provider and implements the [Agent Skills](https://agentskills.io/) progressive-disclosure pattern. When the agent is prompted, it discovers available skills in the Foundry Toolbox via the provider:
1. **Advertise** — skill names and descriptions are injected into the system prompt so the agent knows what is available.
2. **Load** — when the agent decides a skill is relevant, it retrieves the full skill body with detailed instructions via the provider.
3. **Read resources** — if a skill includes supplementary content (reference documents, assets), the agent reads them on demand via the provider.
This way the full skill body and resources are only loaded when the agent actually needs them, reducing token usage.
## Toolbox MCP skills vs. Foundry Skills
Foundry exposes skills in two ways, and this sample uses the second one.
Foundry Skills are managed through the Foundry Skills REST API. An agent downloads each `SKILL.md` as a ZIP at startup, serving the bodies from local files. See the [`09_foundry_skills`](../09_foundry_skills/README.md) sample for a demonstration.
Toolbox MCP skills are accessed through a toolbox over the MCP protocol. A toolbox bundles a curated set of skills behind one MCP endpoint, and any MCP client discovers them automatically. Skill bodies and any supplementary resources are fetched on demand.
## How It Works
### Model Integration
The agent uses `FoundryChatClient` from the Agent Framework to create an OpenAI-compatible Responses client. It connects to the toolbox's MCP endpoint via the `mcp` library's `streamable_http_client`, discovers skills served by the toolbox through `MCPSkillsSource`, and injects them as a context provider via `SkillsProvider`. The toolbox endpoint URL is derived from `FOUNDRY_PROJECT_ENDPOINT` and `TOOLBOX_NAME`.
See [main.py](main.py) for the full implementation.
### Agent Hosting
The agent is hosted using the [Agent Framework](https://github.com/microsoft/agent-framework) with the `ResponsesHostServer`, which provisions a REST API endpoint compatible with the OpenAI Responses protocol.
## Prerequisites
- Python 3.12+
- An Azure AI Foundry project with a deployed model (e.g., `gpt-5`)
- A Foundry Toolbox with skills attached (see below)
- Azure CLI logged in (`az login`)
## Setting up a Foundry Toolbox with skills
This sample requires a Foundry Toolbox that has skills attached to it. Skills are `SKILL.md` files you author once, store centrally in Foundry through the versioned Skills API, and attach to a toolbox so any MCP client can discover and load them.
1. **Author a skill** — Create a `SKILL.md` file following the [Agent Skills](https://agentskills.io/) specification format (YAML front matter with `name` and `description`, plus Markdown body).
2. **Create the skill in Foundry** — Upload the skill via the Skills REST API, Python SDK, or `azd ai skill create`. See [Use skills with Microsoft Foundry agents](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/skills).
3. **Attach the skill to a toolbox** — Add a skill reference to a toolbox version so MCP clients can discover it. See [Attach skills to a toolbox](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/toolbox#attach-skills-to-a-toolbox).
When the agent connects to the toolbox MCP endpoint, skills are advertised through a well-known `skill://index.json` discovery resource. The `MCPSkillsSource` in this sample reads `skill://index.json` the first time the agent runs to discover all attached skills, then fetches each `SKILL.md` body on demand via `resources/read`.
## Running the Agent Host
Follow the instructions in the [Running the Agent Host Locally](../../README.md#running-the-agent-host-locally) section of the README in the parent directory to run the agent host.
An extra environment variable must be set to point to the toolbox name:
```bash
export TOOLBOX_NAME="my-toolbox"
```
Or in PowerShell:
```powershell
$env:TOOLBOX_NAME="my-toolbox"
```
You can also place these in a `.env` file next to `main.py` — see [`.env.example`](.env.example).
## Interacting with the agent
> Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent.
Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What skills do you have available?"}'
```
## Deploying the Agent to Foundry
To host the agent on Foundry, follow the instructions in the [Deploying the Agent to Foundry](../../README.md#deploying-the-agent-to-foundry) section of the README in the parent directory.
@@ -0,0 +1,42 @@
name: hosted-toolbox-mcp-skills
displayName: "Hosted Toolbox MCP Skills Agent"
description: >
A hosted agent that discovers MCP-based skills from a Foundry Toolbox
and makes them available to the agent via the agent skills provider.
metadata:
tags:
- AI Agent Hosting
- Azure AI AgentServer
- Responses Protocol
- Agent Framework
- MCP
- Model Context Protocol
- Agent Skills
- Foundry Toolbox
- Foundry Toolbox Skills
template:
name: hosted-toolbox-mcp-skills
kind: hosted
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
environment_variables:
- name: AZURE_AI_MODEL_DEPLOYMENT_NAME
value: "{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}"
- name: TOOLBOX_NAME
value: "{{TOOLBOX_NAME}}"
parameters:
properties:
- name: TOOLBOX_NAME
secret: false
description: Name of the Foundry Toolbox to connect to for MCP skill discovery
resources:
- kind: model
id: gpt-5
name: AZURE_AI_MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,14 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
kind: hosted
name: hosted-toolbox-mcp-skills
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
environment_variables:
- name: AZURE_AI_MODEL_DEPLOYMENT_NAME
value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
- name: TOOLBOX_NAME
value: ${TOOLBOX_NAME}
@@ -0,0 +1,94 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from collections.abc import Callable, Generator
import httpx
from agent_framework import Agent, MCPSkillsSource, SkillsProvider
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from dotenv import load_dotenv
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamable_http_client
# Load environment variables from .env file
load_dotenv()
class ToolboxAuth(httpx.Auth):
"""Attach a fresh Foundry bearer token to every request."""
def __init__(self, token_provider: Callable[[], str]):
self._get_token = token_provider
def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:
request.headers["Authorization"] = f"Bearer {self._get_token()}"
yield request
async def main() -> None:
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
toolbox_name = os.environ["TOOLBOX_NAME"]
# Build the Toolbox MCP URL from the project endpoint and toolbox name.
toolbox_mcp_url = f"{project_endpoint.rstrip('/')}/toolboxes/{toolbox_name}/mcp?api-version=v1"
credential = DefaultAzureCredential()
# Create a token provider for Foundry bearer auth
token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
# ── Connect to the Foundry Toolbox MCP endpoint ──────────────────────────
# Create an HTTP client that attaches a fresh Foundry bearer token to every
# request and advertises the toolbox preview feature flag.
async with (
httpx.AsyncClient(
auth=ToolboxAuth(token_provider),
headers={"Foundry-Features": "Toolboxes=V1Preview"},
timeout=httpx.Timeout(30.0, read=300.0),
follow_redirects=True,
) as http_client,
streamable_http_client(
url=toolbox_mcp_url,
http_client=http_client,
) as (read, write, _),
ClientSession(read, write) as session,
):
await session.initialize()
print(f"Connected to Foundry Toolbox '{toolbox_name}' MCP server.")
# ── Configure MCP-based skills provider ──────────────────────────────
# MCPSkillsSource reads skill://index.json and creates one MCPSkill per
# skill-md entry; SKILL.md bodies are fetched on demand via
# resources/read.
skills_provider = SkillsProvider(MCPSkillsSource(client=session))
# ── Create the agent ─────────────────────────────────────────────────
client = FoundryChatClient(
project_endpoint=project_endpoint,
model=deployment,
credential=credential,
)
agent = Agent(
client=client,
name=os.environ.get("AGENT_NAME", "hosted-toolbox-mcp-skills"),
instructions="You are a helpful assistant.",
context_providers=[skills_provider],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
# ── Build and run the host ───────────────────────────────────────────
server = ResponsesHostServer(agent)
await server.run_async()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,4 @@
agent-framework
agent-framework-foundry-hosting
mcp>=1.24.0,<2