Most agent samples stop at a single chat loop that calls one model and returns an answer. The moment you try to run that same agent as part of a real application, with several services, persistent state, and a deployment pipeline, the picture gets a lot more complicated. The .NET team has heard this complaint often enough that they built an open source sample called Interview Coach to answer it directly.
Interview Coach is a working mock interview simulator. You upload a resume and a job description, and the application walks you through a behavioral round and a technical round, then gives you a structured performance summary at the end. What makes it worth studying is not the interview logic itself, but how the sample wires together Microsoft Agent Framework, Microsoft Foundry, Model Context Protocol (MCP) servers, and Aspire into something you could actually ship.
What the sample actually does
The application runs through four stages. Intake collects your resume and the target job description. The behavioral stage asks STAR-method questions based on your experience. The technical stage asks role-specific questions. The summary stage generates a review with specific feedback on what you did well and where you fell short.
You interact with all of this through a Blazor web UI that streams the conversation in real time, so it feels like chatting with a human interviewer rather than waiting for a batch response.
Architecture at a glance
The application is split into five separate services, all wired together by Aspire. Microsoft Foundry is the recommended model backend and provides access to different models. The Blazor WebUI is the chat interface the candidate actually uses. The Agent service holds the interview logic built on Microsoft Agent Framework. A MarkItDown MCP server parses resumes in PDF or DOCX format into markdown. And an InterviewData MCP server, written in .NET, stores session state in SQLite.

Each of these runs as an independent process. Aspire handles service discovery between them, runs health checks, and wires up telemetry, so you are not stitching connection strings and ports together by hand.
Why Microsoft Agent Framework instead of Semantic Kernel or AutoGen
If you have built agents on .NET before, you have probably picked between Semantic Kernel and AutoGen and lived with the trade-offs of whichever one you chose. Microsoft Agent Framework is built by the same teams behind both, and it folds AutoGen’s agent abstractions together with Semantic Kernel’s enterprise features, things like state management, type safety, middleware, and telemetry, into one framework. It also adds graph-based workflows for coordinating multiple agents, which is the piece that makes a sample like this possible.
In practice this means you stop choosing between two frameworks and get one set of patterns to learn. Agents use dependency injection and IChatClient the same way any ASP.NET service would, so the hosting model feels familiar rather than bolted on. OpenTelemetry and Aspire integration come built in, and the framework supports sequential workflows, concurrent execution, handoff patterns, and group chat out of the box. Interview Coach uses these pieces in a real application rather than a toy example, which is exactly why it is worth reading through.
Why Microsoft Foundry as the model backend
An agent needs more than a model endpoint to run in production. Microsoft Foundry is Azure’s platform for building and managing AI applications, and it is the backend Microsoft recommends for Agent Framework. It gives you one portal for model access across a catalog from OpenAI, Meta, Mistral and others, built-in content moderation and PII detection, cost-aware routing across models, evaluation and fine-tuning tools, and governance through Entra ID and Microsoft Defender.
For Interview Coach specifically, Foundry supplies the model endpoint that powers every agent. Because the agent code talks to models purely through the IChatClient interface, Foundry is technically just a configuration choice rather than a hard dependency, but it is the option that gives you the most operational tooling without extra work.
Pattern 1: multi-agent handoff instead of agent-as-tools
This is the part of the sample that is genuinely worth studying closely. Instead of one large agent trying to handle intake, behavioral questions, technical questions and summarization in a single prompt, the work is split across five specialized agents.
- Triage: routes messages to the right specialist and holds no tools, since its job is pure routing.
- Receptionist: creates the session and collects the resume and job description, using both the MarkItDown and InterviewData tools.
- Behavioral Interviewer: runs the STAR-method questions, backed by the InterviewData tool.
- Technical Interviewer: asks role-specific technical questions, also backed by InterviewData.
- Summarizer: generates the final performance review, again using InterviewData.
The distinction that matters here is handoff versus agent-as-tools. In a handoff pattern, one agent transfers full control of the conversation to the next agent, and the receiving agent takes over completely. In an agent-as-tools pattern, a primary agent calls other agents the way it would call any tool, gets a result back, and stays in charge throughout. Interview Coach deliberately picks handoff, because each stage of an interview is genuinely a different conversation with different context, not a sub-task that needs to report back to a coordinator.
var workflow = AgentWorkflowBuilder
.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [receptionistAgent, behaviouralAgent, technicalAgent, summariserAgent])
.WithHandoffs(receptionistAgent, [behaviouralAgent, triageAgent])
.WithHandoffs(behaviouralAgent, [technicalAgent, triageAgent])
.WithHandoffs(technicalAgent, [summariserAgent, triageAgent])
.WithHandoff(summariserAgent, triageAgent)
.Build();
This is the workflow builder that wires up who can hand off to whom. The happy path is straightforward: Receptionist hands off to Behavioral, which hands off to Technical, which hands off to Summarizer. Every specialist also has a path back to Triage, which matters more than it looks. If the candidate says something off-script mid-interview, like asking to restart or switching topics, the current specialist does not need custom logic to handle it. It just hands back to Triage, which re-routes based on what the candidate actually said.
One thing worth calling out for anyone building on this pattern: handoff only works cleanly when each agent’s responsibility is genuinely self-contained. If your stages need to share running context, say the Summarizer needs details from the behavioral round that were never written to shared state, you will end up passing state through the MCP data server rather than through the handoff mechanism itself, since control transfer does not carry conversation memory automatically. Interview Coach solves this by pushing all session state into the InterviewData MCP server rather than keeping it in agent memory, which is worth noticing since it is easy to miss on a first read.
The sample also ships a single-agent mode alongside the handoff mode, so you can run both and compare directly rather than taking the handoff approach on faith. For simpler bots that do not need five distinct personas, single-agent mode is the more sensible starting point, and handoff only earns its complexity once you actually need distinct conversational contexts.
Pattern 2: MCP servers instead of tools baked into the agent
Tools in this project do not live inside the agent process. They run as separate MCP (Model Context Protocol) servers that the agent discovers and calls at runtime. This has a practical benefit beyond tidiness: the same MarkItDown server could power a completely different agent project without any changes, and the team building tools can ship on its own schedule instead of waiting on the agent team. MCP is also language-agnostic, which is exactly why MarkItDown runs as a Python server while the agent itself is written in .NET.
var receptionistAgent = new ChatClientAgent(
chatClient: chatClient,
name: "receptionist",
instructions: "You are the Receptionist. Set up sessions and collect documents...",
tools: [.. markitdownTools, .. interviewDataTools]);
The agent discovers tools at startup through MCP clients and hands them to whichever agent needs them. Notice that Triage gets no tools at all since its only job is routing, the two interviewer agents get session access through InterviewData but nothing else, and the Receptionist alone gets both document parsing and session access. That is the principle of least privilege applied at the agent level rather than the user level, and it is a pattern worth copying even outside this sample: do not hand every agent every tool just because it is convenient during development.
Pattern 3: Aspire for orchestration
Aspire is what ties five independent services into one thing you can actually run and reason about. The app host defines which services exist, how they depend on each other, and what configuration each one receives. You get service discovery, so services find each other by name instead of hardcoded URLs, health checks that show up on a dashboard, and distributed tracing wired through shared service defaults, all without writing separate infrastructure code for each piece.

A single command, aspire run –file ./apphost.cs, starts every one of these services together and gets you a dashboard that shows exactly what is running and where. For deployment, azd up pushes the whole application to Azure Container Apps in one step. This is the difference between a demo that only works on the author’s laptop and a sample you can actually hand to a team.
Running it locally and deploying to Azure
Before you clone the repository, you need the .NET 10 SDK or later, an Azure subscription with a Microsoft Foundry project, and Docker Desktop or another container runtime for the services that need containers. Once those are in place, the local setup is short.
git clone https://github.com/Azure-Samples/interview-coach-agent-framework.git
cd interview-coach-agent-framework
dotnet user-secrets --file ./apphost.cs set MicrosoftFoundry:Project:Endpoint "<your-endpoint>"
dotnet user-secrets --file ./apphost.cs set MicrosoftFoundry:Project:ApiKey "<your-key>"
aspire run --file ./apphost.cs
This clones the repository, stores your Foundry endpoint and API key as user secrets rather than in plain text config, and starts every service through Aspire. Once you run this, open the Aspire dashboard, wait until every component shows as Running, and click through to the WebUI endpoint to start a mock interview. A common mistake here is starting the interview before all services report healthy, since the Receptionist will fail silently if the MCP servers have not finished initializing yet.
azd auth login
azd up
These two commands authenticate against Azure and deploy the full application to Azure Container Apps. Once you are done testing, run azd down –force –purge to remove every resource cleanly, otherwise the Foundry project and container apps will keep billing quietly in the background.
DevUI and the chat experience
Microsoft Agent Framework ships with DevUI, a visual tool that renders the handoff graph as you built it in code. It is a genuinely useful way to confirm your workflow wiring matches what you intended before you hand a session to a real user.

The candidate-facing side looks nothing like this diagnostic view. It is a plain chat interface where the candidate pastes in a resume link and a job description link, and the assistant responds with STAR-formatted questions one at a time.

Where this sample is genuinely useful, and where it is not
If your use case is a single well-defined conversation, handoff orchestration and five separate MCP servers are overkill. The single-agent mode included in the sample is the honest starting point for most teams, and you should only reach for handoff once you have concrete evidence that your workflow has distinct stages with genuinely different context and tool needs.
Where this sample earns its complexity is in scenarios that mirror Interview Coach closely: multi-stage conversations where each stage needs different tools, different instructions, and possibly different models, and where you want tool teams and agent teams to ship independently. It is also a solid reference if you are evaluating Aspire for the first time, since the orchestration story here is more complete than most getting-started guides show.
One gap worth flagging for anyone extending this into a real product: the sample does not include evaluation harnesses or automated regression tests for the agent conversations themselves, which matters a lot once you start changing prompts or swapping models. The Microsoft Foundry evaluation tooling mentioned earlier in the architecture is the natural place to plug that in, but it is not wired up in the sample as it stands, so budget for that work separately if you are taking this to production.
Leave a Reply