-
Notifications
You must be signed in to change notification settings - Fork 194
Description
Describe the bug
An MCP App (ext-apps OpenSeadragon deep-zoom viewer) loads and renders its UI inside the sandbox iframe — header, metadata bar, zoom controls all appear — but network requests to declared connectDomains / resourceDomains origins are blocked. The tile canvas remains blank. No image content renders.
This worked correctly until ~Feb 26, 2026 and broke without any server-side code changes, suggesting a change in how the host client (claude.ai) applies CSP or network restrictions to the sandbox iframe.
CSP configuration
The MCP server declares both resourceDomains (maps to img-src, script-src, style-src) and connectDomains (maps to connect-src) on the viewer resource:
csp: {
resourceDomains: ["https://iiif.micr.io", "https://cdn.jsdelivr.net", "https://unpkg.com"],
connectDomains: ["https://iiif.micr.io"],
},This configuration has not changed since the viewer was first deployed (~two weeks ago). The viewer uses OpenSeadragon (loaded from CDN) to fetch IIIF Image API tiles from iiif.micr.io.
To Reproduce
- Connect to the rijksmuseum-mcp-plus MCP server (public, npm:
rijksmuseum-mcp-plus) - Call
get_artwork_imagewithobjectNumber: "SK-C-5"(Rembrandt, The Night Watch) - The ext-apps viewer iframe loads — OpenSeadragon initialises, controls are visible, navigator panel is present
- The tile canvas is empty — no image content renders
Reproducible on any objectNumber. The failure is structural, not artwork-specific.
Expected behaviour
OpenSeadragon should fetch info.json via XHR (connect-src) and load tiles via <img> (img-src) from iiif.micr.io, as permitted by the declared CSP domains. This worked until ~Feb 26.
Investigation
- IIIF server is live:
https://iiif.micr.io/PJEZO/info.jsonreturns 200 OK withaccess-control-allow-origin: *and valid IIIF Image API 3.0 (level2) metadata - No server-side changes: The MCP server code for
get_artwork_imageand the viewer resource has not changed since Feb 21. The deployed Railway instance (d935609a, Feb 26 18:02 UTC) is identical. - CDN loads fine: OpenSeadragon JS from
cdn.jsdelivr.netloads and initialises correctly (controls render). Only the tile/info.json fetches toiiif.micr.iofail. - CORS is correct: The IIIF endpoint returns
access-control-allow-origin: *
Environment
| Host client | claude.ai (web chat) |
| ext-apps SDK | @modelcontextprotocol/ext-apps v1.0.1 |
| MCP server | rijksmuseum-mcp-plus on Railway |
| IIIF endpoint | iiif.micr.io |
| Last working | ~2026-02-26 |
| First failure | 2026-02-27 |
Additional context
The viewer app declares availableDisplayModes: ['inline', 'fullscreen'] and the resource sets prefersBorder: false. The app HTML is served inline via resources/read — not loaded from an external URL.
Related issues in this repo: #374 (map server CSP), #387 (frameDomains CSP mapping).