Importing a URL as a widget
Paste a URL into the workspace search bar and click Add as widget. The platform classifies the URL into one of three tiers and acts accordingly.
Tier A, cooperative
The site publishes a cooperative manifest the platform can discover:
<link rel="widget-manifest">(Robutler / MCP Apps) or<link rel="manifest">(PWA) in the page<head>, or/.well-known/widget-card.json//.well-known/mcp-app.jsonon the same origin, or- Schema.org JSON-LD with
SoftwareApplicationwhoseapplicationCategoryisWidget.
The discovered manifest is snapshotted and the URL is registered as a full widget, it shows up on your canvas as a first-class embed, can be remixed (as a catalog-row copy that still points at the original remote URL), and inherits the manifest's tool declarations and CSP allowlist.
Tier B, passive iframeable
No manifest, but the site is willing to be embedded, the platform
probes for X-Frame-Options and CSP frame-ancestors and finds
neither blocks embedding from your portal.
The URL is registered as a view-only widget. You can pin it to the canvas; it's not remixable (there's no source to clone) and it carries a view-only badge so you know it's not under your control.
Tier C, iframe-denied
The site refuses to be framed (X-Frame-Options: DENY / SAMEORIGIN
or CSP frame-ancestors 'none'/'self'). The import returns a 422
with an inline fallback message:
This site can't be embedded as a widget.
That's it, no modal, no CTA, no automatic suggestion of cloud
browser delegation. If you want browser-driven access to a site
that refuses framing, invoke @robutler.browseruse from chat
yourself; that's an explicit user action the platform deliberately
does not auto-prompt.
Implicit delegation could spend your quota without a clear opt-in, so there is zero auto-delegation UI: browser-driven access is always an explicit user action.
SSRF protections
All server-side fetches the import does (the page URL, the well-known probes, the manifest URL, the JSON-LD origin, the HEAD probe) flow through a single hardened validator:
- HTTPS only by default.
- Private / loopback / link-local IPs blocked (
10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,192.168.0.0/16, etc.) so a malicious page can't redirect us into your internal network. - Cloud-metadata IPs blocked (
169.254.169.254and friends) so cloud-instance credentials can't be probed. - Max URL length 2048, max 3 redirects, 10s timeout per fetch.
- The manifest URL discovered in Tier A is re-validated as a separate origin before fetch, a public page cannot redirect manifest discovery to an internal address.
A URL that fails the SSRF gate returns 422 { reason: 'url_unsafe' },
distinct from a site that simply refuses framing, and the user is told
the URL cannot be imported.
What about per-user import rate limits?
The platform dedupes imports on (url, userId) for 60 seconds (so
double-clicking doesn't fire two imports), and caps concurrent
imports at 3 per user. Beyond that, normal rate limits apply.
See also: App authoring for what the imported manifest needs to look like.