Pular para o conteúdo
← Back to Skalablog

Published article

Do MCP apps break model reasoning?

Software EngineeringClaudeChatGPT

MCP apps succeed when every rendered widget also returns structured data to the model, because a screen the model cannot read turns every follow-up question into a guess. Indeed's AI platform team learned that after adding a job-search widget cut the model from ten or fifteen searches down to one call, and that trade-off shaped three rules they now follow.

Why MCP Apps Need Data Before UI

MCP apps need data before UI because anything rendered in a widget must also reach the model as structured data, or every follow-up question fails. Dustin Mihalik, a technical fellow at Indeed, described this as the first rule from building MCP apps for Claude, ChatGPT, and Indeed's own job-seeker agent.

Indeed, a job search site, attached a rendering widget to its job search tool. With plain text output, the model ran ten or fifteen searches, filtered the results, and assembled a comparison table. Once the widget existed, the model called the tool once and assumed the results were already visible.

The transcript's description of the MCP Apps specification matches the model context protocol documentation, where a tool returns both structured content and a resource URI pointing at the HTML to render. Both have to stay in sync as the API changes.

The data-first pattern also fixes a branding problem. Text responses carry no branding, and getting a host chat app to link out is difficult because the host prefers to keep users inside the conversation. A widget gives control over the apply button and the detail view. Mihalik said getting Claude to show consistent links to jobs took "an absurd number of hours" of reviews, because the host apps have little incentive to send users away.

Structured content is not a new format to learn. It is the same JSON you would already return from a plain text MCP server. The only addition is the resource URI that points at the HTML file the host renders.

The Three Rules for Splitting Search Tools and Render Tools

The three rules from this talk are: return everything shown to the user as model-readable data, state in the tool description that a UI will render, and separate data processing from UI rendering. The third rule supersedes the other two when they conflict.

The first rule prevents the black box. A tool that injects HTML and fetches data through a separate API leaves the model unable to answer 'tell me about the first result' or 'sort this list of companies'. Mihalik points to MCP apps that inject HTML into the page and then call their own APIs, which is exactly the pattern that produces a model that cannot see its own output.

The second rule resolves a display battle. Without a description saying the results render automatically, the model narrates the same results in text underneath the widget. A line near the top of the tool description such as "results are displayed automatically to the user as a UI component" covers most of these cases.

The third rule changes the architecture. Indeed replaced its search-and-render job tool with plain search tasks that return no UI, plus a render widget that accepts a list of job IDs from the model.

That split let the model fetch around 100 jobs, filter down to five, and render only those, which is closer to how a person would work through a search. The model explores, then chooses what to show.

How Indeed Tracks User Interactions

User interactions such as clicking a detail button have to be pushed back to the model through the update model context method, because the model has no idea which result the user opened. That method accepts a string, so apps that track several events append them to a single string.

When a user opens a job modal, the model cannot answer 'write a cover letter for this job' unless the app reports which job was opened. Indeed loads those details on demand through the app rather than through the search tool, so the search tool never sees them. The user gets a modal with the full job description without leaving the chat host.

The MCP Apps documentation uses a shopping cart to illustrate the same pattern: the app reports the cart items and total cost, so the user can ask for information about what is in the cart. The transcript claims this example appears in those docs but does not quote the exact wording, so treat that specific example as unverified.

The context method accepts one string per update. Tracking multiple events means appending them together, which is a limitation worth designing around early. If you plan to log a click, a scroll and a modal open, decide now how you will delimit and order them.

Which MCP App Part Was Open Sourced in 2026

The UI resource served by an MCP app is an ordinary HTML file whose path is declared in the tool's resource URI; the extension that wires it into chat hosts is a specification in the MCP revision published in 2026, and its implementation status depends on the host and the SDK version you install.

The transcript credits openness to the specification and the app pattern rather than a finished product. The distinction matters because a public specification is not the same thing as a stable, feature-complete runtime.

MCP Apps is a new extension, not a replacement for plain MCP servers. Text-only MCP still works and is the recommended default when a widget adds no interactive value.

You should verify the current revision of the specification against the exact SDK and host you target, because both the resource contract and the update model context method are recent. The MCP specification is the primary source for the current field names, and the Apps SDK documentation is where the data and UI separation principle is stated.

Where the Job Search Example Applies Elsewhere

The job search example applies to any workflow where exploration and presentation are different jobs. E-commerce is the obvious analogue: the model can compare a wide product set before rendering a shortlist.

A mapping app can build five candidate addresses in a search tool and hand them to a render tool that draws them, keeping the map surface simple while the model does comparison work.

Render tools can also take creative fields. A job render tool could accept an ID and a reason the model liked that listing, or an ID and a highlighted section of the description. Mihalik observed that text-based MCP runs often contain the model's own reasoning about why it picked one option, and that reasoning can be passed into the widget so the interface reflects the model's judgment rather than an ID-to-card lookup.

The pattern generalizes; the specific tool boundaries do not. Two or three search tools plus one or two render tools fit job search, but the right split depends on which data the model needs to explore freely. Small composite tools with simple descriptions keep the model's context light and leave it room to decide how to explore and how to render.

MCP App Design Rules Compared

The table below compares the three rules on what each prevents, how you implement it, and what breaks without it. Rule three takes precedence over the other two when they conflict.

RuleWhat it preventsImplementationFailure mode if skipped
Return data for everything renderedA widget the model cannot readSame tool returns structured content plus a resource URIFollow-up questions about the screen fail
Declare the UI in the tool descriptionThe model narrating the same results in textDescription states results render automaticallyA duplicate text dump under the widget
Separate data processing from renderingPremature stopping after one tool callPlain search tools plus a render tool taking IDsExploration collapses to a single call

The first two rules are additive and can be retrofitted onto an existing MCP server by editing one description and one return value. The third rule is structural and usually forces a change to the tool set itself.

FAQ

  • What are MCP apps? MCP apps are chat extensions that let a tool return both structured data and an HTML interface the host renders inside the conversation. The model reads the data; the user sees the widget. The pattern is specified in the MCP Apps extension and demonstrated in the job-search example above.
  • Why did adding a UI make Indeed's product worse? Once a widget rendered job results, the model called the search tool once and assumed the results were visible, instead of running ten or fifteen searches and assembling a table. The text-only version explored more, and exploration was what produced good comparisons.
  • What is the update model context method? It is the MCP Apps method that lets an app tell the model about a user interaction, such as opening a job detail view. It accepts a single string, so apps tracking several events append them together. Without it, the model cannot tell which result a follow-up question refers to.
  • Should I use MCP apps instead of a plain MCP server? Not by default. Plain text MCP servers are simpler and the model explores them more freely. Use MCP apps when the interface adds control the chat host does not give you, such as an apply button, a detail modal, or your own branding.
  • What does data-first design mean for an MCP app? It means deciding which data the model should explore before deciding what the interface shows. Rendering becomes a side effect of the model's exploration rather than the starting point, which is why Indeed's render widget takes IDs rather than raw listing content.

Turning a Talk Into a Written Article

The whole value of this talk sat in one reversed assumption: the render widget was supposed to be the point, and it turned out to be a side effect of the data the model could already read. That is a small architecture lesson with a large consequence for anyone building on chat hosts, and it survives only because someone wrote it down.

Talks like this carry the same problem as the widgets they describe. The knowledge exists in a format that is easy to watch and hard to quote, and a written version is what lets the three rules travel beyond the room.

If you have explanations, interviews, or lessons inside YouTube videos, paste the URL into Skala Blog, generate the transcription, and turn it into an article. Gustavo doido.

Source video

From a recorded talk to a quotable article

Dustin Mihalik's three rules exist because someone put an hour of hard-won lessons into a format you can copy, search and argue with. If you have the same kind of knowledge sitting in a recording, an explanation of how you split a tool, what failed the first time, or why a design rule exists, that recording can become a written article with Skala Blog. Paste the YouTube URL, generate the transcription, and publish the piece.