Building Event-Driven Agents with Azure Functions

Microsoft now has several ways to build AI agents. You can create prompt agents in Foundry, build agentic workflows with Logic Apps, or deploy your own agent code as a Foundry hosted agent.

There is another option that fits a different type of problem: the Azure Functions serverless agents runtime.

This runtime lets you define agents in Markdown files and run them as Azure Functions. Agents can be triggered by HTTP requests, timers, queues, blobs, database changes, or connector events. The runtime is currently in preview, so features can still change. See the Microsoft documentation for the latest information.

The sample in this post is a scheduled AI news digest. It searches for recent AI news and sends a card to Teams. The news digest is not the important part. It is just a small example that needs a timer, a model, tools, state, and an external notification.

What is the serverless agents runtime?

The serverless agents runtime is a programming model for event-driven agents.

You define the agent, configure its capabilities, and deploy the project as a normal Python Function App. The runtime discovers the agents, registers their triggers, assembles their tools, and runs them using Microsoft Agent Framework.

The complete function_app.py in the sample is this:

from azure_functions_agents import create_function_app

app = create_function_app()

There is no timer registration or explicit agent construction here. The runtime finds that information in the project files when the Functions host starts.

This is the main idea:

event
-> Azure Functions trigger
-> runtime-built agent
-> model and tools
-> result

Defining an agent

The agent is defined in a markdown file: ai_news_digest.agent.md.

---
name: AI News Teams Digest Agent
description: Finds recent AI news and posts a digest.
model: $FOUNDRY_MODEL
trigger:
type: timer_trigger
args:
schedule: "0 0 6 * * *"
---

The Markdown below the front matter becomes the agent instructions:

You are a scheduled AI news digest agent.
When this timer fires:
1. Research recent AI news.
2. Select at most five distinct articles.
3. Call post_ai_news_digest exactly once.

The schedule uses the six-field Azure Functions NCRONTAB format. In this case, the agent runs every day at 06:00 UTC.

The important point is that the timer is part of the agent definition. The application does not need a separate timer function.

Note the the Foundry endpoint is set as an environment variable. It is not in the markdown file. The Function App gets a managed identity which can access the model referenced by $FOUNDRY_MODEL which is also an environment variable. I used gpt-5.6-luna here.

Check https://learn.microsoft.com/en-us/azure/azure-functions/functions-serverless-agents-runtime-reference to learn about the possible settings in the markdown file. I have used only a small subset here.

What happens at startup?

When create_function_app() runs, the runtime discovers:

  • Agent files and their triggers.
  • MCP servers in mcp.json.
  • Custom Python tools in tools/.
  • Reusable skills in skills/, if present.
  • App-wide settings in agents.config.yaml.

When a trigger fires, the runtime resolves the model and instructions, assembles the available tools, loads session information if needed, and executes the agent.

The app-wide configuration in this sample is small:

model: $FOUNDRY_MODEL
timeout: 900

This keeps the model and timeout outside the Python code. Agent-specific settings can still override these defaults in the front matter. There are other settings like a management endpoint for the Azure Container Apps dynamic session pool used by sandbox tools. They are not used here.

An agent is becoming a directory

A useful pattern is emerging across agent runtimes: an agent is becoming a collection of files rather than one large class or prompt.

For example, this sample uses:

ai_news_digest.agent.md
agents.config.yaml
mcp.json
tools/
skills/

Vercel’s eve uses a similar filesystem-first approach with instructions, tools, skills, schedules, and subagents. LangChain’s Managed Deep Agents also uses files such as AGENTS.mdskills/subagents/, and tools.json.

The files define the agent. The runtime provides the operational parts:

agent files + runtime = running agent

The runtime handles things such as tool execution, state, scheduling, sandboxes, and observability.

The Azure Functions runtime’s particular strength is its event model. An agent can start from a timer, HTTP request, queue message, blob event, or connector event.

Adding tools with MCP

The sample uses Tavily for web search through MCP:

{
"servers": {
"tavily": {
"type": "streamable-http",
"url": "$TAVILY_MCP_SERVER_URL"
}
}
}

The application does not contain Tavily client code. The runtime discovers the remote MCP server and makes its tools available to the agent.

The same model can be used with other remote MCP servers, Azure connector tools, custom Python tools, and sandboxed Python execution. The runtime reference describes these capabilities in more detail.

Custom Python tools are useful for application-specific logic. The sample exposes one tool for posting the digest:

from azure_functions_agents import tool
@tool
def post_ai_news_digest(digest: NewsDigest) -> str:
"""Post up to five articles as one Teams card."""
...

The tool validates the articles, removes ones already sent, creates an Adaptive Card, and stores the article IDs in Blob Storage. That is normal application code. The runtime’s job is to discover the tool and make it available to the agent.

Why Azure Functions?

The runtime is useful because it brings the normal Azure Functions features to agents.

With Flex Consumption, the Function App can scale to zero, scale automatically, and use per-second billing. It also supports managed identity, Application Insights, and virtual network integration.

The runtime supports several event types:

  • HTTP requests.
  • Timer triggers.
  • Queue and Service Bus messages.
  • Blob and database events.
  • Connector events.

This makes it a good fit for background agents, monitors, reports, and event processors.

The runtime also manages session history. In Azure, session data is stored through the Function App’s AzureWebJobsStorage account. The application can still keep its own state, as the news sample does with seen-articles.json.

These are two different things:

Runtime state: conversation and session history
Application state: articles already posted by this sample

How does it compare with other agent options?

Microsoft’s comparison guide summarizes the main differences.

OptionMain ideaBest fit
Serverless agents runtimeMarkdown agents running as FunctionsEvent-driven agents
Foundry prompt agentsDeclarative agents managed by FoundryAgents without custom code
Foundry hosted agentsYour code packaged as a containerCustom agent servers and protocols
Logic Apps agent workflowsAn Agent action inside a workflowBusiness process automation

Foundry prompt agents are useful when instructions and configured tools are enough. You do not need to manage a Function App or custom agent process. They were not a good fit for this agent because I needed a bit of custom code to ensure the agent returns unique links.

Foundry hosted agents are better when you need to bring your own code and container. Foundry manages the endpoint, identity, sessions, scaling, and lifecycle of that container. See the hosted agents documentation. A hosted agent would have been overkill for this agent.

Logic Apps is workflow-first. An autonomous workflow can start from a trigger, pass data to an Agent action, and let the agent use tools created from Logic Apps actions. This is useful when approvals, connectors, retries, and business steps are more important than the agent code itself. See the Logic Apps agent workflow documentation.

A simple way to remember the difference is:

Logic Apps: a workflow that contains an agent
Azure Functions: an agent started by a Function trigger
Foundry hosted: your agent running inside a managed container

Deploying the sample

Deployment is still normal Azure Functions deployment:

func azure functionapp publish <FUNCTION_APP_NAME> \
--python \
--build remote \
--force \
--subscription <SUBSCRIPTION_ID>

After deployment, the output should show the discovered timer function:

ai_news_digest - [timerTrigger]

Application Insights can then show the important parts of the run: MCP discovery, model tool calls, the custom Python tool, and the outbound Power Automate request.

Conclusion

The news digest is only a sample workload. The more interesting part is the hosting model.

The Azure Functions serverless agents runtime combines Markdown-based agent definitions with the event model of Azure Functions. It provides triggers, tool discovery, MCP integration, managed identity, session storage, telemetry, and serverless hosting.

Use it when the agent is naturally started by an event. Use Foundry hosted agents when you need a custom container and endpoint. Use Logic Apps when the main problem is business workflow orchestration.

The runtime gives you another useful choice: an agent can be a Function App workload without requiring you to build and operate a complete agent server yourself.