Contents · 8 sections↓
This guide shows how to build an Azure OpenAI agent with MCP tools: an agent that runs on OpenAI models deployed in your own Azure resource and calls tools from a Model Context Protocol server. It is for developers whose company runs models through Azure AI Foundry for data residency, private networking or billing, and who need the Azure-specific settings right. You will learn the four values Azure needs, the two API styles, keyless sign-in with Entra ID, and the errors you are most likely to hit.
How do you use Azure OpenAI with Promptise?
Deploy a model in Azure AI Foundry, then pass Model("gpt-5-mini", provider="azure", deployment="<your deployment name>") to build_agent(), with the endpoint, key and API version in AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY and OPENAI_API_VERSION. Promptise builds LangChain's AzureChatOpenAI, which is part of the core install. Add your MCP servers in servers=, and the agent sends their tools to your deployment as function definitions and runs the calls the model makes.
Azure routes by deployment name, not model name. The deployment is what you called the model when you deployed it. Getting this wrong is the most common Azure error.
Four values identify a deployment: the model, the deployment name, the resource endpoint and the API version, plus a key or an Entra ID identity.
Azure's v1 API needs no API version. You can reach it through Promptise's openai provider with endpoint=.
Entra ID works without a key. Pass a token provider from azure-identity.
Check before you call.
promptise models check azure:<deployment>names every missing value and where to find it in the portal.
[02]
Set up Azure OpenAI
You need Python 3.10 or later, Promptise Foundry 1.4.2 or later, an Azure subscription and a resource with a deployed model. In the Azure AI Foundry portal, deploy a model (this guide uses gpt-5-mini) and give the deployment a name. The examples call it shop-agent.
pip install "promptise>=1.4.2"Collect four values from the portal:
You need | Where it is | Promptise word | Variable |
|---|---|---|---|
---|---|---|--- | |||
The model (gpt-5-mini) | Deployments, the Model column | model | none |
The deployment name (shop-agent) | Deployments, the Name column | deployment | none (or the string form) |
The endpoint | Your resource, Overview, Endpoint (https://<resource>.openai.azure.com/, no path) | endpoint | AZURE_OPENAI_ENDPOINT |
A key | Your resource, Keys and Endpoint, KEY 1 | api_key | AZURE_OPENAI_API_KEY |
The API version | Azure's API version lifecycle page | api_version | OPENAI_API_VERSION |
promptise models env azure prints the lines for your .env file, with a note on where each value is:
# Azure OpenAI (OpenAI models deployed in Azure AI Foundry) — model string example: azure:chat-prod
export AZURE_OPENAI_ENDPOINT=https://my-resource.openai.azure.com/
# ↳ Azure AI Foundry portal → your resource → Overview → Endpoint (https://<resource>.openai.azure.com/, no path)
export AZURE_OPENAI_API_KEY=...
# ↳ Azure AI Foundry portal → your resource → Keys and Endpoint → KEY 1 (or Entra ID: pass extra={'azure_ad_token_provider': ...})
export OPENAI_API_VERSION=2024-10-21
# ↳ the REST API version your deployment supports, e.g. 2024-10-21 (Azure docs → 'API version lifecycle')Promptise loads the nearest .env file automatically, and variables already exported in your shell win. Pick an API version that supports the model you deployed. Reasoning parameters such as reasoning_effort arrived in 2024-12-01-preview, so for a GPT-5 model use a later version such as 2025-04-01-preview, or skip versions entirely with the v1 API described below.
With nothing set, the check lists every value and where to get it:
$ promptise models check azure:shop-agent
azure:shop-agent → azure_openai:shop-agent (Azure OpenAI (OpenAI models deployed in Azure AI Foundry))
model part: shop-agent — in the string form, your DEPLOYMENT name (Foundry → Deployments → Name); with Model(...), the model name (gpt-4o) — the deployment goes in deployment=
route: native integration (core)
AZURE_OPENAI_ENDPOINT: MISSING — Azure AI Foundry portal → your resource → Overview → Endpoint (https://<resource>.openai.azure.com/, no path)
AZURE_OPENAI_API_KEY: MISSING — Azure AI Foundry portal → your resource → Keys and Endpoint → KEY 1 (or Entra ID: pass extra={'azure_ad_token_provider': ...})
OPENAI_API_VERSION: MISSING — the REST API version your deployment supports, e.g. 2024-10-21 (Azure docs → 'API version lifecycle')
Azure routes requests by deployment name, not model name: Model('gpt-4o', provider='azure', deployment='chat-prod', ...).
Not usable yet.
- AZURE_OPENAI_ENDPOINT is not set — Azure AI Foundry portal → your resource → Overview → Endpoint (https://<resource>.openai.azure.com/, no path) (e.g. https://my-resource.openai.azure.com/)
- AZURE_OPENAI_API_KEY is not set — Azure AI Foundry portal → your resource → Keys and Endpoint → KEY 1 (or Entra ID: pass extra={'azure_ad_token_provider': ...})
- OPENAI_API_VERSION is not set — the REST API version your deployment supports, e.g. 2024-10-21 (Azure docs → 'API version lifecycle') (e.g. 2024-10-21)
- put AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, OPENAI_API_VERSION in a .env file next to your script (loaded automatically; a variable already exported with a non-empty value wins, an empty one is filled from the file), export it, or pass it in
code: Model(..., endpoint=, api_key=, api_version=) — see promptise models env azureWith the three variables set, it reports the route and stops before calling anything:
$ promptise models check azure:shop-agent
azure:shop-agent → azure_openai:shop-agent (Azure OpenAI (OpenAI models deployed in Azure AI Foundry))
model part: shop-agent — in the string form, your DEPLOYMENT name (Foundry → Deployments → Name); with Model(...), the model name (gpt-4o) — the deployment goes in deployment=
route: native integration (core)
AZURE_OPENAI_ENDPOINT: set — Azure AI Foundry portal → your resource → Overview → Endpoint (https://<resource>.openai.azure.com/, no path)
AZURE_OPENAI_API_KEY: set — Azure AI Foundry portal → your resource → Keys and Endpoint → KEY 1 (or Entra ID: pass extra={'azure_ad_token_provider': ...})
OPENAI_API_VERSION: set — the REST API version your deployment supports, e.g. 2024-10-21 (Azure docs → 'API version lifecycle')
Azure routes requests by deployment name, not model name: Model('gpt-4o', provider='azure', deployment='chat-prod', ...).
Configuration OK — every required setting is in place; nothing was called. Add --ping to make a real one-token call to the model.The check proves the configuration is complete. --ping makes one real call, which also proves the deployment name and key are right.
[03]
Build a minimal Azure agent
The agent answers questions about a shop's products. The model is configured once at the top of the file:
import asyncio
import sys
from promptise import Model, build_agent
from promptise.config import StdioServerSpec
# Endpoint, key and API version come from AZURE_OPENAI_ENDPOINT,
# AZURE_OPENAI_API_KEY and OPENAI_API_VERSION (environment or .env).
AZURE = Model("gpt-5-mini", provider="azure", deployment="shop-agent")
async def make_agent(model=AZURE):
return await build_agent(
model=model,
servers={"shop": StdioServerSpec(command=sys.executable, args=["shop_server.py"])},
instructions="You answer questions about the shop's products. "
"Use the tools for stock and prices. Never guess them.",
max_agent_iterations=10,
)
async def main():
agent = await make_agent()
try:
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Is KB-101 in stock, and what does it cost?"}]}
)
calls = [c["name"] for m in result["messages"] for c in getattr(m, "tool_calls", None) or []]
print("tool calls:", ", ".join(calls) or "none")
print(result["messages"][-1].text)
finally:
await agent.shutdown()
if __name__ == "__main__":
asyncio.run(main())What each part does:
`Model("gpt-5-mini", provider="azure", deployment="shop-agent")`. The first argument is the model, which LangChain uses for token accounting. Requests go to the deployment: /openai/deployments/shop-agent/chat/completions on your endpoint. If you leave deployment out, it defaults to the model name, which is only right if you named the deployment after the model.
The string form is "azure:shop-agent", where the part after the colon is the deployment name. Use it when everything else is in the environment.
`max_agent_iterations=10` stops a run that keeps calling tools without finishing.
`result["messages"][-1].text` reads the answer text.
To keep credentials in code instead, for example when they come from a secret store, pass them as words:
AZURE = Model(
"gpt-5-mini",
provider="azure",
deployment="shop-agent",
endpoint="https://my-resource.openai.azure.com/",
api_key=secrets["azure-openai-key"],
api_version="2025-04-01-preview",
)A value given in code counts as provided, so the matching variable isn't needed. A blank string doesn't count, so an empty ${AZURE_OPENAI_API_KEY} in a config file falls back to the environment instead of sending an empty key.
Run it with python azure_agent.py. With a valid deployment, the script prints the tools the model called and then its answer.
[04]
Use MCP tools with Azure OpenAI
The tools come from a small MCP server the agent starts over stdio:
"""A small MCP tool server: product stock and prices for a shop."""
from promptise.mcp.server import MCPServer
server = MCPServer("shop")
STOCK = {"KB-101": 14, "MS-220": 0, "HD-350": 3}
PRICES = {"KB-101": 89.00, "MS-220": 39.50, "HD-350": 129.00}
@server.tool()
async def check_stock(sku: str) -> dict:
"""How many units of a product (by SKU) are in stock."""
if sku not in STOCK:
return {"sku": sku, "error": "unknown SKU"}
return {"sku": sku, "in_stock": STOCK[sku]}
@server.tool()
async def get_price(sku: str) -> dict:
"""The current price of a product (by SKU), in USD."""
if sku not in PRICES:
return {"sku": sku, "error": "unknown SKU"}
return {"sku": sku, "price_usd": PRICES[sku]}
if __name__ == "__main__":
server.run() # stdio by defaultyour code ──▶ Promptise agent ──HTTPS──▶ https://<resource>.openai.azure.com/
│ ▲ /openai/deployments/shop-agent/chat/completions
│ └── tool_calls ◀───┘
▼
MCP client ──stdio──▶ shop_server.py ──▶ tool result ──▶ next model callPromptise is the MCP client. It connects to each server in servers=, lists the tools and sends them to the deployment as function definitions on every call. When the model returns tool calls, Promptise runs them against the MCP server and calls the model again with the results. The MCP server never talks to Azure, so it can stay on a private network next to the agent. To connect a remote MCP server, use HTTPServerSpec(url=..., bearer_token=...) from promptise.config. The MCP connection guide covers the options.
[05]
Azure-specific notes
The v1 API: no API version
Azure's v1 API accepts the standard OpenAI client at https://<resource>.openai.azure.com/openai/v1/ and needs no api-version. In Promptise, that is the openai provider with your Azure endpoint. The model is the deployment name:
AZURE_V1 = Model(
"shop-agent",
provider="openai",
endpoint="https://my-resource.openai.azure.com/openai/v1/",
api_key=secrets["azure-openai-key"],
)Use this route when you don't want to track API versions. The classic provider="azure" route remains the one promptise models check explains field by field.
Keyless sign-in with Entra ID
Some organizations disable key authentication on their Azure OpenAI resources. With Entra ID, drop api_key and pass a token provider from azure-identity, which you install yourself (pip install azure-identity):
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
token = get_bearer_token_provider(
DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
AZURE = Model(
"gpt-5-mini",
provider="azure",
deployment="shop-agent",
endpoint="https://my-resource.openai.azure.com/",
api_version="2025-04-01-preview",
extra={"azure_ad_token_provider": token},
)The identity running the agent (your az login account locally, a managed identity in Azure) needs the Cognitive Services OpenAI User role on the resource. Tokens are refreshed for you. Microsoft's managed identity guide covers role assignment.
Reasoning models and tools
GPT-5 and o-series models are reasoning models. They reject temperature, top_p and the old max_tokens field, so leave temperature out of Model(...). max_tokens= in Model(...) is safe, because LangChain sends it as max_completion_tokens. To set reasoning depth, pass extra={"reasoning_effort": "low"}.
Promptise calls Azure through the Chat Completions API. Microsoft's reasoning models page notes that some newer models, such as the gpt-5.6 family, can't combine reasoning and function tools on Chat Completions and return an error when tools are sent. For those deployments, either set extra={"reasoning_effort": "none"} or deploy a model that supports tools with reasoning on Chat Completions. Check the page for the model you plan to use.
Quotas, content filters and pricing
Each deployment has a tokens-per-minute quota, and requests beyond it get HTTP 429. Agents send several model calls per question, so size the quota for that. See quotas and limits. Azure also runs content filters on prompts and completions, so a blocked request fails with a content filter error rather than an answer. Prices depend on the model, region and deployment type. See the Azure OpenAI pricing page, and put a budget on the agent.
Other models in Azure AI Foundry
Llama, Mistral, DeepSeek and the rest of the Foundry catalog use a different endpoint (https://<resource>.services.ai.azure.com/models) and key. In Promptise that is provider="foundry" with AZURE_INFERENCE_ENDPOINT and AZURE_INFERENCE_CREDENTIAL. The models reference has the details.
[06]
Common problems and fixes
Symptom | Cause | Fix |
|---|---|---|
---|---|--- | ||
| A value is missing from the environment and the code | Set it in .env, export it, or pass endpoint=, api_key=, api_version= |
HTTP 404 DeploymentNotFound | The deployment isn't the Name column in Deployments, or the endpoint belongs to another resource | Copy the Name, not the Model, and check the endpoint |
HTTP 401, access denied | Wrong key, or a key from another resource | Copy KEY 1 from the same resource as the endpoint |
HTTP 401 or 403 with Entra ID | The identity lacks the role, or the token scope is wrong | Assign Cognitive Services OpenAI User, and use the cognitiveservices.azure.com/.default scope |
HTTP 400 about the API version or an unsupported parameter | The API version is too old for the model or the parameter | Use a newer version from the lifecycle page, or the v1 API |
HTTP 400 saying temperature is an unsupported parameter | temperature set for a reasoning model | Remove it, and use reasoning_effort instead |
| A model that can't combine tools and reasoning on Chat Completions | Set reasoning_effort to none, or use another model |
HTTP 429 | The deployment's tokens-per-minute quota | Raise the quota, lower concurrency, or add a FallbackChain to a second deployment |
OpenAIConnectionError: Connection error. | The endpoint host doesn't exist or isn't reachable (private endpoint, firewall, typo) | Check the endpoint and that the agent runs inside the allowed network |
The last row is the real error the example printed with an endpoint that doesn't exist. The other rows are described from Azure's and Promptise's documentation, not from a recorded run.
[07]
Frequently asked questions
What is the difference between the model name and the deployment name in Azure OpenAI?
The model is what you deployed, such as gpt-5-mini. The deployment name is the label you chose when you deployed it, such as shop-agent, and it's what Azure uses to route each request. In Promptise, put the model in the first argument of Model(...) and the deployment in deployment=. In the string form "azure:shop-agent", the part after the colon is the deployment name.
Which API version should I use for an Azure OpenAI agent?
Use one that supports your model and the features you need, listed on Azure's API version lifecycle page. For GPT-5 models with reasoning settings, use 2024-12-01-preview or later. If you'd rather not track versions, use the v1 API at https://<resource>.openai.azure.com/openai/v1/ through Promptise's openai provider. It needs no version.
Can I use Azure OpenAI without API keys?
Yes. Pass a token provider from azure-identity in extra={"azure_ad_token_provider": ...} and leave api_key out. DefaultAzureCredential uses your az login account locally and a managed identity in Azure. The identity needs the Cognitive Services OpenAI User role on the resource.
Do MCP tools work with Azure OpenAI's data and network restrictions?
Yes. Promptise runs the MCP client in your process, so the MCP servers only need to be reachable from the agent, not from Azure. Azure only sees the tool definitions and the tool results in the conversation. If tool results contain sensitive data, they're sent to your deployment like any other prompt content, under your Azure data terms.
Can I switch an Azure agent to OpenAI or another provider?
Yes. Change the Model(...) line, for example to "openai:gpt-5-mini" or a Claude model, and keep the rest of the agent. The instructions and MCP servers don't change. Rerun your tests after the switch, because the same model on Azure and on OpenAI can still differ in version and content filtering.
[08]
Next steps
Build the full agent step by step: how to build an AI agent.
Compare models before you deploy one: best LLM for AI agents.
Serve several customers from one agent: multi-tenant AI agent.
Run it in production: deploy AI agents in production.
Write the tool side: build a production MCP server.
Every Azure word in one place: Models and providers reference.