Host files from coding agents; stage on a branch and attach to GitHub PRs.
sh.uploads/mcp β Model Context Protocol (MCP) File Upload Server
This MCP server provides a way to host files generated by coding agents. It stages uploaded content on a branch and attaches it to GitHub pull requests. The server centers on file hosting for workflows like capturing screenshots during development, with storage configurable via connected backends.
π οΈ Key Features
Host files from coding agents
Stage uploads on a branch
Attach files to GitHub PRs
Screenshot capture and upload
π Use Cases
Capturing screenshots βas you workβ
Adding a tidy comment to a pull request that updates on each revision
β‘ Developer Benefits
Consolidates uploaded screenshots into one comment that updates automatically
Works with a hosted service setup
β οΈ Limitations
Storage connection is required to use a custom bucket; supported options include Cloudflare R2 or any S3-compatible provider
Capture screenshots, recordings, and other artifacts as you work: test
reports, logs, JSON, PDFs, zips. When the pull request opens, uploads.sh puts
them in one tidy comment that updates automatically on each revision. Hosted
uploads.sh is free to start. Connect your own bucket β Cloudflare R2 or any
S3-compatible provider β so storage in that bucket is unmetered, or self-host
the open-source service.
Under active development. uploads.sh is being built in the open, so
APIs can still change. Feedback is welcome β open an issue.
Artifacts ready when the pull request opens
uploads hosts the artifacts coding agents produce at stable public URLs
they can use in pull requests and issues: screenshots and screen recordings,
but also test reports, logs, JSON, CSV, Markdown, PDFs, and zip archives
(the full list is under Plans & limits). On
a branch, uploads put stages each file as soon as it is ready. When the pull
request opens, uploads.sh promotes the staged files into one managed comment.
GitHub's own attachments work from a browser and, since GitHub CLI 2.99
(September 2026), from gh β¦ --attach, but only once a pull request or issue
exists, and the files stay behind GitHub's own hosting, where they cannot be
embedded or fetched anywhere else. uploads.sh gives agents a stable
public URL from the same terminal where they build and test the change, while
the branch is still in progress. The hosted MCP server does the same for agents
that cannot run a command at all, taking files as bytes or a URL. GitHub has no
public API (yet) for that.
Keys are hash-free, so re-uploading the same filename overwrites in place and
the URL never changes β every embed of it updates at once. Workspaces keep
tenants (and their budgets and key policies) apart.
This repo is the source of the canonical deployment at
uploads.sh: the API worker, auth worker, MCP server, the
Astro web app, and the @buildinternet/uploads CLI (published to npm from
packages/uploads).
What it looks like
One comment per PR, rewritten in place on every sync. Files tagged
--state before and --state after pair into a side-by-side table; other
images and video land below it, and non-media files (reports, logs, archives)
list in a file table with their type and size.
Pairing is by --meta path=β¦ when several pairs share a comment (one before
and one after per path), and falls back to filenames that differ only by a
before/after token β hero-before.webp with hero-after.webp.
Everything you attach also lands in your workspace, grouped by where it came
from β pages by path, projects by repo or app β and browsable from one
place.
The screenshots view groups uploads by project and path.
Open any file for a share page: copy-ready embeds (Markdown, HTML, and more),
the raw URL, and a delete button.
Each file's share page β copy-ready embeds, details, and delete.
On a branch, put stages the file automatically. Open the pull request however
you normally would. The GitHub App
promotes the staged files into one managed attachments comment.
More ways to upload
Use the same commands for before-and-after evidence, an open pull request, a
browser capture, or an annotated image:
bash
# Pair two states from the same page in the pull request comment.
uploads put ./before.png --meta path=/settings --state before
uploads put ./after.png --meta path=/settings --state after
# See what this branch will attach when the pull request opens.
uploads staged
# Attach files directly when a pull request or issue is already open.
uploads attach ./before.png ./after.png
# Capture, annotate, and upload a page in one command.
uploads screenshot http://localhost:4321/settings --via local --annotate ./callouts.json
attach detects the repository and current PR through gh, uploads all files,
and creates or updates that same one comment. Without the GitHub App, run
uploads attach --promote after opening the pull request to promote files that
you staged earlier. All commands run under npx @buildinternet/uploads β¦
without a global install.
Sign in with GitHub or a magic link, then create your own workspace or accept
an invite into one β see enrollment. Hosted files are
public URLs β private-repo attachments get non-guessable links
(how that works), but anyone holding a URL can
view the file. Do not upload secrets or sensitive UI.
Connect your agent
The hosted MCP server runs at https://agents.uploads.sh/mcp and is listed in the
MCP Registry
as sh.uploads/mcp. Local stdio is uploads mcp on the same npm package.
uploads install adds the agent skills and the MCP server, so future sessions
can capture each visual milestone without being asked. The skills also install
standalone into any agent runtime:
bash
npx skills add buildinternet/uploads
That installs three skills: github-screenshots (visuals β PRs/issues),
uploads-cli (full CLI reference), and annotate-screenshots (callouts and
redaction on a capture).
On Claude Code or Codex, the plugin
bundles the skills, the MCP server, and a pre-PR screenshot reminder.
Full CLI usage, including annotations, managed comments, public galleries,
and change feeds (repo-wide or one pull request), lives in
docs/cli.md.
uploads feed create prints a live page of the screenshots on a repo:
The deployables: the REST API worker (api.uploads.sh), the auth worker, the remote MCP server, and the Astro site at uploads.sh
packages/
Shared code β most notably @buildinternet/uploads (the CLI, published to npm), @uploads/storage (the files-sdk adapter factory all storage goes through), and @uploads/plugin (Claude/Codex plugin version, not published)
skills/
The three agent skills that ship to users
hooks/, plugins/, .mcp.json
Agent-runtime wiring: the shared pre-PR screenshot hook and the Claude / Codex plugin manifests
server.json
MCP Registry listing (sh.uploads/mcp): stdio uploads mcp plus the hosted remote
Each worker and the web app deploy separately. All storage access goes through
createStorage() in packages/storage β adding a provider is one new case
plus peer deps, no API changes.
Docs
Product docs β install, the staged loop, the GitHub App, limits β live at
https://uploads.sh/docs. The docs in this repo are the companion: CLI and API
reference, contributor setup, and operator material, all mapped from
docs/README.md.
Prerequisites: Node β₯24 and pnpm β₯11 (corepack enable). No Cloudflare
account needed for the core local loop β wrangler dev simulates R2, KV, and
D1 on disk:
bash
pnpm bootstrap # one-command setup: tooling, deps, env vars, local D1, default workspace
pnpm dev # API on :8787 (local R2 + KV + D1)
bootstrap is idempotent, and pnpm doctor diagnoses a setup without changing
it. The rest of the loop β the authenticated dev stack, the check and test
gates, and how to open a pull request β is in
CONTRIBUTING.md.