A minimal .NET / ASP.NET Core chat app that runs on Embr and uses an Azure
AI Foundry-deployed model as its LLM. The agent loop is orchestrated locally
inside this app via Microsoft Agent Framework (Microsoft.Agents.AI); only
the LLM inference call reaches Foundry.
This is the .NET sibling of embr-foundry-chat-sample-python.
Same UI, same tools, same flows — different stack.
- ASP.NET Core minimal-API web app on
net10.0. - Two local tools the agent can call:
GetWeatherandRollDice— plain static methods with[Description]attributes, registered viaAIFunctionFactory.Create. - Foundry model is configured through the OpenAI-compatible endpoint (
OpenAIpackage +OpenAIClientOptions { Endpoint = ... }), so the same code path works against Azure OpenAI or Foundry without any SDK swap. - Single static
wwwroot/index.htmlchat UI (markdown via marked + DOMPurify from CDN, no bundler).
app/
Program.cs # endpoints, message history, static mount
AgentFactory.cs # Microsoft.Agents.AI agent + Foundry client construction
Tools.cs # local tools the agent can invoke
wwwroot/index.html # chat UI
embr.yaml # Embr deploy config (platform: dotnet)
.env.example # required env vars (copy to .env for local runs)
Three env vars (see .env.example):
| Variable | Purpose |
|---|---|
FOUNDRY_BASE_URL |
OpenAI-compatible endpoint for your Foundry project, e.g. https://<resource>.services.ai.azure.com/api/projects/<project>/openai/v1 |
FOUNDRY_API_KEY |
API key from the Foundry resource (Keys + Endpoint page) |
FOUNDRY_MODEL_DEPLOYMENT |
Deployment name, e.g. gpt-4o-mini, gpt-5.4-mini-1 |
- In the Foundry portal, create a project and deploy
a chat-completion model (e.g.
gpt-5.4-mini-1). - Open the deployment → Target URI. It looks like
https://<resource>.services.ai.azure.com/api/projects/<project>/openai/v1/responses. Strip the trailing/responsesto get yourFOUNDRY_BASE_URL. - From the resource page → Keys + Endpoint → copy a key into
FOUNDRY_API_KEY. - Use the deployment name (not the model name) as
FOUNDRY_MODEL_DEPLOYMENT.
cd app
cp ../.env.example ../.env # then fill in real values
dotnet runThen open http://localhost:5000 (or whatever port dotnet run reports).
Smoke test from another terminal:
curl http://localhost:5000/health
curl http://localhost:5000/api/config
curl -X POST http://localhost:5000/api/chat \
-H 'Content-Type: application/json' \
-d '{"message":"Roll a 20-sided die"}'gh repo create embr-foundry-chat-sample-dotnet --source=. --public --push
embr quickstart deploy <your-user>/embr-foundry-chat-sample-dotnet
embr variables set FOUNDRY_BASE_URL=... FOUNDRY_API_KEY=... FOUNDRY_MODEL_DEPLOYMENT=...The first deploy provisions the project + environment and triggers a build. Subsequent pushes auto-deploy.
| Path | Method | Purpose |
|---|---|---|
/ |
GET | Chat UI (static wwwroot/index.html) |
/health |
GET | Liveness probe |
/api/config |
GET | Resolved Foundry endpoint summary (host/project/model) |
/api/chat |
POST {message, threadId?} |
Run one agent turn |
/api/reset |
POST ?thread_id=… |
Clear server-side history for a thread |
- Agent and message history are in-memory — fine for the demo, not durable.
- Errors from the model surface as HTTP
502with the provider message indetail. The user message is rolled back on failure so retries don't compound. - Same conversation shape as the Python sample (
{role, content}over aChatRole/ChatMessagelist).