Sofia Lindström
August 23, 2026
8 min read
Microsoft pushed a batch of updates into Azure AI Foundry this month that change how the platform is actually used. As of August 2026, Claude models hosted inside Microsoft Foundry now support structured outputs, web search, web fetch, MCP connectors, and tool search natively, turning what used to be a bare model endpoint into something closer to a full agent platform. If you’ve been putting off learning Azure AI Foundry because the docs felt scattered across a dozen half-finished preview pages, this is the moment it stopped being a moving target. This tutorial walks through building, testing, and deploying a real agent from a blank resource group to a running Azure Container Apps instance, using the exact tools and settings available right now.
You’ll create a Foundry project, deploy a model from the catalog, wire up structured outputs so your agent returns clean JSON instead of chatty prose, connect web search and a custom MCP server, orchestrate a multi-step workflow with the Microsoft Agent Framework, and ship the whole thing to production with checkpointing so it survives a crash mid-task. Twelve steps, roughly 100 minutes if you follow along, and a working project you can extend afterward.
Don’t miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
What Is Azure AI Foundry, and Why It’s Different in August 2026
Azure AI Foundry is Microsoft’s unified platform for discovering, testing, customizing, and deploying AI models and agents inside Azure. It bundles a model catalog spanning OpenAI, Anthropic Claude, Meta Llama, Mistral, and Microsoft’s own models, a hosted Agent Service for building and running agents without managing your own inference infrastructure, and a set of governance tools (identity, policy, observability) that IT teams actually need before they’ll let an agent touch production data. The pitch has always been “one place to build AI apps on Azure,” but for most of 2025 that meant stitching together separate services yourself.
What changed is the tooling around the model, not just the model. According to Microsoft’s Agent Factory blog series, the platform is explicitly being repositioned around turning a model endpoint into a production agent platform, with tool calling, retrieval, and orchestration built in rather than bolted on. That’s the frame for this whole tutorial: you’re not just calling a chat completion endpoint, you’re assembling an agent that can search the web, call your own APIs through MCP, and hand off work to other agents when a task gets complex.
The other shift worth knowing about before you start: Fabric’s OneLake now integrates directly with Azure AI Foundry for retrieval-augmented generation, so if your organization already has data cataloged in Fabric, you don’t need a separate vector database just to ground an agent in enterprise knowledge. We won’t build a full RAG pipeline in this tutorial, but it’s worth knowing that door exists once you’re past the basics covered here.
Understanding the Hub-and-Project Architecture
Before you create anything in the portal, it helps to understand how Foundry organizes resources, because the naming trips people up constantly. A Foundry resource (what older documentation calls a “hub”) is the top-level container tied to a specific Azure region and a specific set of network and identity settings. It’s where billing, private networking, and connections to other Azure services like storage accounts and Key Vault get configured once. A project lives inside that resource and is where the actual work happens: model deployments, agents, threads, and evaluation runs. One resource can hold multiple projects, which is the pattern most teams land on once they have more than one agent in flight, since it means shared networking and identity config without duplicating security review for every new project.
The practical implication for this tutorial: everything you build lives inside one project, but if you later spin up a second agent for a different team, you don’t need a second resource group and a second set of firewall rules. You just create a second project under the same Foundry resource. Keep that distinction in mind when you hit Azure RBAC errors later — permissions can be scoped at either the resource level or the project level, and a role assigned at the wrong layer is a common source of confusing “you don’t have access” errors that look like authentication bugs but are actually authorization scope mismatches.
What’s New This Month: Structured Outputs, Web Tools, and MCP for Claude Models
The headline change for August 2026, and the reason this tutorial exists now rather than six months ago, is that Microsoft Foundry added five capabilities to Claude models hosted on Azure in a single update: structured outputs, web search, web fetch, MCP connector support, and tool search. Before this, if you wanted an Anthropic model with tool use on Azure, you were working around gaps that OpenAI-hosted models on the same platform didn’t have. That parity gap is now closed, at least for these five features.
Practically, here’s what each one buys you in this tutorial:
- Structured outputs — force the model to return JSON that matches a schema you define, instead of parsing free text and hoping for the best.
- Web search — the agent can pull current information into its context without you building a separate search integration.
- Web fetch — the agent can retrieve and read a specific URL’s content on demand.
- MCP connector — the agent can call tools exposed through the Model Context Protocol, an open standard originally from Anthropic that’s now widely adopted for connecting AI agents to external systems.
- Tool search — when an agent has access to a large catalog of tools, tool search lets it find the right one dynamically instead of you hardcoding every tool into every prompt.
Also worth flagging: Microsoft’s “What’s new” documentation for Azure AI Foundry was last refreshed on August 22, 2026, and now includes a walkthrough for deploying Private Agentic Retrieval, a pattern for keeping retrieval calls inside your private network boundary. And a new AI Model Marketplace, which lets teams discover, purchase, and integrate pre-trained models directly inside Foundry, shipped this same month. We’ll stick to the free-tier model catalog for this tutorial, but the marketplace is worth a look if you need a specialized third-party model later.
Prerequisites: Accounts, Tools, and Exact Versions You Need
Before you touch the Azure portal, get your local environment sorted. Mismatched CLI versions are the single most common reason this kind of tutorial breaks halfway through, so check versions before you start, not after something fails.
| Tool / Account | Minimum Version | Why You Need It |
|---|---|---|
| Azure subscription | Pay-as-you-go or free trial with quota | Hosts the Foundry resource, model deployments, and Container Apps |
| Azure CLI | 2.65 or later | Creates resource groups and Foundry resources from the terminal |
| Azure Developer CLI (azd) | Latest release | Handles the azd up deployment flow in Step 11 |
| Python | 3.10 or later | Runs the Foundry SDK, agent code, and local testing scripts |
| Docker Desktop or Docker Engine | Latest stable | Builds the container image for Azure Container Apps |
| Visual Studio Code | Latest release, with Azure AI Foundry extension | Optional but strongly recommended for the Agent Service portal-free workflow |
| .NET SDK (optional) | .NET Aspire 9.2 | Only needed if you follow the Aspire-based deployment path instead of the Python/Docker path |
You’ll also need permission to create re and, if your organization has Azure Policy locked down, confirmation that AI Foundry reck with your Azure admin before Step 1 rather than after you hit a policy denial error
Two more things worth confirming before you start the clock on this tutorial. First, check your subscription’s quota for the specific model you plan to deploy; Claude and other high-demand models are often subject to tokens-per-minute quota limits that are lower by default than what a production workload needs, and requesting an increase can take anywhere from a few minutes to a day depending on your subscription tier. Second, if you’re working from a locked-down corporate laptop, confirm outbound HTTPS access to *.services.ai.azure.com isn’t blocked by a proxy or firewall policy, since that’s the endpoint pattern the Foundry SDK talks to and a silent network block looks identical to an authentication failure until you dig into it.
Step 1: Create Your Azure AI Foundry Re
Everything in Foundry lives inside a project, and every project lives inside a Foundry re. Start from the terminal so the whole setup is reproducible
# Log in and set your target subscription
az login
az account set --subscription "YOUR_SUBSCRIPTION_ID"
# Create a resource group
az group create --name rg-foundry-tutorial --location eastus2
# Create the Azure AI Foundry resource
az cognitiveservices account create
--name foundry-tutorial-resource
--resource-group rg-foundry-tutorial
--kind AIServices
--sku S0
--location eastus2
--custom-domain foundry-tutorial-resource
# Create a project inside that resource
az ml workspace create
--kind project
--name foundry-tutorial-project
--resource-group rg-foundry-tutorial
--hub-id foundry-tutorial-resource
If you’d rather click through the UI, the Azure AI Foundry portal walks you through the same steps with a project creation wizard, and it’s the faster path the first time you do this since it validates region and quota availability before you commit. Either way, note the project’s endpoint URL once it’s created; you’ll need it in Step 3.
Pick your region carefully. Not every model in the catalog is available in every Azure region, and Claude models in particular are only deployed in a subset of regions. Check the official Azure AI Foundry documentation for current regional availability before you commit to a reon isn’t a simple config change
Step 2: Deploy a Model From the Foundry Model Catalog
With the project created, open the Model Catalog inside the Foundry portal (or use the CLI) and deploy the model you’ll build the agent around. For this tutorial we’re using a Claude model specifically because it’s the one that just picked up structured outputs, web search, web fetch, MCP connector, and tool search support this month, so you get to exercise every new feature in one build.
az ml online-endpoint create
--name claude-agent-endpoint
--resource-group rg-foundry-tutorial
--workspace-name foundry-tutorial-project
az ml online-deployment create
--name claude-deployment
--endpoint claude-agent-endpoint
--model azureml://registries/azure-ai-model-catalog/models/Claude
--resource-group rg-foundry-tutorial
--workspace-name foundry-tutorial-project
Deployment usually takes a few minutes. Once it’s live, go to the Foundry portal’s Playground and send a test prompt before writing any code. This confirms the deployment is actually serving traffic and lets you sanity-check latency before you build orchestration logic on top of it. If the playground response looks reasonable, copy the deployment name and endpoint; both go into your environment variables next.
Step 3: Set Up Your Local Python Environment
Now move to your local machine and set up the SDK. Keep credentials out of your code; use environment variables or a .env file that’s excluded from version control.
python -m venv foundry-env
source foundry-env/bin/activate # Windows: foundry-envScriptsactivate
pip install azure-ai-projects azure-ai-inference azure-identity python-dotenv
# .env file
cat > .env << 'EOF'
AZURE_AI_PROJECT_ENDPOINT=https://foundry-tutorial-project.services.ai.azure.com
AZURE_AI_MODEL_DEPLOYMENT=claude-deployment
EOF
Authenticate with DefaultAzureCredential rather than an API key wherever possible. It picks up your Azure CLI login locally and switches cleanly to a managed identity once you deploy to Container Apps in Step 11, so you’re not maintaining two auth paths for dev and prod.
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
import os
from dotenv import load_dotenv
load_dotenv()
project_client = AIProjectClient(
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
credential=DefaultAzureCredential(),
)
print("Connected to project:", project_client._config.endpoint)
Run that script. If it prints your endpoint without throwing an authentication error, you’re set up correctly and ready to build the actual agent.
Step 4: Build Your First Agent With Foundry Agent Service
Foundry Agent Service manages the agent’s state, conversation threads, and tool-calling loop for you, so you don’t have to hand-roll a while loop that checks for tool calls and re-prompts the model. Create an agent with a system instruction and no tools yet — we’ll add those in the next two steps.
agent = project_client.agents.create_agent(
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT"],
name="support-ticket-triage-agent",
instructions=(
"You triage incoming support tickets. Classify severity, "
"identify the affected product area, and draft a first response. "
"Always return structured data, never free-form prose."
),
)
thread = project_client.agents.create_thread()
project_client.agents.create_message(
thread_id=thread.id,
role="user",
content="Ticket: 'Our checkout page has been throwing 500 errors for the last 20 minutes, we're losing sales.'",
)
run = project_client.agents.create_run(thread_id=thread.id, agent_id=agent.id)
This creates the skeleton: an agent, a conversation thread, and a run. Right now the agent will respond in prose because we haven’t constrained its output format. That’s the next step.
Step 5: Add Structured Outputs to Force Reliable JSON
This is the feature that shipped this month for Claude models on Foundry, and it’s the one that matters most if you’re wiring an agent into any downstream system. Instead of parsing prose with regex and hoping the model didn’t change its phrasing, you define a schema and the model is constrained to match it.
from pydantic import BaseModel
from typing import Literal
class TicketTriage(BaseModel):
severity: Literal["low", "medium", "high", "critical"]
product_area: str
requires_immediate_escalation: bool
draft_response: str
agent = project_client.agents.create_agent(
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT"],
name="support-ticket-triage-agent",
instructions="Triage incoming support tickets using the provided schema.",
response_format={
"type": "json_schema",
"json_schema": {
"name": "ticket_triage",
"schema": TicketTriage.model_json_schema(),
},
},
)
Run the same test ticket through this version of the agent and the output changes shape entirely. Here’s a realistic example of what comes back:
{
"severity": "critical",
"product_area": "checkout",
"requires_immediate_escalation": true,
"draft_response": "We've identified an issue affecting checkout and are actively investigating. We'll update you within 15 minutes."
}
That’s a payload you can pipe straight into a ticketing system’s API without writing a single parsing rule. This is the difference structured outputs makes in practice: it turns the model into a component you can wire into existing software rather than a chatbot you paste responses out of.
Step 6: Wire Up Web Search and Web Fetch Tools
Ticket triage gets more useful if the agent can check whether a reported issue correlates with a known incident. Enable the built-in web search and web fetch tools so the agent can look things up without you building a search integration from scratch.
agent = project_client.agents.create_agent(
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT"],
name="support-ticket-triage-agent",
instructions="Triage tickets. Check status.yourcompany.com for known incidents before drafting a response.",
tools=[
{"type": "web_search"},
{"type": "web_fetch"},
],
response_format={
"type": "json_schema",
"json_schema": {"name": "ticket_triage", "schema": TicketTriage.model_json_schema()},
},
)
With both tools enabled, the agent can search for context and pull the full content of a specific status page before it commits to a severity rating. Watch the run’s step history in the portal the first few times you do this; it’s the easiest way to see exactly which searches the agent ran and whether it’s actually using the tool or just guessing, which matters when you’re debugging why an output looks off.
Step 7: Build and Register a Custom MCP Server
Web search covers public information. For anything internal, like your actual ticketing system or a database of known outages, you need a custom tool exposed through MCP. Per Microsoft’s MCP documentation for Foundry, the recommended pattern is a lightweight Azure Functions app that exposes your internal API as MCP tools, which you then register in Azure API Center so it’s discoverable across projects and teams, not just hardcoded into one agent.
# mcp_server.py — minimal MCP tool exposing an internal incident lookup
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("incident-lookup")
@mcp.tool()
def check_known_incidents(product_area: str) -> str:
"""Look up open incidents for a given product area."""
incidents = {
"checkout": "INC-4821: Elevated 500 errors, investigating since 09:14 UTC",
"auth": "No open incidents",
}
return incidents.get(product_area, "No open incidents")
if __name__ == "__main__":
mcp.run(transport="stdio")
Deploy this as an Azure Function following the pattern in Microsoft’s own get-started-with-ai-agents sample repository, which includes a working Azure Functions MCP scaffold you can adapt instead of building the Functions wrapper from scratch.
Step 8: Connect Your Agent to the MCP Toolbox
Rather than wiring one MCP server per agent, Foundry supports a Toolbox pattern: register your MCP servers once, then point any agent at the Toolbox endpoint and it inherits the whole tool catalog. This is what tool search (the fifth new capability that shipped this month) is for — when the catalog grows past a handful of tools, tool search lets the agent find the relevant one dynamically instead of you cramming every tool description into the system prompt.
agent = project_client.agents.create_agent(
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT"],
name="support-ticket-triage-agent",
instructions="Triage tickets. Use the incident-lookup tool before drafting a response.",
tools=[
{"type": "web_search"},
{"type": "web_fetch"},
{
"type": "mcp",
"server_label": "incident-lookup",
"server_url": os.environ["MCP_TOOLBOX_ENDPOINT"],
},
{"type": "tool_search"},
],
response_format={
"type": "json_schema",
"json_schema": {"name": "ticket_triage", "schema": TicketTriage.model_json_schema()},
},
)
Re-run the checkout ticket now and the agent should return a severity of “critical” with a draft response that explicitly references the open incident it found through your MCP tool, not just a generic acknowledgment. That’s the point where the agent stops being a demo and starts being something you could actually route real tickets through.
Step 9: Orchestrate Multi-Agent Workflows With the Microsoft Agent Framework
One agent triaging tickets is useful. In practice you often need several agents cooperating: one that triages, one that drafts a customer response, and one that escalates to on-call if severity is critical. The Microsoft Agent Framework, which ships as part of the current Foundry tooling, supports four orchestration patterns: group chat, sequential, concurrent, and handoff.
from agent_framework import SequentialOrchestration
orchestration = SequentialOrchestration(
agents=[triage_agent, response_drafter_agent, escalation_agent],
condition=lambda result: result.get("requires_immediate_escalation") is True,
)
result = orchestration.run(
input="Ticket: 'Our checkout page has been throwing 500 errors for 20 minutes.'"
)
print(result.final_output)
Sequential orchestration is the right pattern for a triage pipeline like this one, since each step depends on the last. Use concurrent orchestration instead when agents work independently on different aspects of the same input (say, one checking severity while another checks for duplicate tickets), and handoff when you want one agent to fully transfer control to a specialist agent rather than just passing along its output.
Step 10: Add Durable Workflows With Checkpointing
A multi-agent pipeline that dies halfway through because the process crashed or the container restarted is a production liability, not a proof of concept. The Agent Framework’s durable workflows checkpoint state as the workflow progresses, so a crashed run resumes from its last completed step instead of starting over and possibly double-sending a customer response.
from agent_framework import DurableWorkflow
workflow = DurableWorkflow(
orchestration=orchestration,
checkpoint_store="azure_storage",
checkpoint_connection_string=os.environ["AZURE_STORAGE_CONNECTION_STRING"],
)
run_id = workflow.start(input="Ticket: 'Checkout throwing 500 errors.'")
# If the process restarts, resume with:
# workflow.resume(run_id=run_id)
Checkpointing costs you a small amount of storage overhead and a bit of latency per step, but for anything customer-facing or tied to an SLA, it’s not optional. Test this by deliberately killing the process mid-run during development, then calling workflow.resume() and confirming it picks up where it left off rather than re-running the triage step and generating a second draft response.
Step 11: Deploy to Azure Container Apps With azd up
With the agent working locally, package it for production. Azure Container Apps is the recommended target for Foundry agents that need to run continuously or respond to webhooks, and the Azure Developer CLI’s azd up command handles provisioning, building, and deploying in one pass.
# Initialize azd in your project directory
azd init --template minimal
# azure.yaml (generated, edit the service name)
# name: ticket-triage-agent
# services:
# agent:
# project: .
# language: python
# host: containerapp
# Provision and deploy in one command
azd up
azd up will prompt for a subscription, region, and environment name, then provision the Container App, container registry, and any supporting ree and pushing it live. A typical run finishes in three to six minutes depending on image size. Expect output that ends with a deployed endpoint URL you can hit immediately to confirm the service is live
If you’re building on .NET instead of Python, this is also where .NET Aspire 9.2’s integration with Azure Container Apps comes in, since Aspire’s orchestration model maps directly onto azd up‘s provisioning flow without extra configuration.
Step 12: Monitor, Trace, and Debug Your Agent in Production
Once the agent is live, you need visibility into what it’s actually doing, not just whether the endpoint returns 200. Foundry integrates with Azure Monitor and Application Insights, and every agent run generates a trace you can inspect step by step, including which tools were called and what each one returned.
from opentelemetry import trace
from azure.monitor.opentelemetry import configure_azure_monitor
configure_azure_monitor(
connection_string=os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("ticket-triage-run"):
run = project_client.agents.create_run(thread_id=thread.id, agent_id=agent.id)
In Application Insights, filter traces by agent name and look for two things first: tool call latency (a slow MCP server will show up as a long span nested inside the run) and response format failures (rare with structured outputs enforced, but worth an alert if the rate climbs above zero). If you’re already running OpenTelemetry elsewhere in your stack, this plugs into the same pipeline rather than requiring a separate monitoring setup.
Testing Your Agent Before Production: A Simple Evaluation Harness
Before shipping any of this to real traffic, you need more than a couple of manual test prompts in the Playground. Build a small evaluation harness that runs a batch of realistic sample tickets through the agent and checks the output against expected values. This doesn’t need to be elaborate; a simple loop with assertions catches most regressions before they reach production.
test_cases = [
{
"input": "Checkout page throwing 500 errors for 20 minutes.",
"expected_severity": "critical",
"expected_area": "checkout",
},
{
"input": "Small typo on the pricing page footer.",
"expected_severity": "low",
"expected_area": "marketing",
},
{
"input": "Users report intermittent login failures for the last hour.",
"expected_severity": "high",
"expected_area": "auth",
},
]
results = []
for case in test_cases:
thread = project_client.agents.create_thread()
project_client.agents.create_message(thread_id=thread.id, role="user", content=case["input"])
run = project_client.agents.create_run(thread_id=thread.id, agent_id=agent.id)
output = get_run_output(run) # parse the structured JSON response
passed = (
output["severity"] == case["expected_severity"]
and output["product_area"] == case["expected_area"]
)
results.append({"input": case["input"], "passed": passed, "output": output})
pass_rate = sum(r["passed"] for r in results) / len(results)
print(f"Pass rate: {pass_rate:.0%}")
Run this harness every time you change the system instructions, the schema, or the underlying model deployment. A pass rate that drops after what looked like a minor prompt tweak is the earliest warning sign you’ll get that something in the agent’s behavior shifted, and it’s far cheaper to catch in a test run than in a customer-facing ticket queue. For a higher bar, log every run’s full trace alongside the pass/fail result so you can diff behavior across model versions when Microsoft ships an update to the underlying Claude deployment.
Complete Working Project: The Full Support-Ticket Triage Agent
Put together, the twelve steps above give you a project with this shape: a Foundry reo structured JSON output, web search and web fetch enabled, a custom MCP server exposing your internal incident data, tool search so the catalog scales, a sequential three-agent workflow (triage, draft, escalate) with durable checkpointing, deployed to Azure Container Apps, and instrumented with OpenTelemetry tracing into Application Insights
That’s a genuinely production-shaped system, not a notebook demo. The same skeleton extends to other use cases with minimal changes: swap the triage schema for a sales-lead qualification schema, point the MCP server at your CRM instead of an incident tracker, and the orchestration and deployment layers barely change. That reusability is the actual argument for learning Foundry’s agent stack instead of hand-rolling your own orchestration loop around a raw model API.
If your agent needs to reason over structured enterprise data rather than just call tools, this is also where pairing Foundry with a proper retrieval layer pays off; teams building vector search over a document store hit similar tradeoffs around latency and grounding accuracy, even though that guide covers AWS rather than Azure.
Real-World Use Cases Beyond Ticket Triage
The triage agent built in this tutorial is deliberately narrow so each step stays easy to follow, but the same architecture shows up across a wide range of production use cases once you swap the schema and tools. Sales teams use nearly identical sequential orchestration to qualify inbound leads: a triage agent scores fit against a schema, a research agent runs web search and web fetch against the company’s own site and recent news, and a drafting agent writes a personalized outreach email, all checkpointed so a failed run doesn’t silently drop a lead.
Internal IT help desks run the pattern almost unchanged from the ticket-triage example here, just pointed at a different MCP server that queries an asset management database instead of an incident tracker. Content operations teams use the concurrent orchestration pattern instead of sequential: one agent checks a draft article against a style guide, another checks factual claims via web search, and a third checks for SEO issues, all running in parallel against the same input before a human editor sees the combined feedback. And compliance teams have started using handoff orchestration specifically, where a general-purpose agent escalates to a specialist agent the moment a document mentions a regulated topic, keeping the specialist’s narrower, more carefully audited instructions out of the hot path for routine requests.
What’s common across all of these is the same five pieces you just built: a scoped agent with a schema, tools appropriate to its job, an orchestration pattern that matches how the sub-tasks actually depend on each other, checkpointing so failures don’t lose work, and tracing so you can see what happened after the fact. That’s the actual reusable skill here, not the specific ticket-triage example.
Project File Structure: How the Pieces Fit Together
With twelve steps behind you, it’s easy to lose track of how the individual files relate to each other. Here’s the directory layout that falls out of the steps above once you organize it for a real repository rather than a sequence of scratch scripts:
ticket-triage-agent/
├── azure.yaml # azd deployment manifest (Step 11)
├── Dockerfile # container build for Azure Container Apps
├── requirements.txt # azure-ai-projects, azure-ai-inference, agent_framework, mcp
├── .env.example # template for local env vars, never commit the real .env
├── src/
│ ├── agents/
│ │ ├── triage_agent.py # Step 4-8: agent + schema + tools
│ │ ├── response_drafter.py # second agent in the sequential workflow
│ │ └── escalation_agent.py # third agent in the sequential workflow
│ ├── schemas/
│ │ └── ticket_triage.py # Pydantic TicketTriage model (Step 5)
│ ├── orchestration/
│ │ └── workflow.py # SequentialOrchestration + DurableWorkflow (Step 9-10)
│ ├── tracing.py # OpenTelemetry / Application Insights setup (Step 12)
│ └── main.py # entrypoint the Container App runs
├── mcp_server/
│ └── incident_lookup.py # custom MCP server (Step 7), deployed separately as an Azure Function
└── tests/
└── eval_harness.py # the evaluation harness from the testing section above
Notice that the MCP server lives in its own directory and deploys as a separate Azure Function rather than bundling into the main Container App image. That separation matters in practice: it lets you update the incident-lookup logic without redeploying the agent itself, and it means the MCP server can be reused by a completely different agent later without copying code between repositories. If you’re building more than one agent against the same internal systems, this is the layout that avoids duplicating tool logic across projects, which is exactly the problem the Toolbox pattern in Step 8 is designed to solve at the platform level.
Common Pitfalls When Building Azure AI Foundry Agents
Five mistakes show up repeatedly when teams build their first Foundry agent. Catching them early saves a lot of debugging time later.
- Skipping regional availability checks. Not every model is deployable in every region, and Claude models specifically have a narrower regional footprint than OpenAI models on the same platform. Verify availability before creating your resource group, not after a deployment fails.
- Hardcoding API keys instead of using managed identity. It works locally, then breaks (or worse, becomes a leaked-credential incident) the moment you deploy to Container Apps. Use
DefaultAzureCredentialfrom day one. - Overloading a single agent with too many tools. An agent juggling ten unrelated tools makes worse tool-selection decisions than a scoped agent with three. Split responsibilities across agents and use handoff orchestration instead.
- Forgetting to test the crash-and-resume path. Durable workflows only help if you’ve actually verified
workflow.resume()works before you need it in production during an incident. - Treating structured outputs as a guarantee against all bad data. The schema constrains shape, not correctness. A field that’s supposed to hold a severity level will always be one of your four allowed values, but the model can still misjudge which one applies. Validate business logic separately from schema conformance.
Troubleshooting: 8 Errors You’ll Hit and How to Fix Them
These are the errors most likely to stop you mid-tutorial, along with what actually causes them and how to resolve each one. Most of these fall into three buckets: identity and permissions, network reachability, and schema mismatches, so if you hit something not listed here, it’s worth checking which of those three categories it falls into before digging deeper, since the fix pattern is usually similar within a bucket even if the exact error text differs.
| Error / Symptom | Likely Cause | Fix |
|---|---|---|
| “Model not available in this region” | Chosen region doesn’t host the model you’re deploying | Check the model catalog’s regional availability filter and redeploy in a supported region |
| 401 Unauthorized on SDK calls | Local Azure CLI session expired, or managed identity not assigned | Run az login again locally; in production, confirm the Container App’s managed identity has the Cognitive Services User role |
| Structured output validation errors | Pydantic schema includes a type the model can’t reliably constrain (e.g. deeply nested optional unions) | Flatten the schema and use Literal types instead of open-ended strings where possible |
| MCP server times out on first call | Cold-start latency on Azure Functions consumption plan | Switch to a Premium or dedicated Functions plan, or add a warm-up ping before agent runs |
| Agent ignores the MCP tool entirely | Tool description is vague, so the model doesn’t recognize when to call it | Rewrite the tool’s docstring to explicitly state when it should be used, matching the phrasing you expect in real inputs |
| Checkpoint store connection failures | Storage account firewall blocking the Container App’s outbound traffic | Add a private endpoint or allow the Container App’s outbound IP range in the storage account’s network rules |
| azd up fails at provisioning step | Subscription quota exceeded for the target region | Request a quota increase or redeploy in a region with available capacity |
| No traces appearing in Application Insights | Connection string not passed to the container’s environment variables | Confirm APPLICATIONINSIGHTS_CONNECTION_STRING is set in the Container App’s configuration, not just locally |
Advanced Tips: Cost Control, Security, and Scaling to Production
Once the basic pipeline works, a few adjustments separate a tutorial project from something a team can actually run. On cost: model inference is billed by token consumption, and multi-agent sequential workflows multiply token usage since each agent re-processes context from the previous step. Check the official Azure AI Foundry pricing page before committing to a specific model tier for a high-volume workflow, and consider a cheaper model for the triage step and a stronger one only for the escalation step, since severity classification generally needs less reasoning capacity than a customer-facing response draft.
On security: lock down the MCP server so it only accepts calls from your Foundry project’s managed identity, not from any authenticated caller. Register it in Azure API Center with an explicit policy rather than leaving it open, especially if the tool touches customer data. On scaling: Container Apps scales on HTTP concurrency or a custom KEDA rule by default; for an agent workload, scale on queue depth if you’re processing tickets asynchronously, since request-based scaling doesn’t account for the fact that one agent run can take considerably longer than a typical API call.
One more thing worth doing before this goes anywhere near real customer data: run the same threat-modeling checklist you’d apply to any internet-facing service. If you haven’t set one up recently, the steps in building an incident response plan apply directly to an agent that can autonomously call external tools and escalate tickets.
Scaling This Pattern to Higher Traffic
Everything in this tutorial works at low volume without changes, but a few things start to matter once you’re processing hundreds of tickets an hour instead of a handful during testing. Batch where you can: if tickets arrive in bursts rather than a steady trickle, queue them behind Azure Service Bus and process in small batches rather than spinning up a run per ticket the instant it arrives, which reduces the per-run overhead of thread creation and keeps you further from any tokens-per-minute quota ceiling. Cache aggressively on the MCP server side too; if your incident-lookup tool queries the same handful of product areas repeatedly within a short window, a short-TTL cache in front of the actual data source cuts both latency and load on whatever system backs it.
Watch concurrent run limits on the Foundry project itself, since there’s a ceiling on how many agent runs can execute simultaneously per project, and a burst of traffic that exceeds it queues rather than fails outright, which can look like a latency spike if you’re not watching for it specifically. If you consistently run against that ceiling, splitting traffic across multiple projects under the same Foundry resource (the architecture described earlier in this tutorial) gives you more concurrent capacity without needing a second resource group or a second set of credentials to manage.
Governance and Compliance: What IT Will Ask Before Sign-Off
Getting an agent working locally is the easy part. Getting it approved to touch real customer data is where most teams lose weeks, mostly because nobody thought through the governance questions until security review asked them. Four questions come up in almost every internal review, and it’s worth having answers ready before you ask for sign-off rather than scrambling afterward.
First: what data does the agent actually see, and where does it live afterward? Structured outputs help here because you can log exactly what fields the agent reads and writes, rather than an opaque blob of conversation history. Second: what happens if the model hallucinates a tool call with bad parameters? Test this deliberately by feeding the agent a malformed or adversarial ticket and confirming your MCP server validates inputs rather than trusting whatever the model sends. Third: who can see the agent’s traces in Application Insights, and do those traces contain anything that shouldn’t be visible to a broader engineering team, like customer PII embedded in a ticket body? Fourth: what’s the rollback plan if the agent starts producing bad outputs in production, and how fast can you disable it without taking down the rest of the pipeline it’s embedded in?
Azure AI Foundry’s governance tooling helps with parts of this out of the box: role-based access control at both the resource and project level, audit logging through Azure Monitor, and network isolation options including private endpoints for the Foundry resource itself. None of that substitutes for actually answering the four questions above with your specific data flows in mind, but it does mean you’re not building the plumbing for RBAC and audit logging from scratch, which is often the slowest part of a security review for a homegrown agent framework.
Azure AI Foundry vs Amazon Bedrock vs Vertex AI: Quick Comparison
If you’re choosing a platform rather than committed to Azure already, here’s how Foundry’s current feature set stacks up against the two other major managed agent platforms.
| Feature | Azure AI Foundry | Amazon Bedrock | Google Vertex AI |
|---|---|---|---|
| Managed agent service | Foundry Agent Service | Bedrock AgentCore | Vertex AI Agent Builder |
| Multi-agent orchestration framework | Microsoft Agent Framework (group chat, sequential, concurrent, handoff) | Multi-agent collaboration (supervisor pattern) | Agent Development Kit |
| MCP support | Native MCP connector + Toolbox pattern | MCP support via Bedrock AgentCore Gateway | MCP support via ADK |
| Claude model hosting | Yes, first-party in model catalog | Yes, first-party (Anthropic’s primary cloud partner) | Limited, via Vertex Model Garden |
| Durable/checkpointed workflows | Yes, via Agent Framework durable workflows | Yes, via Step Functions integration | Yes, via Vertex AI Pipelines |
| Primary deployment target | Azure Container Apps / AKS | Lambda / ECS / EKS | Cloud Run / GKE |
None of these platforms is strictly better across the board; the right pick depends heavily on where the rest of your infrastructure already lives. If you’re already running workloads on Amazon Bedrock, the migration cost to Foundry for a single agent usually isn’t worth it unless you specifically need a Foundry-exclusive feature. The clearest signal for choosing Foundry specifically is a team that’s already standardized on Azure for identity (Entra ID) and networking, since the governance benefits described above compound when the agent platform shares an identity and network boundary with everything else you run. Teams starting from a blank slate with no existing cloud commitment tend to make the decision based on which model provider they want as a primary, since all three platforms have converged on a broadly similar feature set for the core agent-building workflow this tutorial covers.
Frequently Asked Questions
Is Azure AI Foundry free to use?
Azure AI Foundry itself has no separate platform fee; you pay for the underlying rempute for Container Apps, storage for checkpointing, and Application Insights ingestion. Check the official pricing page for current per-model token rates, since they vary by model and region
Do I need to use Claude models, or can I follow this tutorial with a different model?
The core agent-building steps work with any model in the Foundry catalog that supports tool calling. The specific reason this tutorial uses Claude is that structured outputs, web search, web fetch, MCP connector, and tool search all shipped for Claude models on Foundry this month, giving you access to every feature covered here through one model.
What is MCP, and why does Azure AI Foundry support it?
Model Context Protocol is an open standard for connecting AI models to external tools and datamat, an MCP server you build for a Foundry agent can, in principle, work with any MCP-compatible client, not just Azure’s. Full documentation lives at modelcontextprotocol.io
Can I run Azure AI Foundry agents outside of Azure Container Apps?
Yes. Container Apps is the deployment target this tutorial uses because azd up automates provisioning end to end, but the underlying agent code runs anywhere Python (or your chosen SDK language) runs, including Azure Kubernetes Service, Azure Functions, or even a VM, as long as it can reach the Foundry project endpoint.
How is this different from just using the Azure OpenAI Service directly?
Azure OpenAI Service gives you a raw model endpoint. Azure AI Foundry wraps that (and other models, including Claude) with agent state management, built-in tools, orchestration, and governance. If you only need single-turn completions, the raw endpoint is simpler. If you’re building anything with multi-step reasoning, tool use, or multiple cooperating agents, Foundry’s Agent Service saves you from re-implementing that plumbing yourself.
What happens if my durable workflow checkpoint gets corrupted?
The workflow fails to resume from that checkpoint and falls back to starting the affected step over. This is why the checkpoint store should live in a resilient storage account with redundancy enabled, and why it’s worth testing your resume path deliberately during development rather than discovering it fails during an actual incident.
Does Azure AI Foundry support languages other than Python?
Yes. The SDK is available for Python, C#/.NET, TypeScript/JavaScript, and Java. The .NET path is particularly well-supported right now given the recent .NET Aspire 9.2 integration with Azure Container Apps, which streamlines the exact deployment flow covered in Step 11 for teams already on the .NET stack.
How do I know if my agent is ready for production traffic?
At minimum, confirm four things: structured output validation errors are at or near zero across a realistic test set, the crash-and-resume path has been tested manually, tracing is flowing into Application Insights, and you’ve set token-usage alerts so a runaway loop doesn’t generate an unexpected bill. If any of those four are missing, treat it as still in development regardless of how well the demo runs.
Can multiple teams share the same Azure AI Foundry re
Yes, and it’s the recommended pattern once more than one team is building agents, since it avoids duplicating networking and identity setup. Create a separate project per team under the shared resource, and scope RBAC roles at the project level rather than the resource level so one team’s agents and deployments stay isolated from another’s. The main thing to watch is shared quota: if the underlying Foundry resource has a fixed tokens-per-minute limit for a given model, multiple teams’ projects draw from the same pool, so a traffic spike in one project can throttle another. Monitor per-project usage in Azure Monitor if you’re sharing a resource across teams with different traffic patterns.
![Azure AI Foundry Tutorial: Build Agents in 12 Steps [2026] Azure AI Foundry Tutorial: Build Agents in 12 Steps [2026]](https://aitoday7.com/wp-content/uploads/2026/08/how-to-build-azure-ai-foundry-agents-2026-1-1024x585.webp)