41. MCP clients download files under /mcp#

Date: 2026-10-31 (#479)

Status#

Accepted

Context#

read, search and current_page give image and file URLs under /api/blobs/{sha}. Behind Cloudflare Access only /mcp takes the service token, so an MCP client sent there gets the email login page, not the file. A client could not save an attachment, publish a page with its pictures, or get a drawing past the drawing tool’s 200 kB cut.

The PWA fetches /api/blobs/... from the browser with no bearer token, so /api cannot need one. Telling a machine request from a browser one (by the Access JWT’s claims, or the CF-Access-Client-Id header) would tie scrap to one proxy and add moving parts.

Decision#

  • The read-only GETs a client needs are served under /mcp too, by the /api routes’ own handlers (mcp_files.py registers each one on a second path; a missing one stops startup): /mcp/blobs/{sha}, /preview and /uses; /mcp/pages/{key}/download; /mcp/captures/{cid}/photos/{sha}/drawing and /versions. One more is /mcp only: .../drawing.excalidraw, the whole current drawing as an .excalidraw file (as a page’s download has it).

  • BearerAuth already guards /mcp and everything under it, and the Access application on path /mcp covers its subpaths, so they need the same headers as /mcp: no new auth code and no Cloudflare change. With no token set they are open, as /mcp is.

  • They are GETs, so hostguard checks their Host (as for every route) and no Origin is needed: curl works.

  • Results keep url (the browser’s link) and add download_url (images, files, page), drawing_download_url, image_download_url (search rows) and the drawing tool’s download_url.

Consequences#

  • The /api paths and the PWA are unchanged; nothing is written through /mcp/....

  • A client with the MCP headers can read every stored file by hash, as anyone past the email login can.

  • A new download for MCP clients is a line in mcp_files.MIRRORED.