Browse the docs

Host and deploy on Foundry

Serve an agent or workflow with ResponsesHostServer, package it, deploy it as a Foundry hosted agent version, and call it from a client.

Before you startA Foundry project in a region that supports hosted agents, and a data-plane role on it.View as Markdown

Before you start

  • Region: Foundry hosted agents are region-limited, and an account's region is fixed when it is created. Check Microsoft's hosted agents region list. An unsupported region fails with bad_request: Unsupported region for Foundry Hosted Agents.
  • Model: the project needs a deployed chat model.
  • Role: creating and invoking agent versions needs a data-plane role on the project, such as Foundry User. Control-plane Owner or Contributor alone is not enough.
  • Tooling: locally you only need azure-ai-projects>=2.3.0 and azure-identity to drive the deploy. Foundry installs requirements.txt itself in a remote build.

Write main.py

ResponsesHostServer serves the Responses protocol (/responses) and a health probe on port 8088, which is the runtime contract Foundry expects. A single agent is served directly:

main.py (single agent)
from agent_framework_foundry_hosting import ResponsesHostServer

from agent import create_agent


def main() -> None:
    ResponsesHostServer(create_agent()).run()


if __name__ == "__main__":
    main()

A workflow is wrapped with as_agent() first. Passing a raw Workflow makes the host call Workflow.run(messages=...), which fails. This version also turns on process-loss recovery and allow-lists the checkpoint types; Harden an agent or workflow for AMC explains both.

main.py (workflow)
from agent_framework_foundry_hosting import CheckpointStoreProvider, ResponsesHostServer
from azure.ai.agentserver.responses import ResponsesServerOptions

from workflow import build_workflow, create_agents


def main() -> None:
    workflow = build_workflow(*create_agents())
    server = ResponsesHostServer(
        workflow.as_agent(name="contoso-content-pipeline"),
        options=ResponsesServerOptions(resilient_background=True),
        checkpoint_store_provider=CheckpointStoreProvider(
            allowed_checkpoint_types=["workflow:Draft"],
        ),
    )
    server.run()


if __name__ == "__main__":
    main()

Container

Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m appuser
USER appuser
EXPOSE 8088
CMD ["python", "main.py"]

Keep .env, virtual environments and the deploy script out of the image and the deploy package.

Deploy with the Python SDK

This script zips the folder, creates a new code version with a remote build, waits for it to go active, routes all traffic to it, and smoke-tests it. Each run creates a new immutable version.

deploy.py
import sys, tempfile, time, zipfile
from pathlib import Path

from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import (
    AgentEndpointConfig, CodeConfiguration, CodeDependencyResolution,
    FixedRatioVersionSelectionRule, HostedAgentDefinition, ProtocolConfiguration,
    ProtocolVersionRecord, ResponsesProtocolConfiguration, VersionSelector,
)
from azure.identity import AzureCliCredential

ENDPOINT = "<your-project-endpoint>"
MODEL_NAME = "<model-deployment-name>"
AGENT_NAME = "contoso-content-pipeline"
SOURCE_DIR = Path(__file__).parent.resolve()
EXCLUDE_PARTS = {".git", ".venv", "__pycache__", ".azure", ".env"}
EXCLUDE_NAMES = {".env", ".dockerignore", "deploy.py"}


def make_zip() -> Path:
    zp = Path(tempfile.gettempdir()) / f"{AGENT_NAME}.zip"
    zp.unlink(missing_ok=True)
    with zipfile.ZipFile(zp, "w", zipfile.ZIP_DEFLATED) as zf:
        for p in SOURCE_DIR.rglob("*"):
            rel = p.relative_to(SOURCE_DIR)
            if not p.is_file() or rel.name in EXCLUDE_NAMES:
                continue
            if any(part in EXCLUDE_PARTS for part in rel.parts):
                continue
            zf.write(p, rel.as_posix())
    return zp


def wait_active(c, ver) -> None:
    for _ in range(90):
        time.sleep(10)
        status = c.agents.get_version(agent_name=AGENT_NAME, agent_version=ver)["status"]
        if status == "active":
            return
        if status in ("failed", "Failed"):
            raise RuntimeError("provisioning failed")
    raise TimeoutError("version never went active")


def main() -> None:
    zp = make_zip()
    with zp.open("rb") as code, AzureCliCredential() as cred, \
         AIProjectClient(endpoint=ENDPOINT, credential=cred) as c:
        created = c.agents.create_version_from_code(
            agent_name=AGENT_NAME,
            description="Contoso content pipeline",
            definition=HostedAgentDefinition(
                cpu="1", memory="2Gi",
                code_configuration=CodeConfiguration(
                    runtime="python_3_13", entry_point=["python", "main.py"],
                    dependency_resolution=CodeDependencyResolution.REMOTE_BUILD),
                environment_variables={
                    "FOUNDRY_PROJECT_ENDPOINT": ENDPOINT,
                    "FOUNDRY_MODEL_NAME": MODEL_NAME},
                protocol_versions=[ProtocolVersionRecord(protocol="responses", version="2.0.0")],
            ),
            code=code,
        )
        wait_active(c, created.version)
        c.agents.update_details(
            agent_name=AGENT_NAME,
            agent_endpoint=AgentEndpointConfig(
                version_selector=VersionSelector(version_selection_rules=[
                    FixedRatioVersionSelectionRule(agent_version=created.version, traffic_percentage=100)]),
                protocol_configuration=ProtocolConfiguration(responses=ResponsesProtocolConfiguration()),
            ),
        )
        with c.get_openai_client(agent_name=AGENT_NAME) as oai:
            r = oai.responses.create(input="Slogan for an affordable electric bike")
        print("smoke test:", r.status, r.error)


if __name__ == "__main__":
    sys.exit(main())
SettingValueWhy
runtimepython_3_13Agent Framework installs cleanly. Avoid python_3_14, where some wheels are missing.
entry_point["python", "main.py"]main.py starts ResponsesHostServer.
dependency_resolutionREMOTE_BUILDFoundry installs requirements.txt in the cloud.
protocol_versionsresponses 2.0.0Required by the responses 2.x runtime that process-loss recovery uses.
cpu / memory1 / 2GiAllowed sizes are 0.5/1Gi, 1/2Gi and 2/4Gi.

A healthy smoke test has status == "completed" and error of None. To roll back, point FixedRatioVersionSelectionRule at an earlier version. Never delete the version that is currently routed. Microsoft also documents an azd ai agent flow; the reference deployments behind this guide use the SDK path above because it is easier to run non-interactively.

Call the deployed agent from a client

This is a preview API. It is also how you read the raw stream AMC will see.

verify.py
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import VersionRefIndicator
from azure.identity import DefaultAzureCredential

ENDPOINT = "<your-project-endpoint>"
AGENT_NAME = "contoso-content-pipeline"

with (DefaultAzureCredential() as cred,
      AIProjectClient(endpoint=ENDPOINT, credential=cred, allow_preview=True) as project):
    ver = project.agents.get(agent_name=AGENT_NAME).versions["latest"].version
    session = project.agents.create_session(
        agent_name=AGENT_NAME, version_indicator=VersionRefIndicator(agent_version=ver))
    oai = project.get_openai_client(agent_name=AGENT_NAME)
    stream = oai.responses.create(
        input="Draft a launch slogan.",
        stream=True,
        extra_body={"agent_session_id": session.agent_session_id},
        metadata={"x-ms-debug-mode-enabled": "1"},
    )
    for event in stream:
        print(event.type)
    project.agents.delete_session(agent_name=AGENT_NAME, session_id=session.agent_session_id)

x-ms-debug-mode-enabled gives richer events. AMC sends the same metadata when it runs the agent.

Last updated 2026-10-21