Back to Blog

Enterprise AI · Azure · MCP

From Proof of Concept to Production: Azure AI That Actually Ships

Bibin Prathap

Microsoft MVP · Abu Dhabi, UAE

bibinprathap@gmail.com

The hook

90% of Azure AI demos work. Most never ship. The differentiator is architecture, not the model. Everything in this guide comes from a real build — the VeritasGraph medical MCP server deployed on Azure, plus a real Power BI natural-language agent — walking an Azure AI agent from a POC that impresses to a system that survives production.

Keywords— Azure AI, Azure AI Foundry, Model Context Protocol (MCP), GraphRAG, Azure Functions, Flex Consumption, Azure OpenAI, Production AI, MLOps, LLMOps, Medical AI, Power BI RLS
Watch: closing the POC-to-production gap for Azure AI.

I. THE PRODUCTION GAP

The demo works on stage. Then the client asks "is this production-ready?" — and every question below is exactly what the demo quietly skipped. Bridging that gap is not about a bigger model or a cleverer prompt; it is about the engineering column on the right.

The POC shows…Production also demands…
A model answering a promptGrounding in your data with citations
"It worked in the notebook"Audit trail a compliance reviewer can act on
One user, your loginIdentity + RBAC at the boundary
In-memory stateExternalized, durable state
Happy-path callsResilience to bad params, throttling, tool failures
"Trust me, it's fine"Observability — logs, traces, evals
Runs somewhereData residency & compliance

II. OUTCOMES

By the end of this walkthrough you can answer, with evidence, the five questions that decide whether an Azure AI system ships:

  1. Ground it — RAG/GraphRAG so answers come from your data, not the model's memory.
  2. Externalize state — never trust serverless in-memory state.
  3. Secure it — identity at the boundary; entitlements enforced before data is used.
  4. Observe it — logs, traces, and groundedness/safety evaluations.
  5. Ship it compliant — region-pinned, PHI-aware, least-privilege.

III. THE REFERENCE BUILD

VeritasGraph is a remote MCP (Model Context Protocol) server that an Azure AI Foundry agent calls to answer clinical questions with verifiable citations.

Why medical? A regulated domain forces every production concern at once — grounding, audit, RBAC, residency. If it ships here, the patterns generalize to any enterprise workload. The server exposes four tools an agent can call:

ToolPurpose
veritasgraph_queryMulti-hop, graph-grounded answer with [doc#chunk] citations + reasoning path
veritasgraph_search_entitiesFast subgraph retrieval — no LLM call
veritasgraph_ingest_documentChunk text, extract entities/relationships (Azure OpenAI), merge into the graph
veritasgraph_get_graphReturn the full knowledge graph: nodes, edges, stats

IV. CORE ARCHITECTURE

Read the architecture left-to-right — every box exists to answer a production question, not to look clever. The client or agent authenticates through identity (Entra ID + function key), reaches the MCP server hosted on Azure Functions (Flex Consumption), which drives the graph engine, calls Azure OpenAI for extraction and reasoning against the knowledge graph, and persists everything to a durable Azure Files mount with Storage and Application Insights alongside.

Reference architecture — VeritasGraph MCP on Azure Functions, Azure OpenAI, knowledge graph and durable Azure Files mount
Reference architecture — every box exists to answer a production question.
Foundry Agent / MCP client
   → Identity (Entra ID + function key)
      → Azure Functions (Flex Consumption, 4 mcpToolTrigger tools)
         → veritasgraph-mcp + graphrag_engine
            → Azure OpenAI (extraction + reasoning)
            → Knowledge Graph
         → Durable Azure Files mount (externalized state)
         → Storage + App Insights (observability)

The Azure Files mount is the cold-start and state fix — the single change that turned a flaky serverless POC into a durable service.

V. BILL OF MATERIALS

ComponentChoiceWhy
Tool protocolMCPOpen protocol — any agent can call your tools
ComputeAzure Functions, Flex ConsumptionScales to zero for cost; fast event-driven scale-out; VNet support
StateAzure Files mountServerless memory is wiped on scale/restart — externalize it
InferenceAzure OpenAI (gpt-5-mini, swappable)Managed, in-tenant, region-pinnable
IdentityEntra ID + function/system keyIdentity at the boundary
ObservabilityApplication InsightsLogs, latency, dependencies

VI. FROM POC TO DEPLOYED — THE BUILD PATH

A. Scaffold the MCP server

Initialize the Python v2 programming model and add the MCP tool functions. All four tools live in a single function_app.py with @app.mcp_tool_trigger decorators, and host.json enables the experimental extension bundle that provides the MCP trigger.

func init veritasgraph-mcp --worker-runtime python --model V2
cd veritasgraph-mcp

B. Host on Flex Consumption

Flex Consumption is the production hosting choice: scale-to-zero cost, fast event-driven scaling, VNet support, and optional Always Ready instances to kill cold starts — without Premium's mandatory always-on cost.

az functionapp create \
  --name $FUNCAPP \
  --resource-group $RG \
  --storage-account $STORAGE \
  --flexconsumption-location $LOCATION \
  --runtime python --runtime-version 3.11 \
  --functions-version 4

C. Deploy with a remote build

local.settings.json is never uploaded — push the same values as application settings (using a Linux-writable path for state), then publish with a remote build so the engine installs on the Linux host.

func azure functionapp publish $FUNCAPP --build remote
Built-in MCP test UI validating the four veritasgraph_* tools with the warfarin and amiodarone scenario
Validating the four tools locally before deploying — warfarin + amiodarone scenario.

D. Connect the Azure AI Foundry agent

In the Microsoft Foundry Agents Playground, add a Model Context Protocol (MCP) tool pointing at the remote endpoint with the x-functions-key header. Then give the agent instructions that require grounded, cited answers: use the VeritasGraph tools, ingest first if the graph lacks the facts, and always return [doc#chunk] citations plus the reasoning path.

Deployed Azure Function App showing the four mcpToolTrigger MCP tool functions running
The deployed Function App exposes the four veritasgraph_* MCP tools.
Azure AI Foundry MCP connection configured with the remote endpoint and x-functions-key header
Connecting the Foundry agent to the remote MCP endpoint with an x-functions-key.
Medical agent in the Foundry Playground returning a cited, grounded answer with reasoning path
The agent answers the clinical question with citations and the reasoning path.

VII. GRAPHRAG — MULTI-HOP, CITED ANSWERS

veritasgraph_ingest_document chunks text, calls Azure OpenAI to extract entities and relationships, and records the source chunk behind every node and edge. The punchline is multi-hop reasoning: no single chunk states the answer; the graph assembles it from separate edges.

Ingesting a clinical note into the knowledge graph — entity and relationship extraction
Ingestion chunks the note and extracts entities and relationships with Azure OpenAI.
The built knowledge graph showing nodes, edges and their source chunks
The built knowledge graph — nodes, edges and the source behind each.
Amiodarone --inhibits--> CYP2C9 --metabolizes--> Warfarin --increases--> Bleeding Risk

That 3-hop chain is the argument for GraphRAG over plain vector RAG when relationships carry the meaning — and every hop is traceable.

VIII. AUDITABILITY & EXPLAINABILITY

veritasgraph_query returns an answer plus citations plus a reasoning path:

{
  "answer": "Reduce the warfarin dose for patient 4471 because amiodarone inhibits CYP2C9...",
  "citations": ["doc_warfarin_note#0", "doc_warfarin_note#1"],
  "reasoning_path": ["Amiodarone → CYP2C9", "CYP2C9 → Warfarin", "Warfarin → Bleeding Risk"]
}

This is explainability a compliance reviewer can act on — often the feature that unblocks the whole project in regulated clients.

IX. SEMANTIC-LAYER ACCESS CONTROL

Access control belongs at the boundary, not in the prompt. Carry the Entra identity; query Power BI RLS / Dataverse roles / Dynamics security on-behalf-of the user so entitlements apply before data reaches the graph; partition or label-filter the subgraph; re-check citations at query time.

A second shipped system makes this concrete: a Power BI natural-language → DAX agent. A question validates the user's Power BI OAuth token → discovers schema → generates DAX → executes via the Power BI executeQueries REST API → self-corrects on error (up to 3×) → summarizes. Because it calls Power BI with the user's token, row-level security is enforced by the platform — not re-implemented in code.

Power BI Manage security roles — row-level restrictions defined on the semantic model
Row-level security is defined once on the semantic model — filtered per role.
Power BI Manage security roles dialog creating row-level data restrictions across tables
Manage security roles — row-level filters applied per role on the semantic model.
Power BI semantic model showing the related tables and relationships in model view
The Power BI semantic model stays the source of truth for what a user can see.
Power BI executeQueries REST API returning results for the generated DAX query
The agent generates DAX and runs it live via the Power BI executeQueries REST API.
Power BI natural-language to DAX agent flow built with Azure AI Foundry
Shipped for real — the Power BI natural-language → DAX agent flow.
The line that lands: the semantic layer owns "who can see what"; the graph owns "what is true and where it came from."

X. PRODUCTION TRADE-OFFS

Four decisions that decide whether it ships:

DecisionRule of thumb
RAG vs fine-tuneRAG for facts; fine-tune for behaviour
Vector vs hybrid vs graphHybrid wins on IDs/entities; graph when relationships carry meaning
Managed vs custom inferenceManaged (Azure OpenAI) first
Stateless vs statefulNever trust serverless memory — externalize state

XI. FAILURE-RESILIENT DESIGN & OBSERVABILITY

Resilience is design, not luck. Anchored by real examples from this build:

  • Tolerate model constraints gpt-5-mini rejects temperature=0; omit unsupported params, don't fail.
  • Self-correct — the DAX loop feeds the error + schema back and retries up to 3×.
  • Externalize state — Flex Consumption wiped the in-memory graph; fixed with a mounted Azure Files share.
  • Validate inputs, handle throttling, degrade gracefully.

You can't ship what you can't see. Observability layers:

  • Application Insights — logs, latency, dependencies.
  • Foundry Traces — tool selection and arguments.
  • Evaluations — groundedness and safety.
  • Alerts — cost and throttle thresholds.
Application Insights dashboard for the Function App — usage, reliability, responsiveness and server response time
Application Insights — usage, failures, latency and dependencies for the Function App.

XII. DATA RESIDENCY & COMPLIANCE

The regulated-client reality:

  • Region-pin every resource; keep inference in-tenant.
  • Handle PHI with guardrails and classification.
  • Secrets in Key Vault; managed identity over shared secrets.
  • Least privilege via semantic-layer RLS.

Foundry guardrails apply content-safety and sensitive-data (PHI/PII) controls around every tool call. When a request would leak protected data, the guardrail blocks it before it ever reaches the model. Compliance and velocity aren't a trade-off if you design for it.

Azure AI Foundry Medical Guardrails policy with PII/PHI sensitive-data recognition scoped to the agent
A Medical Guardrails policy applies PHI/PII controls around every tool call.
Foundry guardrail blocking a request that would leak protected health information
The guardrail blocks a PHI-leaking request before it reaches the model.
Foundry Agents Playground showing a PHI request blocked by the agent's Medical Guardrails control
In the Playground, a request for patient names and phone numbers is blocked by the agent guardrail.

XIII. FIELD LESSONS — REAL WAR STORIES

All real, all from this build, all passed the demo but broke production until fixed:

  1. Interpreter/venv mismatch ModuleNotFoundError (host used global Python).
  2. Wrong Azure OpenAI deployment name 404.
  3. gpt-5 temperature constraint → omit the param.
  4. Flex Consumption wiped in-memory state → mount Azure Files.
  5. MCP required-property validation hard-failing agent calls.
  6. A competing web-search tool silently overriding grounding.
  7. Secrets committed in local.settings.json → move to app settings / Key Vault.

XIV. THE PRODUCTION-READY CHECKLIST

This is the answer when a client asks "is this production-ready?"

  • Grounded — answers cite your data ([doc#chunk]).
  • State externalized — no reliance on serverless memory.
  • Identity at the boundary — Entra + keys; on-behalf-of for data.
  • Entitlements enforced before data reaches the model (RLS/roles).
  • Resilient — bad params, throttling, tool failures handled.
  • Observable — logs, traces, evals, cost alerts.
  • Region-pinned & compliant — inference in-tenant, PHI-aware.
  • Secrets in Key Vault — managed identity, least privilege.
  • Reproducible deploy — remote build, app settings, pinned config.

RECAP

Synthesized in one breath: ground it, externalize state, secure it, observe it, make it resilient, keep it compliant. The gap between a POC and production is architecture — close it deliberately.

Bibin Prathap — Microsoft MVP

Enterprise Knowledge Graphs, MCP, GraphRAG and on-prem GenAI for regulated environments. Presented at the MCT Summit — Azure AI & Microsoft Copilot general session.