← The Harness
CONNECT The Harness 2026-Q3

Agents Get a UI

MCP was the USB port for agents. The 2026-07-28 revision gives that port a screen, with host-mediated widgets in the chat, plus extensions for tasks, auth, and whatever comes next.

You’ve given your house an agent. Ask it what’s on, whether you’re in Claude Desktop, a browser chat, or some host you haven’t built yet, and it can answer with a picture: a glowing floorplan, lights you can toggle, a furnace setpoint you can nudge. Same tools, same server, and now a visual that travels with the conversation no matter where you asked from.

We’ve been saying MCP is the USB-C port for agents since early on: one shape of plug, many peripherals. The 2026-07-28 revision is the moment that port grew a head. The connector can still ship plain tool results, and it can also ship interactive HTML that the host renders inline. The model stays in the loop because the host writes your clicks back into the transcript.

The demo we use for this is Hearthstone, a small smart-house MCP server in Umwelten’s agent-browser examples: lights, a furnace, a drifting indoor temperature, and a dashboard widget returned with every tool result. Click a room card, the porch comes on, and the next question to the model (“what’s the house status?”) gets answered from current state.

Hearthstone house dashboard widget with porch lit, furnace heating to 72°F, and a live-controls footer after a set_light through the host bridge
Hearthstone after a host-mediated click. Porch on, furnace heating to 72°F, transcript tagged from the tool call.

Extensions are how MCP grows now

The structural move is that the core got smaller and everything ambitious moved into named extensions a client and server opt into. The core is a wire and capability lives in the catalog. A client declares what it supports in every request’s _meta; a server advertises what it offers in server/discover; anything undeclared stays off. A UI-emitting tool still returns useful text for a client that never claimed the UI extension.

One foundational change underneath that catalog is that MCP is now stateless. No protocol sessions, no Mcp-Session-Id, no initialize handshake, no resumable streams, and no server-initiated requests. Every request carries its own protocol version and capabilities. When a tool needs human input mid-flight, it returns input_required with an opaque requestState blob; the client gathers answers and retries (Multi Round-Trip Requests). That is what lets a fleet of interchangeable server instances behind a dumb load balancer run interactive tools without sharing session memory, which is how the USB port scales once you start hanging screens and long jobs off it.

What’s on the shelf today:

  • MCP Apps (io.modelcontextprotocol/ui). Tools ship interactive HTML (ui:// resources) that hosts render in sandboxed iframes inline in the conversation. The app speaks a ui/* JSON-RPC dialect over postMessage; it can ask the host to call the server’s tools, push context back to the model, and receive fresh tool results. A chart or a form stays in the chat instead of a tab the user loses.
  • Tasks (io.modelcontextprotocol/tasks). A tool call may return a durable task handle instead of blocking (workinginput_requiredcompleted), polled via tasks/get, answered mid-flight via tasks/update, with cooperative cancellation. This is the long-running delegation primitive, and it matches the A2A task lifecycle, the shape that lets an MCP server act like a peer you hand work to.
  • OAuth Client Credentials and Enterprise-Managed Authorization (ext-auth). Machine-to-machine auth without a consent click, plus centralized org control over which agents reach which servers. If an agent is going to call another agent’s tools on a schedule, someone has to solve this.
  • The experimental track. Incubation repos (experimental-ext-*) owned by working groups, graduating through the SEP process with a required reference implementation. Third parties use the same mechanism under their own reversed-domain prefix (com.example/whatever). The namespace is open.

So when someone asks whether MCP will get feature X, the answer is that the core stopped growing on purpose. X becomes an extension you can adopt or ship yourself.

This is the shape Crosstalk has been circling all season: how agents talk to each other, to your tools, and to the world outside the box. At AI Engineer Miami, Laurie Voss’s eval put numbers under the MCP-vs-skills argument (correctness a wash; MCP heavier on calls, latency, and cost). A chunk of that overhead was protocol ceremony and cache-hostile context. Deterministic tool ordering, cacheable list results (ttlMs, cacheScope), and killing the handshake are the spec cutting at that gap.

The widget never talks to the server

This is the bit people get backwards on first contact. In an MCP App, the interactive flow is host-mediated end to end, and each leg exists for a reason:

  1. Tool call → widget. The model calls a tool; the result carries both plain text and a ui:// HTML resource. The host renders the HTML in a sandboxed iframe. Text-only hosts keep the text and move on.
  2. Click → host. The user clicks something in the widget. The sandbox means the iframe can’t reach the server, and shouldn’t. It posts a message to the host page asking it to run this tool with these arguments.
  3. Host → server. The host owns auth, consent, and whatever session you keep on your side. It executes the tools/call on the user’s behalf.
  4. Fresh state → widget. The result carries a new UI resource; the host swaps it in place. The dashboard shows the world as it is.
  5. Host → model. The host records the UI action into the conversation context. Skip this leg and the model keeps reasoning from stale state while the user flips switches it never saw.

Hearthstone’s room cards run that loop in the agent-browser bench. The same widget opened outside a bridging host degrades to a local preview. One flow, and you can see the extension’s design claims (sandboxing, host mediation, graceful degradation, context sync) in a single click. Meteora and Atlas are the same pattern for forecasts and routes.

Meteora 14-day forecast widget rendered inline in the agent-browser chat
Meteora, a 14-day forecast as an MCP App, same host bridge, same sandbox.
Atlas cycling route widget with a mapped route rendered in the agent-browser chat
Atlas, route map in chat. The widget still never talks to the server directly.

What else left the core

A few other moves are easy to miss under the UI story, and they’ll hit you when you upgrade.

Roots, Sampling, and Logging are deprecated. Pass paths as parameters, call your LLM provider yourself, and send logs to stderr or OpenTelemetry. Dynamic Client Registration gives way to Client ID Metadata Documents, where the OAuth client_id is an HTTPS URL pointing at a JSON document the authorization server fetches and validates, so client identity is portable and there’s no registration round-trip. Tasks left core for the extension above. List results grew cache hints, and servers are told to return tools in deterministic order so prompt caches hit.

server/discover (mandatory, cacheable, returning identity, versions, and capabilities) is functionally MCP’s agent card. Combined with the tasks extension, the protocol is borrowing the long-running-delegation idiom A2A already had. Where that boundary actually sits gets its own piece in Where A2A Begins and Ends.

The bench check

We ran the revision against Umwelten, where our MCP and A2A surfaces live. Every server we run was already stateless (fresh instance per request, no session generator), because session identity lived at the A2A layer: contextId keys the conversation, a verified JWT binds the speaker. That posture is what STD-010 already required (“agent sessions MUST persist to Postgres”), and it’s now what the protocol assumes. We never implemented roots, sampling, or logging, so there’s nothing to unwind.

Two places we’re on the wrong side of the new spec, both common. First, we still do Dynamic Client Registration on both sides: our CLI client registers itself dynamically, and our mcp-serve framework is an authorization server with a live RFC 7591 registration endpoint. The migration is hosting a client metadata document at a stable URL and teaching the authorization server to fetch and validate URL-shaped client ids. Second, elicitation coverage is zero. We never built the server-initiated version, which turns out fine, because the retry-shaped MRTR version builds directly on our stateless servers. What’s missing is client UX that notices input_required, renders the form, and retries. That gap is the old “MCP-auth-in-conversation” problem, the moment the agent turns around mid-task and asks you for something in band while the transcript keeps flowing.

The 2024-era handshake dance is gone, and with it a whole class of “works in the inspector, fails behind the load balancer” bugs. The protocol stopped pretending a tool call is a phone call; it’s a postcard, and postcards scale.