04 — Foundry Toolbox
Connect your hosted agent to a Foundry Toolbox — a managed collection of tools (Code Interpreter, File Search, and more) exposed via an MCP (Model Context Protocol) endpoint. Your agent gains powerful capabilities without you having to build or host them.
What you'll learn
- What a Foundry Toolbox is and how it works.
- Create a Toolbox with Code Interpreter and File Search in the Foundry portal.
- Connect to the Toolbox MCP endpoint using
MCPStreamableHTTPTool. - Authenticate with
get_bearer_token_providerand a customhttpx.Authclass. - Mix local tools and Toolbox tools in the same agent.
What is a Foundry Toolbox?
A Toolbox is a managed resource in your Foundry project that bundles one or more server-side tools behind an MCP-compliant endpoint. You create a Toolbox in the portal, add tools (like Code Interpreter or Web Search), and your agent connects to it over HTTP.
flowchart LR
Agent["Hosted Agent"] -->|MCP / Streamable HTTP| Toolbox["Foundry Toolbox"]
Toolbox --> CI["Code Interpreter"]
Toolbox --> FS["File Search (RAG)"]
Toolbox --> Custom["Custom tools (future)"]
Key properties:
| Feature | Detail |
|---|---|
| Protocol | MCP (Streamable HTTP transport) |
| Authentication | Azure AD bearer token (https://ai.azure.com/.default) |
| Required header | Foundry-Features: Toolboxes=V1Preview |
| Built-in tools | Code Interpreter, Web Search (Bing), File Search |
| Scope | Per Foundry project |
| Endpoint format | {project_endpoint}/toolboxes/{toolbox_name}/mcp?api-version=v1 |
Create the Toolbox
You create the Toolbox in the Foundry portal — and the vector store for File Search is created inline as part of adding that tool (there is no separate "create a vector store" step).
- Open your Foundry project (
<baseName>-project) → Build → Toolboxes → + Create toolbox. - Name →
workshop-toolbox(must match theTOOLBOX_NAMEenv var). - Under Included, click + Add and add each tool from the Configured tab:
- File search → in Attach files, choose Index option: Create a new index,
name it
workshop-guidelines, and uploaddata/clinical-guidelines.md. Wait for Success, then Attach. - Code interpreter → select it and Add tool (no configuration needed — sandboxed Python).
- File search → in Attach files, choose Index option: Create a new index,
name it
- Click Create to publish the first version.
Tool Search (optional)
The Tool search toggle enables the toolbox's toolbox_search_preview capability.
Instead of exposing every tool's full schema via the MCP tools/list (which grows
your model's context with each added tool), it exposes two meta-tools — tool_search
and call_tool — so the agent searches for the right tool on demand. Microsoft
recommends turning it on once a toolbox has more than ~5 tools; for this two-tool
lesson it's optional and works either way (leaving it off keeps tool discovery the
most direct).
Toolbox name → TOOLBOX_NAME
Add the toolbox name to your workspace .env for local runs:
Project structure
examples/04-toolbox/
├── main.py ← agent with Toolbox MCP integration
├── Dockerfile
├── .dockerignore
├── requirements.txt
└── data/
└── clinical-guidelines.md ← sample doc for File Search
azure.yaml is generated by azd ai agent init
As in lessons 01–03, the hosting manifest is generated by init and git-ignored —
there is no committed agent.yaml. For this lesson you add one extra env var
(TOOLBOX_NAME) to the generated azure.yaml (see Try it).
The code
"""Lesson 04 — Foundry Toolbox."""
import os
from typing import Annotated
import httpx
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from dotenv import load_dotenv
from pydantic import Field
from agent_framework import Agent, MCPStreamableHTTPTool, tool
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
load_dotenv()
credential = DefaultAzureCredential()
# The hosted runtime injects FOUNDRY_PROJECT_ENDPOINT; fall back to
# AZURE_AI_PROJECT_ENDPOINT for local .env-based runs.
PROJECT_ENDPOINT = os.environ.get("FOUNDRY_PROJECT_ENDPOINT") or os.environ["AZURE_AI_PROJECT_ENDPOINT"]
client = FoundryChatClient(
project_endpoint=PROJECT_ENDPOINT,
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=credential,
)
_TOOLBOX_FEATURES = "Toolboxes=V1Preview"
class _ToolboxAuth(httpx.Auth):
"""httpx Auth that injects a fresh bearer token on every request."""
def __init__(self, token_provider) -> None:
self._get_token = token_provider
def auth_flow(self, request: httpx.Request):
request.headers["Authorization"] = f"Bearer {self._get_token()}"
yield request
def resolve_toolbox_endpoint() -> str:
toolbox_name = os.environ["TOOLBOX_NAME"]
return f"{PROJECT_ENDPOINT.rstrip('/')}/toolboxes/{toolbox_name}/mcp?api-version=v1"
token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
http_client = httpx.AsyncClient(
auth=_ToolboxAuth(token_provider),
headers={"Foundry-Features": _TOOLBOX_FEATURES},
timeout=120.0,
)
toolbox = MCPStreamableHTTPTool(
name="toolbox",
url=resolve_toolbox_endpoint(),
http_client=http_client,
load_prompts=False,
)
@tool(approval_mode="never_require")
def summarize_findings(
findings: Annotated[str, Field(description="The findings text to summarize")],
) -> str:
"""Summarize a set of findings into a concise bullet list."""
return f"Summary of findings:\n{findings}"
async def main() -> None:
agent = Agent(
client=client,
instructions=(
"You are a healthcare research assistant with access to a Foundry Toolbox. "
"Use the File Search tool to look up clinical guidelines and evidence-based recommendations. "
"Use the Code Interpreter tool to run Python code for data analysis and calculations. "
"Use the summarize_findings tool to compile your results. "
"Always cite sources and remind the user your answers are informational only."
),
tools=[summarize_findings, toolbox],
default_options={"store": False},
)
server = ResponsesHostServer(agent)
async with agent:
await server.run_async()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Step-by-step walkthrough
1. Toolbox authentication
_TOOLBOX_FEATURES = "Toolboxes=V1Preview"
class _ToolboxAuth(httpx.Auth):
def __init__(self, token_provider):
self._get_token = token_provider
def auth_flow(self, request):
request.headers["Authorization"] = f"Bearer {self._get_token()}"
yield request
token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
http_client = httpx.AsyncClient(
auth=_ToolboxAuth(token_provider),
headers={"Foundry-Features": _TOOLBOX_FEATURES},
timeout=120.0,
)
The Toolbox MCP endpoint requires:
- An Azure AD bearer token scoped to
https://ai.azure.com/.default. - The feature header
Foundry-Features: Toolboxes=V1Previewon every request.
get_bearer_token_provider handles token caching and refresh automatically. The custom httpx.Auth class injects the token into each outbound request.
2. Build the Toolbox endpoint URL
def resolve_toolbox_endpoint() -> str:
toolbox_name = os.environ["TOOLBOX_NAME"]
return f"{PROJECT_ENDPOINT.rstrip('/')}/toolboxes/{toolbox_name}/mcp?api-version=v1"
PROJECT_ENDPOINT is resolved once from FOUNDRY_PROJECT_ENDPOINT (with an AZURE_AI_PROJECT_ENDPOINT fallback). The ?api-version=v1 query parameter is required — omitting it returns a 400 error.
3. Create the MCP tool
toolbox = MCPStreamableHTTPTool(
name="toolbox",
url=resolve_toolbox_endpoint(),
http_client=http_client,
load_prompts=False,
)
MCPStreamableHTTPTool is MAF's built-in MCP client. It connects to the Toolbox and automatically discovers all available tools (Code Interpreter, Web Search, etc.).
load_prompts=False— required; the Toolbox MCP server does not implementprompts/list.- The
http_clientcarries both the authentication and the feature header.
4. Mix local and Toolbox tools
You can combine local @tool functions and MCP tools in the same tools list. The agent sees all of them as callable tools.
5. Async context manager
MCP tools require async initialization (to discover available tools from the server). The async with agent context manager connects to the Toolbox, discovers tools, then starts the server. On exit it cleanly disconnects.
6. azure.yaml with TOOLBOX_NAME
The generated azure.yaml maps AZURE_AI_MODEL_DEPLOYMENT_NAME automatically. This lesson needs one more variable — add TOOLBOX_NAME to the agent service's environmentVariables:
environmentVariables:
- name: AZURE_AI_MODEL_DEPLOYMENT_NAME
value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
- name: TOOLBOX_NAME
value: ${TOOLBOX_NAME}
The project endpoint is not listed here — the runtime injects FOUNDRY_PROJECT_ENDPOINT automatically (see lesson 01).
Try it
Generate the project manifest with azd ai agent init
Bind to your existing project and ACR (see lesson 01 for the full wizard walkthrough):
cd examples/04-toolbox
export BASE_NAME=<your-unique-name>
export RESOURCE_GROUP=rg-foundry-advanced-workshop
PROJECT_ID="$(az cognitiveservices account show \
--name $BASE_NAME --resource-group $RESOURCE_GROUP \
--query id -o tsv)/projects/${BASE_NAME}-project"
azd ai agent init \
--agent-name toolbox-agent \
--project-id "$PROJECT_ID" \
--deploy-mode container \
--model-deployment gpt-5-mini
Add TOOLBOX_NAME (this lesson's extra step)
init only wires AZURE_AI_MODEL_DEPLOYMENT_NAME. Add TOOLBOX_NAME to the agent's
environmentVariables in the generated azure.yaml (see step 6),
then register it in the azd environment so it's injected at deploy time:
For local runs, TOOLBOX_NAME is also picked up from your workspace .env.
Run locally
Toolbox tools require cloud connectivity
Unlike lessons 01–03, the Toolbox MCP endpoint is a remote service. Even when running locally, your agent makes outbound calls to the Toolbox endpoint. Ensure you are authenticated (az login) and the Toolbox exists in your project.
Invoke (in a separate terminal)
cd examples/04-toolbox
azd ai agent invoke --local "Use Code Interpreter to calculate the average BMI from this data: weights = [72, 85, 68, 95, 78], heights = [1.75, 1.82, 1.60, 1.90, 1.68]"
Expected: the agent writes and executes Python code, returns the computed averages.
File Search:
azd ai agent invoke --local "What are the LDL-C targets for a very high risk patient with established cardiovascular disease?"
Expected: the agent searches the clinical guidelines document and returns the target (< 1.4 mmol/L and ≥ 50% reduction).
Combining both tools:
azd ai agent invoke --local "Look up the CHA2DS2-VASc scoring criteria, then use code interpreter to calculate the score for a 70-year-old woman with hypertension and diabetes."
Expected: the agent retrieves the scoring table via File Search, then uses Code Interpreter to compute the score.
Deploy to the cloud
This builds the container remotely, pushes to ACR, and registers a new agent version. No manual agent-identity role assignment is needed — the Bicep template's Foundry User grant on the project identity also lets the deployed agent reach the Toolbox (see lesson 01).
Then invoke remotely:
azd ai agent invoke "What are the first-line medications for hypertension according to the guidelines? Use Code Interpreter to create a comparison table."
Key takeaways
- A Toolbox bundles managed tools (Code Interpreter, File Search, Web Search) behind an MCP endpoint.
- You create the Toolbox in the portal; File Search's vector index is created inline when you add that tool.
MCPStreamableHTTPTooldiscovers and connects to Toolbox tools automatically.- Authentication uses
get_bearer_token_providerwith scopehttps://ai.azure.com/.default. - The
Foundry-Features: Toolboxes=V1Previewheader is required on all requests. - The endpoint URL must include
?api-version=v1, andload_prompts=Falseis mandatory. - Add
TOOLBOX_NAMEto the agent'senvironmentVariablesinazure.yamlandazd env setit. - Tool Search (
toolbox_search_preview) keeps context flat for large toolboxes — optional here. - Local
@toolfunctions and Toolbox MCP tools can be mixed in the same agent.