MCP Apps: Give the Model Data, Give the User a UI — Dustin Mihalik, Indeed
Read the talk
MCP Apps: Give the Model Data, Give the User a UI
Dustin Mihalik’s lessons from Indeed show why an attractive widget can weaken an agent—and how separating search, selection, rendering, and interaction context preserves both model capability and a useful interface.
From a talk by Dustin Mihalik
At a glance
Ideas worth remembering
Anything visible in an MCP App must also be available as model-readable data, or follow-up questions encounter a black box.
Tell the model that the tool renders UI; otherwise it may repeat the widget’s contents in prose.
Push meaningful interaction state—such as the selected job or shopping-cart contents—back into model context.
Separate repeatable data-processing tools from deliberate rendering tools so UI does not suppress exploration or multiply into clutter.
Build small composable search and render tools, and let the final render call include the model’s reasons or highlighted evidence.
Why a job-search UI is worth building
Dustin Mihalik works on job search, AI platform infrastructure, guardrails, gateways, and compliance at Indeed. His practical lessons come from building MCP Apps for Claude, ChatGPT, and Indeed’s Career Scout job-seeker agent. The examples revolve around a useful question: how can a chat host give the model enough freedom to search while giving the user an interface that feels like a real product? 0:14
A plain-text search for a barista job in Austin already works reasonably well. The model can divide results between Austin and nearby suburbs and explain what it found. But Indeed loses control over presentation: its branding disappears, the model chooses the organization, and links may not appear consistently. Mihalik says the team spent a “ridiculous number of hours” on evaluations intended to make Claude link to job results reliably. Five jobs “somewhere on the internet” are not very useful if the user cannot open them. 1:43
An MCP App can control the parts that matter. A job card can include an Apply button, emphasize selected information, and open View Details in a modal without sending the user away from the conversation. The host keeps the user in its environment, while Indeed supplies a recognizable and actionable job-search experience. That is the genuine advantage of UI—not decoration, but a controlled path from a result to the next action. 2:43
The trap is assuming that an existing website can simply be dropped into chat. Once a widget loads data through its own APIs, the user may see a rich interface while the model sees only that a tool ran. Product presentation improves, but the conversational agent loses access to the substance it needs for follow-up work. 3:39
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Keep the model’s data view aligned with the UI
Suppose a widget displays ten jobs and the user asks, “Tell me about the first result” or “Rank these companies.” If the widget fetched those jobs privately, the model cannot identify the first item, inspect the companies, or compare them. The UI has become a black box. Mihalik’s first rule follows directly: anything shown to the user must also be provided as data to the model. 4:09
- Return model-readable data: The tool should return the structured content that a text-based MCP integration would already expose.
- Return the interface resource: The accompanying
resourceURIidentifies the HTML used to render the experience. - Maintain parity: When the displayed API response gains a field or changes shape, the model-visible representation must change with it. Returning both forms once is insufficient if they later drift apart.
Giving the model the data creates a second problem: duplication. The widget renders the results, then the model behaves as if no interface exists and narrates the same list underneath it. The tool description must therefore explain the presentation side effect. Mihalik found that a direct instruction such as results were automatically displayed to the user as UI components covers many cases. The model can then offer a short summary and point to the displayed links instead of reconstructing the entire UI in prose. 5:30
These two requirements solve different problems. Model-readable data preserves reasoning and follow-up questions; the tool description coordinates who presents the answer. There will still be some tension between what belongs in prose and what belongs in the widget, but the description gives the model enough information to avoid obvious repetition.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A click is new context, not merely a UI event
Static result parity does not cover what happens after rendering. Imagine that the widget returns ten jobs and the user opens one job’s View Details modal. The interface now contains a full job description, but the model may not know which job was selected or what appeared in the modal. Requests such as “summarize this description” or “write a cover letter for this job” fail for the same reason as the original black-box widget: the user and model are looking at different state. 6:30
Interaction state must therefore flow back into model context. The MCP Apps mechanism Mihalik describes is update model context, which accepts a string. Because the interface supplies a single string, an app that needs a history of several events may have to append them over time. The model does not need every low-level click; it needs the resulting semantic state, such as which job is open and which details the user can now see. 7:20
A shopping-cart example makes the pattern concrete. The app can write the cart’s total cost and item list into model context, allowing the user to ask about the items currently in the cart. For job search, the equivalent update would identify the selected job and expose the relevant description. The observable change is simple—a modal opens—but the causal chain matters: the click changes application state, the app summarizes that state into model context, and only then can the model answer a referential follow-up such as “this job.”
At this point the model can see both rendered results and subsequent interactions. Yet the architecture can still make the overall product worse, because visibility alone does not preserve the model’s willingness to explore.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Why coupling search to a widget stops exploration
Mihalik does not use an agent merely to perform one easy search: “I give Claude my really hard problems to solve.” His richer job-search request spans a desired title, several possible relocation cities, compensation preferences, and industries to exclude. With text-based MCP, the model can run ten or fifteen searches, collect candidates, filter them, and produce one final table. 8:36
Attaching rendering directly to that search changes the model’s behavior. After one call produces a carousel, the model may treat the results as already delivered and stop the deeper search. Repeating the call would also create ten different carousels, which neither the user nor the model is likely to want. This was an observed failure in the presented workflow, not a quantified comparison across models or deployments, so its frequency remains uncertain; the architectural consequence is still clear when it occurs. 9:43
Mihalik’s third rule supersedes the first two: separate data processing from UI rendering. Search should remain a plain, model-readable operation that can run repeatedly without producing interface clutter. A separate render tool should be called only after the model has explored, compared, and selected what deserves presentation. 10:13
What does that split make possible? The flow below follows the concrete Indeed example from a broad request to five displayed jobs.
The relationship to inspect is the gap between retrieval and presentation. The model can call search jobs as often as needed, accumulate perhaps 100 candidates, and reduce them to five. Only those five IDs cross into the render widget. UI becomes the result of reasoning rather than a side effect of every exploratory call. 10:43
The render tool’s description must identify where its inputs come from and what format it expects. For example, it can instruct the model to call one of several data tools before rendering. This is a modest amount of coordination logic, but it preserves a valuable asymmetry: data tools are cheap to compose and repeat, while rendering is deliberate and sparse.
How can the model search 100 jobs but render only five?
Read the diagram as text
- Complex job request. Title, multiple cities, compensation preference, and excluded industries.
- Plain search tools. Data processing — Return data without rendering UI and can be called repeatedly.
- 100 candidate jobs. Data processing — A broad working set gathered during exploration.
- Model filters to five. Model reasoning — Applies the user’s location, pay, title, and industry constraints.
- Render jobs widget. UI rendering — Accepts the selected list of job IDs.
- Five visible job cards. UI rendering — Shows only the curated results to the user.
- Complex job request → Plain search tools: drives repeated searches.
- Plain search tools → 100 candidate jobs: returns model-readable data.
- 100 candidate jobs → Model filters to five: filter and rank.
- Model filters to five → Render jobs widget: pass five IDs.
- Render jobs widget → Five visible job cards: display once.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Let rendering carry the model’s judgment
The same split generalizes beyond jobs. An e-commerce agent can explore many products before rendering a shortlist. A mapping agent can consider several locations before passing five selected addresses into a map. In each case, the useful boundary sits between broad data exploration and the smaller set chosen for human inspection. 12:13
A render tool can carry more than identifiers. Indeed could accept a job ID plus the model’s reason that the job is a good fit, or an ID plus a highlighted passage from the description. That lets the interface preserve the model’s comparative judgment instead of reducing its work to a generic set of cards. The model explores freely, then contributes explanation to the final presentation. 12:43
- Search variants: Two or three small tools can expose different ways to find jobs without attaching UI to every result.
- List rendering: One render tool can display a curated set of jobs.
- Focused rendering: Another can highlight one particular job in detail.
- Simple descriptions: Narrow tools need less explanation, reducing tool-description overload while leaving the model several ways to compose a workflow.
The ending reverses the usual framing of MCP Apps. Start by deciding what data the model needs and what operations it should be free to perform. Treat rendering as the outcome of that exploration. A good interface remains valuable, but it should not become the point where the agent stops thinking. 13:42
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Mihalik’s page describes his work building AI platforms at Indeed and provides a place to follow his engineering work.
Further reading
A searchable transcript with chapter links and concise answers to the talk’s main implementation questions.
Related talks
- Building Interactive UIs in VS Code with MCP Apps — Marlene Mhangami & Liam Hampton, GitHub
Extends the interface side of this talk with MCP host/client/server structure, UI-resource fetching, iframe isolation, and an interactive profiling demonstration.
- MCP Apps: Extending the Frontier — Ido Salomon & Liad Yosef
Provides specification-level context for branded interactive MCP interfaces, host-to-app communication, and the cost of rendering demanding interfaces repeatedly.
- Agent Output Is Not UX: Rendering Layer Your LLM Pipeline Is Missing - Bala Ramdoss, Amazon Lens
Explores the complementary production problem of turning model output into controlled, version-aware, progressively rendered interfaces.
Transcript & slides
Loading transcript and slides…