This repo is a complete, Python-first demo that connects:
- Two MCP servers (Policy + Orders) exposed over SSE + JSON-RPC
- A Microsoft Agent Framework (Agents SDK) agent backed by Azure OpenAI
- A modern, lightweight AG‑UI web app (served by FastAPI) that renders:
- streaming assistant text
- tool calls (args/results)
- images (order photos)
- human approval cards for state-changing actions
It’s designed as a practical pattern you can reuse: keep backends as MCP tools, keep the “agent brain” in one service, and give users a UI that makes tool activity visible and trustworthy.
- Location: app/policy-server/main.py
- Transport:
GET /ssefor server-sent eventsPOST /messages?session_id=...for JSON-RPC messages
- Tools:
list_policy_documents— lists policy blobs available in a containerread_policy_document— fetches a policy document from Azure Blob Storage
Important: policies are Blob Storage as the source of truth (no local file fallback).
- Location: app/order-server/main.py
- Tools:
get_order_detailslist_orders_by_statusupdate_order_statusget_order_photo— returns JSON withimageUrlset_order_photo— writesOrderPhotoUrl(optionally gated by UI approval in the agent service)
Schema helper:
- setup.sql creates the
Orderstable, includingOrderPhotoUrl.
- Location: app/agent_agui/main.py
- Responsibilities:
- Connects to both MCP servers via SSE
- Creates an Azure OpenAI agent using Microsoft Agent Framework
- Streams AG‑UI events over SSE from
POST /agui - Serves a modern UI from
GET / - Adds an interactive approval endpoint
POST /agui/approval
This service enriches the experience by emitting AG‑UI events such as:
- lifecycle:
RunStarted,RunFinished,RunError - streaming text:
TextMessageStart/Content/End - tool tracing:
ToolCallStart/Args/End/Result - progress:
StepStarted/StepFinished - rich UI:
Custom:imageandCustom:approval_request
It’s intentionally “boring-tech”: just HTML/CSS/JS. The UI parses the SSE stream and renders:
- a chat transcript
- a tools panel
- a raw event feed
- image cards when the agent emits
Custom:image - a glossy Approve/Reject card for
set_order_photo
- Clean boundaries: MCP servers stay focused on domain logic (policy retrieval, orders/SQL).
- Tool transparency: AG‑UI makes tool calls visible, not hidden.
- Trust & safety: state-changing actions can be gated behind human approval.
- Extensible UX: “tool returns imageUrl → UI shows image” is a great example of tool-shaped rendering.
- Docker Desktop
- Azure resources:
- Azure OpenAI: endpoint + chat deployment
- Azure SQL: reachable connection string
- Azure Blob Storage: container with policy docs
Copy and edit your env file:
- Create
.env(or edit existing) based on.env.sample
Common variables:
AZURE_OPENAI_ENDPOINTAZURE_OPENAI_CHAT_DEPLOYMENT_NAMEAZURE_OPENAI_API_KEY(or useaz loginlocally when not in Docker)SQL_CONNECTION_STRINGPOLICY_CONTAINER_NAME(defaults topolicies)
From repo root:
docker compose up --build -d
Services (default ports):
- Policy MCP server:
http://localhost:8000/sse - Order MCP server:
http://localhost:8001/sse - Agent + AG‑UI UI:
http://localhost:9000/ - Streamlit sample (optional):
http://localhost:8501/
- Show an order photo (image rendering)
- Ask in the UI: “Show me the photo for order 5390”
- Expected:
- tool call
get_order_photo Custom:imageevent- image card rendered
- tool call
- Attempt to set a photo (approval gating)
- Ask: “Set the photo for order 5390 to https://…/order_5390.jpg”
- Expected:
- approval card appears
- click Approve →
set_order_photoexecutes - click Reject → action is cancelled
- Policy lookup
- Ask: “List available policy docs, then read the hazardous policy.”
Orders include a nullable OrderPhotoUrl field (a URL). This is a pragmatic demo choice:
- It keeps SQL as the “index” and Blob as the “payload”.
- The UI can render the image directly once the URL is returned.
If you want to be stricter in production:
- store only a blob name/key in SQL
- generate short-lived SAS URLs in a dedicated service
- The MCP servers implement SSE transport:
- Client opens
GET /sse - Server emits an
endpointSSE event telling the client where to POST JSON-RPC messages
- Client opens
- The agent service uses an MCP client over SSE to call tools.
- The agent service streams AG‑UI events to the browser via
POST /agui. - The browser renders events live and can respond (approval) via
POST /agui/approval.
- If tools can’t reach Azure SQL, verify
SQL_CONNECTION_STRINGand network/firewall rules. - If policy docs aren’t found, check
POLICY_CONTAINER_NAMEand that your Blob container has the expected blobs. - If images don’t render, confirm the URL ends with a common extension like
.jpg/.pngand is publicly accessible (or your browser can access it).
- Microsoft Agent Framework (Python Agents SDK)
- Azure OpenAI
- MCP (Model Context Protocol)
- FastAPI + SSE
- Plain HTML/CSS/JS UI