Skip to content

Serve MCP images as public R2 WebP URLs - #19

Merged
b1llow merged 6 commits into
mainfrom
cursor/r2-public-image-urls-f3da
Aug 14, 2026
Merged

b1llow merged 6 commits into
mainfrom
cursor/r2-public-image-urls-f3da

Conversation

@b1llow

@b1llow b1llow commented Aug 14, 2026 •

Copy link
Copy Markdown
Owner

Image tools currently return full PNG base64 as MCP ImageContent, which most hosts drop or render as raw b64. This change follows the Grok Imagine pattern: return a public https:// image URL that any client can put in <img src> or markdown.

What changed

  • Default tool result is structuredContent.image_url plus a resource_link to https://nai.hoshinoaya.com/i/img_<id>.webp. Inline base64 ImageContent is only emitted when the R2/WebP path is unavailable.
  • WebP encode happens once at generation time via the Cloudflare Images binding (quality: 99). If that binding is missing or fails, the public object is the original PNG.
  • R2 is the image store. Original PNG at orig/<id>.png (private, owner in customMetadata) for upscale/director/img2img chaining. Public rendition at i/<id>.webp. KV keeps vibe tokens, vibe cache, and OAuth data only.
  • No expiry. image_id and public URLs are permanent capability URLs (128-bit img_ id, no auth). GET /i/:file serves them with Cache-Control: public, max-age=31536000, immutable.
  • Transition: getImage still reads legacy img: KV rows so image_ids from the 24h before deploy keep working.
  • Preview widget (image-preview-v3.html) prefers images[].url and allows the Worker origin in CSP resourceDomains.
  • Local wrangler dev rewrites Host to the custom domain over http; image URLs in that case use http://127.0.0.1:8787 so they stay fetchable.

Infra

  • New R2 bucket nai-images (already created)
  • Wrangler bindings: IMG_BUCKET, IMAGES

Tests

npm test (210) and npm run typecheck pass.

Local wrangler dev smoke: mock NovelAI zip → nai_generate_image → image_url http://127.0.0.1:8787/i/img_….webp → 200 / image/webp / 96-byte RIFF WebP from the local Images binding.

Open in Web Open in Cursor 

Image tools now return a public https image_url (WebP quality 99) instead of
inline PNG base64, matching the Grok-style hosted URL that clients can render
with a normal img tag. Original PNGs move from KV to R2 for tool chaining;
image_id and public URLs do not expire.

Co-authored-by: billow <billow.fun@gmail.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
nai c7c9d77 Commit Preview URL

Branch Preview URL
Aug 14 2026, 04:38 AM

wrangler dev serves the custom domain over http and overwrites Host, so
public image URLs now use http://127.0.0.1:8787 in that case. Production
https://nai.hoshinoaya.com requests are unchanged.

Co-authored-by: billow <billow.fun@gmail.com>
@b1llow
b1llow marked this pull request as ready for review August 14, 2026 03:19
cursoragent and others added 4 commits August 14, 2026 03:22
Keep public WebP image_url results and R2 artifact storage, and take
main's nai_render_image_preview data/render split plus Grok OAuth callback.

Co-authored-by: billow <billow.fun@gmail.com>
CI failed because worker-configuration.d.ts hash/order was stale after adding IMG_BUCKET and IMAGES. Regenerated without local .dev.vars so the committed types match GitHub Actions.

Co-authored-by: billow <billow.fun@gmail.com>
Rate-limit GET/HEAD /i with the existing IP limiter, rewrite the custom
domain to localhost only for loopback clients, pin widget img URLs to
this Worker's /i/ capability paths, render URL and base64 images in the
same gallery, and honor weak or list If-None-Match.

Co-authored-by: billow <billow.fun@gmail.com>
Inject the request origin plus the production issuer into the preview
HTML so img src matches resourceDomains, instead of allowing every
workers.dev host. Give GET/HEAD /i its own IMAGE_RATE_LIMIT binding so
image fetches cannot starve /v1 or /mcp.

Co-authored-by: billow <billow.fun@gmail.com>
@b1llow
b1llow merged commit eabfa02 into main Aug 14, 2026
2 checks passed
@b1llow
b1llow deleted the cursor/r2-public-image-urls-f3da branch August 14, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants