Ingram Cloud

Documentation

MCP Apps

MCP Apps

Attach an interactive UI to an agent and an mcp deployment serves it to any host that supports the MCP Apps extension (SEP-1865) — Claude, ChatGPT, Copilot. The host renders your HTML inline in a sandboxed iframe instead of plain text; a host that doesn't support it ignores the UI and shows the reply. Author once on the agent, render everywhere the smith runs.

You write standard MCP Apps HTML (the @modelcontextprotocol/ext-apps App class, the ui:// scheme, the text/html;profile=mcp-app MIME) — Ingram Cloud is a conforming MCP Apps server. Templates are tenant-authored and version-pinned; Ingram never generates UI at runtime.

The pieces

  • A template is an HTML bundle attached to an agent under a name. Its bytes live in blob storage; the agent carries the reference.
  • Publishing an agent version freezes its templates into the immutable snapshot, so a rendered panel is reproducible per version.
  • A smith running that agent (over its mcp deployment) is what the host talks to; the panel it renders is byte-identical for every caller.

Author a template

Upload the bundle as multipart — the HTML file part plus a JSON metadata sidecar. Uploads land on the agent's draft; publish a version to freeze it.

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/agents/agt_…/ui \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -F 'file=@dashboard.html;type=text/html' \
  -F 'metadata={"name":"dashboard","csp":{"connectDomains":["https://api.example.com"]}}'
# → 200 { "name": "dashboard", "content_hash": "…", "csp": { … } }

metadata fields:

  • name — unique per agent ([a-z0-9_-]); re-uploading the same name replaces it.
  • csp — the MCP Apps _meta.ui.csp object (connectDomains, resourceDomains, frameDomains, baseUriDomains), the origins the sandboxed iframe may reach. Omit it and the deny-by-default sandbox allows only a self-contained bundle.
  • permissions — the _meta.ui.permissions map the template requests (e.g. { "camera": {} }).
  • tool — present only for a typed app-tool (below).

List, fetch, and remove templates on the draft:

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/agents/agt_…/ui \
  -H "Authorization: Bearer $IC_TOKEN" -H "IC-Api-Version: 2026-05-01"
# → { "data": [ { "name": "dashboard", … } ] }

# The raw bytes (302 to a presigned URL, or inline):
curl https://api.cloud.ingram.tech/v1/agents/agt_…/ui/dashboard/content \
  -H "Authorization: Bearer $IC_TOKEN" -H "IC-Api-Version: 2026-05-01"

curl -X DELETE https://api.cloud.ingram.tech/v1/agents/agt_…/ui/dashboard \
  -H "Authorization: Bearer $IC_TOKEN" -H "IC-Api-Version: 2026-05-01"

The internal blob key is never returned. These endpoints ride the same runs:read / runs:write scopes and tenant-admin gate as the rest of /v1/agents — a smith-scoped token can't touch them.

Then publish so the template is live for the deployment:

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/agents/agt_…/versions \
  -H "Authorization: Bearer $IC_TOKEN" -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" -d '{ "note": "with dashboard UI" }'

How it renders

Deploy the agent as an mcp server and connect a host. There are three ways the smith drives which template shows:

  • Bound to ask — name a template ask and every ask reply renders inside it. The ask tool descriptor carries _meta.ui.resourceUri so the host prefetches the bundle.
  • Typed app-tools — give a template a tool ({ description, input_schema, instruction, mutating }). The deployment surfaces one MCP tool per app-tool, each linked to its own template; the host's model routes to the right one, and the smith runs the template's scoped instruction with the host-supplied arguments. A mutating tool carries the standard destructiveHint annotation, so the host asks the user before a write; reads run freely.
  • Dynamic — the smith calls the built-in render_app tool mid-reply to pick a template (by name) and the JSON it renders. Enabled automatically once the agent has at least one template.

In every case the panel receives the run's structured result over the standard MCP Apps channel, and any tool the panel calls back rides the same tools/call endpoint — a write that needs approval pauses the run on the standard approval channel.

Degradation

Only MCP hosts render ui:// panels. On a chat channel (Slack, Telegram, email) or any host without the extension, the smith's text reply stands on its own — so write a reply that reads well without the UI.

In the console

An agent's Apps tab lists its templates, uploads new bundles, sets CSP origins and permissions, binds a tool, and previews a template in a sandboxed iframe so you can test it without connecting a host.