uploads.shStar on GitHub

How to get agents to upload screenshots & video to GitHub

Claude Code, CI jobs, review bots, plain scripts — anything without a browser.

The problem: no drag-and-drop from a terminal

You put a screenshot on a pull request by dragging the file into the comment box. GitHub then hosts it at github.com/user-attachments/…. That only works in a signed-in browser. There is no gh command and no API for it.

So an agent that just changed your UI can't show its work. The usual workarounds all hurt:

The fix: host the file at a public URL the agent can create itself, then write plain Markdown. That's what uploads.sh does.

Step 1 — install the CLI and sign in (once)

npm install -g @buildinternet/uploads
uploads login

uploads login trades an invite code for a workspace token and saves it. You do this once per machine. Access is invite-only for now — ask via the GitHub repo. Check your setup with uploads doctor.

Step 2 — teach your agent

One command sets up Claude Code:

uploads install

Or set up each piece yourself, for other agent runtimes:

npx skills add buildinternet/uploads --skill uploads-cli
claude mcp add --transport http uploads https://agents.uploads.sh/mcp

Step 3 — the agent attaches media with one command

From a branch with an open pull request, attach only needs the files. It finds the PR through gh, uploads each file to a stable URL, and keeps one managed comment:

uploads attach ./before.png ./after.png
>> uploading ./before.png
>> optimized 421337 → 96412 bytes (before.webp)
>> uploading ./after.png
>> optimized 407190 → 91205 bytes (after.webp)
URL: https://storage.uploads.sh/gh/you/app/pull/123/before.webp
MARKDOWN: ![before.png](https://storage.uploads.sh/gh/you/app/pull/123/before.webp)
URL: https://storage.uploads.sh/gh/you/app/pull/123/after.webp
MARKDOWN: ![after.png](https://storage.uploads.sh/gh/you/app/pull/123/after.webp)
>> attachments comment updated

Not on a PR branch? Point it somewhere:

uploads attach ./repro.gif --issue 45
uploads attach ./shot.png --repo owner/name --pr 123

Want to place the image yourself — in a PR description or README? --no-comment prints the URL and Markdown without posting anything. uploads put gives full control over naming and output.

Why the URLs matter

Attachments get stable keys, like gh/owner/repo/pull/123/shot.webp. Upload the same filename again and the PR shows the new image at the same URL within a minute. No stale screenshots.

Video and everything else

A short screen recording is often the best proof that a change works. MP4, WebM, GIFs, zips, logs — anything uploads as-is:

uploads attach ./demo.mp4 # linked from the comment

One caveat: GitHub only plays videos it hosts itself. Outside video shows up as a link, not a player. GIFs embed like images.

Images are optimized by default: re-encoded to WebP, size capped, and EXIF stripped so they load fast and leak nothing. Opt out with --no-optimize or --keep-exif. Add device chrome with --frame phone|browser|iphone-16-pro.

FAQ

Why can't my agent upload images to GitHub directly?
GitHub's own image hosting (github.com/user-attachments) only works when you drag a file into the comment box in a signed-in browser. There is no gh command or API for it. So any image an agent puts in a comment must already live at a public URL.
Do the image URLs change when I re-upload a screenshot?
No. PR and issue attachments get stable keys like gh/owner/repo/pull/123/shot.webp. Upload the same filename again and every embed shows the new image within about a minute.
Can agents upload video to GitHub pull requests?
Yes. MP4, WebM, and other files upload as-is and get a stable public URL. GitHub only plays videos it hosts itself, so the comment links the file instead of embedding a player.
How do I set up Claude Code to attach screenshots to its pull requests?
Run `uploads install` once. It installs the uploads-cli agent skill and registers the hosted MCP server with Claude Code. After that, the agent runs `uploads attach <files>` on its own when it changes something visual.
Are uploaded files private?
No. Files are public to anyone with the URL — even media attached to private repos. Don't upload secrets or sensitive UI. Uploading itself requires an invite-issued workspace token.

Every command and flag is in the docs. Agents can read /llms.txt. If this saved you a hand-rolled upload script, a star on GitHub helps others find it.