Agent · ai · AI Foundry · Architecture · Azure CLI · Azure Cognitive Services · Azure Open Ai · AzureFunctions · Bicep

Building a Serverless AI Agent on Azure Functions – with Nothing but a Markdown File

A hands-on walkthrough of the Azure Functions serverless agents runtime (public preview, announced at Microsoft Build 2026). We build a tiny “Cloud Study Buddy” agent, deploy it to a real Azure subscription with azd, and test it end to end — documenting every command, every gotcha, and every fix along the way.

What was announced, and why it matters

At Microsoft Build 2026, Microsoft shipped the public preview of the Azure Functions serverless agents runtime — a markdown-first programming model for building AI agents that run as ordinary Azure Functions apps.

The pitch is simple: production agents need more than a prompt and a model. They need a reliable way to start work (triggers), call external systems (tools/MCP), persist conversation history (sessions), run untrusted code safely (sandbox), authenticate without secrets (managed identity), emit telemetry, and scale on demand. Azure Functions already solves all of those operational concerns – so the runtime layers an agent model on top of it.

Instead of stitching together hosting, model clients, tools, session storage, identity, and observability yourself, you describe an agent in a .agent.md file and deploy it like any other function app. It’s powered by the Microsoft Agent Framework (MAF) and published on PyPI as azurefunctions-agents-runtime.

Key capabilities:

  • Build agents with markdown – instructions, trigger config, and tool bindings live in .agent.md files.
  • Any Functions trigger starts an agent – HTTP, timer, queue, blob, Event Hub, Service Bus, Cosmos DB, and connector triggers (Teams, Outlook, SharePoint…).
  • 1,400+ connectors via connector-backed MCP servers.
  • Custom tools in plain Python – drop a file in tools/, decorate a function with @tool.
  • Automatic HTTP + MCP endpoints – expose a chat API, a debug chat UI, and an MCP tool with no extra code.
  • Serverless with built-in sessions – runs on the Flex Consumption plan, scales to zero, and persists multi-turn conversations in Azure Blob Storage.
  • Pluggable model providers – Microsoft Foundry (recommended), Azure OpenAI, or OpenAI.

Reference docs:

What we’re going to build

A deliberately small demo – a single chat agent called Cloud Study Buddy – so the runtime concepts stay front and center rather than the app logic:

  • It explains Azure / cloud concepts in plain language.
  • It uses the sandboxed Python code interpreter (Azure Container Apps dynamic sessions) to do exact calculations instead of guessing.
  • It exposes a built-in chat UI, a JSON chat API, and an MCP tool – all for free from one markdown file.

The entire “code” of the agent is this one file, src/main.agent.md:

---
name: Cloud Study Buddy
description: A friendly Azure learning companion that explains cloud concepts and can crunch numbers with sandboxed Python.

builtin_endpoints: true

mcp: false
---

You are **Cloud Study Buddy**, a friendly and concise tutor that helps people learn Microsoft Azure and general cloud computing concepts.

Follow these guidelines:

1. Explain concepts in plain language first, then add a short technical detail for depth. Keep answers skimmable.
2. When a question involves numbers - cost estimates, capacity planning, unit conversions, or quick data analysis - use the sandboxed Python code interpreter to compute the exact answer rather than guessing. Show the key figures you calculated.
3. When useful, give a tiny, copy-pasteable Azure CLI or example snippet.
4. If you are unsure or a topic is outside cloud computing, say so briefly instead of inventing an answer.
5. End non-trivial answers with a one-line "💡 Try next:" suggestion so the learner knows what to explore next.

Keep a warm, encouraging tone. You are here to make cloud concepts click.

The YAML front matter is configuration; the markdown body becomes the agent’s system prompt. builtin_endpoints: true is the magic that gives us the chat UI + chat API + MCP tool.

Anatomy of the project

The runtime expects a small, conventional layout. After scaffolding (next section), our src/ looked like this:

FilePurpose
src/main.agent.mdThe agent. Front matter + markdown instructions. The filename stem (main) becomes the endpoint slug: /agents/main/.
src/function_app.pyBootstrap for the Functions host. Literally three lines.
src/agents.config.yamlApp-wide runtime defaults (model, timeout, system tools like the code interpreter).
src/host.jsonStandard Functions host config + extension bundle.
src/requirements.txtJust azurefunctions-agents-runtime.
infra/Bicep that azd uses to provision everything.
azure.yamlTells azd the src/ folder is a Python function service.

function_app.py is the entire host wiring:

from azure_functions_agents import create_function_app

app = create_function_app()

agents.config.yaml binds the code interpreter and model app-wide:

system_tools:
  dynamic_sessions_code_interpreter:
    endpoint: $ACA_SESSION_POOL_ENDPOINT

model: $FOUNDRY_MODEL
timeout: 900

Note the $ACA_SESSION_POOL_ENDPOINT and $FOUNDRY_MODEL placeholders – the runtime substitutes these from app settings (environment variables) at load time. The Bicep populates them for us.

Prerequisites & environment check

Prerequisites list (from the official quickstart):

  1. Azure Developer CLI (azd)
  2. Azure CLI (az)
  3. An Azure subscription with permissions to create resource groups, function apps, managed identities, Microsoft Foundry resources, model deployments, and Azure Container Apps session pools.
  4. (Optional) A Microsoft 365 account if you want to enable the connector-based email demo. We skipped this to keep the demo minimal.

Scaffolding from the official template

The runtime ships an azd template that provisions the whole supporting cast. I initialized it straight into my repo:

azd init --template Azure-Samples/functions-quickstart-serverless-agents-azd -e serverless-agents

The -e serverless-agents flag names the azd environment (it tracks deployment state and seeds resource names).

Understanding what azd up will provision

Before spending money, I read infra/main.bicep. The template deploys (all into one resource group):

  • user-assigned managed identity (the app’s identity – no secrets).
  • Microsoft Foundry account + project, and a model deployment.
  • Flex Consumption App Service plan (FC1) + the Function App.
  • storage account (deployment package + session history).
  • Log Analytics + Application Insights (with local auth disabled).
  • An Azure Container Apps dynamic session pool (the Python sandbox).
  • RBAC role assignments wiring the identity to Foundry, storage, and the session pool.

The important parameters (from infra/main.parameters.json), and the app settings the Function App receives:

AZURE_FUNCTIONS_AGENTS_PROVIDER = foundry
FOUNDRY_PROJECT_ENDPOINT = <project endpoint>
FOUNDRY_MODEL = <model deployment name>
ACA_SESSION_POOL_ENDPOINT = <session pool endpoint>
AZURE_CLIENT_ID = <managed identity client id>

The allowed regions are constrained in Bicep to those that support Flex Consumption + ACA session pools + the Foundry model simultaneously: centralus, eastus, eastus2, northcentralus, southcentralus, westus.

Configuring the deployment

I set the region and a conservative model capacity via the azd environment:

azd env set AZURE_LOCATION eastus2
azd env set FOUNDRY_DEPLOYMENT_CAPACITY 50   # 50K tokens/min - plenty for a demo

Deploy

azd up --no-prompt

The resources that landed in rg-serverless-agents:

ResourceNameNotes
Function App (Flex Consumption)func-agents-nameHosts the agent
App Service planplan-nameSKU FC1 (Flex Consumption)
Foundry account / projectacc-name/ …-projModel host
Model deploymentgpt-5-miniversion 2025-08-07
ACA session poolsession pool namePython sandbox
StoragestoagePackage + session history
Managed identityid-agents-nameApp identity
App Insights / Log Analyticsappi-… / log-…Telemetry

azd registered four functions from that one markdown file:

agent_main_builtin_chat # POST /agents/main/chat (JSON)
agent_main_builtin_chatstream # POST /agents/main/chatstream (SSE)
agent_main_builtin_chat_page # GET /agents/main/ (debug chat UI)
agent_main_builtin_mcp # MCP tool via /runtime/webhooks/mcp

Test – the browser chat UI

Browse to https://func-name..azurewebsites.net/agents/main/. On first load it prompts for the base URL and the function key (stored in browser local storage), then gives you a full chat experience against the deployed agent – no frontend code written.

How the runtime starts the app (under the hood)

When the Functions host imports function_app.pycreate_function_app():

  1. Resolves the app root (AzureWebJobsScriptRoot).
  2. Loads agents.config.yaml.
  3. Loads every *.agent.md file.
  4. Discovers MCP servers (mcp.json), reusable skills/, and custom tools/.
  5. Composes app-wide config and registers one Azure Function per agent trigger, plus any built-in endpoints.

Environment-variable substitution ($VAR / %VAR%) is applied to agents.config.yamlmcp.json, agent front matter, and the markdown body (outside fenced code blocks) – which is how $ACA_SESSION_POOL_ENDPOINT and $FOUNDRY_MODEL get filled in from app settings.

Cost, cleanup, and iteration

  • Cost: Flex Consumption scales to zero, the session pool bills per use, and gpt-5-mini is inexpensive – a demo like this costs cents. The model deployment reserves capacity (I set 50K TPM), so tear it down when done.
  • Iterate: Edit main.agent.md and run azd deploy (code only) or azd up (infra + code) again.
  • Tear down everything:azd down --purge --force # or: az group delete -n rg-serverless-agents Use --purge so the Foundry (Cognitive Services) account doesn’t linger in a soft-deleted state and block future re-deploys.

Built and verified on 2026-07-07 against the public preview. Preview APIs and defaults may change before GA.

Leave a comment