Which origin to allowlist
- Local
- Hosted
The view runs on the Inspector’s scheme and port with the hostname swapped between
localhost and 127.0.0.1. The swap is what keeps the sandbox on a different origin from the app.Because the swap depends on which one you open the Inspector with, allowlist both:Cross-origin requests carry the origin, not the full path — MCPJam sends
Referrer-Policy: strict-origin-when-cross-origin. So a third party sees
http://127.0.0.1:6274/, and an allowlist pattern keyed on the origin
matches. A pattern keyed on a path will not.CSP is a separate gate
Allowlisting your origin tells the third party to accept your requests. It does not let your widget make them — MCPJam’s default policy denies everything not declared. Both have to pass. Declare what the API needs in the UI resource’s_meta.ui.csp:
resourceDomains covers scripts, images, styles and fonts; connectDomains covers fetch/XHR/WebSocket. A JavaScript SDK usually needs both. frameDomains is only for APIs you embed as a nested iframe — an embed widget, not a JS SDK.
Anything still blocked shows up in the Workbench’s Findings tab with the directive that refused it.
_meta.ui.domain
SEP-1865 lets a server request a dedicated origin with _meta.ui.domain. The format is host-specific and each host derives its own — Claude uses sha256(<connector URL>)[:32] + ".claudemcpcontent.com", ChatGPT a per-plugin label — so one declared string cannot match every host.
MCPJam derives the origin it serves your view from rather than routing on what you declare; a server-chosen string would otherwise let one server claim another’s origin, and with it that origin’s cookies and storage. If you declare a domain, the Workbench’s Findings tab reports whether it matches what MCPJam serves. A mismatch is informational — it is the normal state for a server already targeting Claude or ChatGPT — and simply means an allowlist keyed on that value will not match requests coming from MCPJam.
Rendering mode
MCPJam mounts a view by writing its HTML into a blank document, the same way Claude does. A document written that way without a doctype is parsed in quirks mode, where the box model and percentage heights differ from what you probably tested against. Start your resource HTML with<!DOCTYPE html>. The readiness report flags this as claude.apps.design.doctype.
Opaque-origin mode
When the active client’s Same-origin app iframe setting is off (for example, the Copilot preset), the inspector mounts the view withsrcdoc and no allow-same-origin. The view runs in an opaque origin — it has no URL of its own, localStorage/sessionStorage/indexedDB throw, cookies are inaccessible, and outbound fetches send Origin: null instead of a real origin.
In this mode the View origin chip in the Sandbox Stack shows about:srcdoc rather than a URL. There is no origin to allowlist with a third-party API — if your app relies on a real origin for CORS, OAuth redirects, or referrer-restricted keys, it will not work on hosts that mount apps this way.
If the app reloads itself while running in opaque mode, the proxy stops relaying messages for it and the Sandbox Stack shows channel closed.
