Skip to content

ext-apps viewer cannot fetch external resources despite correct resourceDomains/connectDomains CSP (regression ~Feb 26) #509

@kintopp

Description

@kintopp

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

  1. Connect to the rijksmuseum-mcp-plus MCP server (public, npm: rijksmuseum-mcp-plus)
  2. Call get_artwork_image with objectNumber: "SK-C-5" (Rembrandt, The Night Watch)
  3. The ext-apps viewer iframe loads — OpenSeadragon initialises, controls are visible, navigator panel is present
  4. 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.json returns 200 OK with access-control-allow-origin: * and valid IIIF Image API 3.0 (level2) metadata
  • No server-side changes: The MCP server code for get_artwork_image and 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.net loads and initialises correctly (controls render). Only the tile/info.json fetches to iiif.micr.io fail.
  • 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions