Share a file from Claude: how MCP turns a report into a link
Add https://replilo.com/mcp to your assistant, ask it to share
a file, and you get back a link like
https://replilo.com/s/ab3xk9qp that asks for a confirmed email
address before it serves the download. There are two ways to connect: an
API key in an Authorization header for anything that can set
one, and OAuth for browser clients like claude.ai connectors that can't.
Real files go up over a one-time URL with curl, not through
the model. The rest of this is why each of those is the way it is.
A chat window is not a delivery mechanism
Say the assistant has just produced a forty-page competitive analysis. It can render it. It can rewrite it, shorten it, turn it into a PDF on disk. What it cannot do is give it an address — something you paste into an email to a customer who is not in this conversation and never will be.
So the last mile gets done by hand. Copy the file out, open a dashboard, upload it, set the link options, copy the URL, come back. Six steps, and the only one anybody wanted was the last.
What MCP changes
MCP is the protocol assistants speak to reach tools that aren't part of the
model. Replilo's server lives at https://replilo.com/mcp:
Streamable HTTP, stateless, JSON replies. Stateless is the part worth
noticing — there's no session to keep alive, so every call carries its own
credential and a connector survives a restart, a network blip, or a
conversation resumed a day later.
What the agent gets is not a special agent-shaped subset. It's the same account, the same documents, the same links and the same subscriber list the dashboard shows. Upload from Claude at your desk, expire the link from the browser on your phone.
Two ways to authenticate, and you don't really pick
Which one you use is decided by whether your client can put a header on an HTTP request. Most can. The ones that matter most can't.
An API key, for anything that can set a header
Create one on the AI agents page of the dashboard and send
it as Authorization: Bearer rpl_…. Only a sha-256 of the key
is stored, so it's shown once and cannot be recovered — if you lose it,
you make a new one. Deleting the key revokes it, immediately and
completely.
Keys are deliberately not accepted on the key-management endpoints themselves. An agent holding a key can share files and read your subscriber list; it cannot mint itself a second credential that outlives the one you can see and delete.
claude mcp add --transport http replilo \
https://replilo.com/mcp \
--header "Authorization: Bearer rpl_…"
OAuth 2.1, for clients that can't
A claude.ai custom connector runs in a browser. There is no config file to
edit and no header to set, so a bearer token is not an option at all. For
those, the server is also its own authorization server: discovery at
https://replilo.com/.well-known/oauth-protected-resource/mcp,
dynamic client registration under RFC 7591, and authorization code with
PKCE using S256.
In practice none of that is visible. You add
https://replilo.com/mcp as a custom connector in claude.ai,
the browser bounces you to Replilo, you approve the prompt, and you're
done. Nothing to paste, and no long-lived secret sitting in a chat log
where it can be quoted back.
The rule of thumb: if you're editing a config file or running a CLI, use a key. If you're clicking "add connector" in a browser, it's OAuth, and you will never see the words PKCE or RFC 7591.
If you just want the setup steps for claude.ai rather than the reasoning, adding Replilo as a custom connector covers the URL, the consent screen and what it grants.
The tools an agent gets
| To do this | Tools |
|---|---|
| Put a file up | create_upload_url, upload_document |
| Manage the link | list_links, get_link, update_link, regenerate_link_slug |
| Manage the files | list_documents, delete_document, get_storage_usage |
| Read what happened | list_subscribers, list_downloads |
Why a real file never travels through the model
There are two upload tools and the difference between them is the most important design decision on the server.
upload_document takes the file base64-encoded inside the tool
call. That works, and it's the right thing for a note the agent wrote a
moment ago. But tool arguments are generated tokens: whatever goes
in that field, the model has to emit one token at a time, out of the same
output budget it uses for everything else. A 3 MB PDF is 4 MB of base64.
The model does not run out of patience — it runs out of output length, and
what arrives is a truncated file that decodes to nothing.
So for anything real there's create_upload_url. It mints a
single-use URL and the agent shells out:
curl -sS -X POST -F "file=@./q3-report.pdf" \
"https://replilo.com/api/uploads/9f2c…"
The bytes go from disk to server and never enter the conversation, so a 25 MB file costs the same number of tokens as a 25 KB one. curl replies with the share link as JSON, which the agent reads and hands you.
The ticket is narrow on purpose. It expires after 30 minutes. It works once — two curls racing on the same URL produce one document, not two. The filename and title are fixed when the URL is minted, so whoever runs the upload supplies bytes and nothing else, and a leaked URL can't retitle someone's document. And only a hash of the token is stored, the same way the API key is.
What the exchange actually looks like
> share q3-report.pdf behind an email wall and expire it in 30 days
create_upload_url { "filename": "q3-report.pdf",
"title": "Q3 Revenue Report" }
→ { "uploadUrl": "https://replilo.com/api/uploads/9f2c…",
"expiresAt": "2026-08-29T11:43:07.000Z" }
bash curl -sS -X POST -F "file=@./q3-report.pdf" \
"https://replilo.com/api/uploads/9f2c…"
→ { "document": { "filename": "q3-report.pdf", "sizeBytes": 3145728 },
"link": { "id": "cm4b…", "slug": "ab3xk9qp",
"url": "https://replilo.com/s/ab3xk9qp" } }
update_link { "linkId": "cm4b…",
"expiresAt": "2026-09-28T23:59:59Z" }
→ { "link": { "slug": "ab3xk9qp", "expiresAt": "2026-09-28T23:59:59Z" } }
Done — https://replilo.com/s/ab3xk9qp
Anyone opening it gives an email address and confirms a six-digit code
before the download starts. It stops working on 28 September.
Three calls, none of which you made. What the recipient sees is a page asking for an email address; disposable domains are rejected, the domain's MX and SPF records are checked, a six-digit code confirms the address, and then the file downloads. That's the same wall you'd get from the dashboard — the reasoning behind it is in why a confirmed address beats a typed one.
What comes back the other way
Sharing is only half of it. list_subscribers and
list_downloads mean "who took the pricing deck this week?" is
a question answered from your own records rather than guessed at, in the
same conversation where you shared the thing.
If those addresses need to end up somewhere other than a dashboard, that's a different mechanism — see how to push verified subscribers into a webhook or a spreadsheet.
The short version
An assistant with an MCP connection can do the last mile itself: put the
file somewhere, gate it, hand back a URL. Use an API key wherever you can
set a header and OAuth where you can't, which in practice means claude.ai.
And let big files go up by curl from a one-time URL — the
inline base64 path exists for notes, and a file the model has to spell out
in base64 is a file that arrives broken.
Let your assistant hand over the file
Free account, 100 MB of storage, no card. Connect it once and ask in plain language.
Start sharing free