[2026] MCP Apps: Server-Rendered UI for Agents (Proof)
MCP Apps (SEP-1865) lets servers ship interactive HTML that hosts render in sandboxed iframes, with UI templates declared ahead for prefetch, cache and review. I shipped a GST-invoice approval card from Junagadh in a day — every UI action flows through the same audit and consent path as direct tool calls, P95 780ms.
Author: Deepak Bagada — AI Developer & Architect, Junagadh, Gujarat, India — Founder SaaS Next, builder of Curro. Connect linkedin.com/in/deepak-bagada · deepakbagada.in — Last reviewed 2026-09-17.
I run AI Development & Autonomous Agents where MCP Apps cards are now the default approval surface. Screens are built in Website Development & Laravel Architecture, approvals fan out through Business Workflow Automation, and custom card work starts at get in touch.
What MCP Apps actually are
MCP Apps arrived with the 2026-07-28 specification as one of two official extensions (the other is Tasks), under the new extensions framework SEP-2133. Extensions carry reverse-DNS IDs, negotiate through an extensions map on client and server capabilities, live in their own repositories with delegated maintainers, and version independently of the core spec. That last point matters: UI capability can now ship on its own timeline instead of waiting for a protocol revision.
The mechanism is simple and strict. Tools declare their UI templates ahead of time, so hosts can prefetch, cache and security-review them before anything runs. The rendered UI lives in a sandboxed iframe and talks back to the host over the same JSON-RPC base protocol as everything else in MCP — so every UI-initiated action travels the identical audit and consent path as a direct tool call. No side channels. No shadow DOM tricks that skip the OPA gate. A button click inside the card is a tool call, logged with trace_id, tenant_id and policy decision like any other.
For Indian SME workflows this is the missing piece. Our clients approve quotations, GST filings and dispatch orders inside WhatsApp-style chat, and until MCP Apps the choice was a dead-text summary ("reply YES to approve ₹48,500" — error-prone) or a full custom frontend (₹2-4L, six weeks). The card is the middle path: rich enough to show line items, cheap enough to ship in a day.
War story: the unescaped invoice that sandboxing caught
I will confess the bug that made me respect the iframe boundary. Our first approval card rendered invoice line items with string interpolation straight from the seller's Tally export — item names included a <script> tag crafted by nobody malicious, just a Rajkot trader whose item master contains HTML-likeason characters from a copy-paste out of Excel (12" Pipe <SALE> style garbage). The card rendered, the script did not execute — sandbox + a strict CSP blocked it — but the layout broke into raw markup in front of the client.
The fix took 40 minutes: escape at the template boundary, validate against a Pydantic schema before render, and add a preview test with adversarial item names (image tags with onerror handlers, unbalanced quotes, 4KB emoji strings) to the 90-day replay set. The lesson stuck: declare templates ahead, never trust upstream text, and let the host review the template before it runs. MCP Apps gives you the hooks for all three — use them, because the model can see UI strings and shape them.
Second war story, the money one. A Surat textile client approved dispatch orders over phone calls: 4.2 hours median approval latency, two dispatch errors in Q1 2026 from misheard quantities (₹1.9L in return freight). We replaced the call with an MCP Apps card — order lines, quantities, transport, one Approve button gated by OPA (orders above ₹15K need a second tap from the owner). Median approval latency is now 90 seconds. Zero misheard-quantity errors in 90 days. The card cost one day to build against our existing FastMCP gateway; the client recovered its build cost in 11 days of saved freight. That is the ROI table I show every SME founder who asks whether agents are toys.
Code: declaring a card template on the server
// FastMCP tool with a declared MCP Apps UI template (SEP-1865)
server.tool({
id: "acme.example/approve-dispatch",
ui: {
template: "dispatch-card.v1", // declared ahead — host prefetches + reviews
csp: "default-src 'none'; style-src 'self'",
sandbox: ["allow-scripts"], // no allow-same-origin, no top navigation
},
handler: async ({ orderId, tenant }) => {
const order = await loadOrder(orderId, tenant); // Pydantic-validated
const jwt = await mintScopedJWT(tenant, "approve-dispatch");
if (!(await opaAllow({ tenant, tool: "approve-dispatch", amount: order.total })))
return { ui: "denied-card.v1", reason: "HITL required above threshold" };
return { ui: "dispatch-card.v1", props: escapeAll(order), jwt };
},
});
# Host side — every UI action re-enters the audited tool path
from pydantic import BaseModel
class UIAction(BaseModel):
template: str # must match a declared, reviewed template
action: str
tenant_id: str
trace_id: str
def on_card_action(a: UIAction) -> dict:
assert a.template in REVIEWED_TEMPLATES, "unreviewed template blocked"
emit_otel(a.trace_id, a.tenant_id, a.action) # same ledger as tool calls
return dispatch_tool(a.action, tenant=a.tenant_id) # OPA gate inside
Note what the host enforces: template allow-listing, sandbox flags, and re-entry through the audited path. The card never calls the database. It calls the host, the host calls the tool, the ledger records it. When our Surat client's owner taps Approve, the trace in Grafana Tempo is indistinguishable from an API-driven approval — same span shape, same policy fields.
When NOT to use MCP Apps
Cards are not free. Each template is a review burden: prefetch policy, cache scope, CSP, sandbox flags, and a HITL rule for irreversible actions. If the tool is chat-complete in one line ("invoice paid", "sync done"), a card adds review surface for zero UX gain — send text. If the host is low-trust or you cannot enforce template allow-listing, do not ship interactive UI there; the AWS guidance is explicit that platform teams should set an MCP Apps policy before the first server in the fleet ships one. And if your approval needs wet signatures or government portals (GST filings still need the portal), the card can prepare but cannot sign — design the card as prep + handoff, not as the signature itself.
| Surface | Best for | Cost to ship | Audit quality |
|---|---|---|---|
| Plain chat text | Status, confirmations, one-line answers | Zero | Full ledger, weak UX |
| MCP Apps card | Approvals, line items, prep + handoff | ~1 day per card | Full ledger, same path as tools |
| Custom frontend | Portals, signatures, complex flows | ₹2-4L, 4-6 weeks | Separate audit stack |
Rollout rules from our Junagadh lab
One gateway serves both stacks — Next.js chat widget and Laravel RFQ inbox render the same card templates from the same FastMCP server, one OPA, one ledger. Templates version like APIs (dispatch-card.v1, never latest in prod), rollback is a catalog pointer flip in under two seconds, and every card ships with its HITL threshold in the manifest. The 90-day ledger replays 500 samples weekly; any card whose approval misclick rate crosses 1% gets downgraded to text-first pending review. That is how a Tier-3 lab runs UI for agents without a design team.
Frequently Asked Questions
What are MCP Apps in the 2026-07-28 specification?
MCP Apps (SEP-1865) is an official extension letting servers ship interactive HTML UIs that hosts render in sandboxed iframes, with templates declared ahead for prefetch, cache and review. Every UI action re-enters the host over JSON-RPC through the same audit and consent path as direct tool calls — no side channels.
How do you secure an MCP Apps card in production?
Allow-list reviewed templates, strip same-origin and top-navigation from sandbox flags, escape all upstream text at the template boundary, and gate irreversible actions with OPA plus HITL. Our Rajkot XSS scare proved the iframe boundary works; Pydantic validation before render and adversarial preview tests keep it working.
How much does an approval card cost for a Gujarat SME in 2026?
About one day of build against an existing MCP gateway, versus ₹2-4L and 4-6 weeks for a custom frontend. Our Surat dispatch card cut median approval 4.2 hours to 90 seconds with zero quantity errors in 90 days, recovering build cost in 11 days of saved freight.
Can a Gujarat SME ship MCP Apps without a metro agency in 2026?
Yes — our two-person Junagadh team shipped the first card in a day on a ₹6K VPS gateway with the same OPA and ledger as our metro competitors. Templates version like APIs with 2s rollback, so the risk window is one card behind a feature flag, not a frontend project.
Bottom Line: MCP Apps turns approvals from phone calls into audited cards — one day to ship, same ledger as every tool call, and the sandbox boundary that saved us from our own invoice data. Declare templates ahead, gate the money, log everything.
From Junagadh — where the card approves in 90 seconds and the ledger remembers every tap.