agenticJune 18, 20265 min read

MCP, A2A, REST — three surfaces for one primitive

We didn’t set out to ship three integration protocols for the same send. We ship the surfaces that keep showing up — and they each earn their place in different parts of an agent stack.

ReTransfer is one primitive — upload some bytes, mint a short-lived link, deliver it — and three ways to call it. The first time you read that you assume two are vestigial. They aren’t. Each surface lines up with a different shape of caller, and the wrong one makes the simple thing feel hard.

Three callers, three surfaces

An AI host that wants to spawn a subprocess and exchange JSON-RPC: that’s MCP. An agent on another box that wants to know "can you receive a transfer for me?" without opening a connection: that’s A2A, served at /.well-known/agent-card.json. Anything else that speaks HTTP: the OpenAPI 3.1 contract served at /.well-known/openapi.json.

Same authentication. Same rate limits. Same expiry semantics. The choice is about where your code already lives, not what it can do.

MCP — when the host runs your tools

A stdio binary speaking JSON-RPC over the Model Context Protocol. Drop the path into Claude Desktop, Cursor, Cline, or Continue, and the host registers four tools: retransfer_send_files, retransfer_list_transfers, retransfer_get_transfer, retransfer_revoke_transfer. The send_files tool collapses our four-step REST flow (file init → presigned upload → complete → transfer finalize) into one call — because no model wants to chain four tool invocations to do what a human would call "send a file".

On a 401 the binary auto-rotates if you set RETRANSFER_REFRESH_TOKEN. On a 429 it surfaces retry_after_seconds so the host can show the user a wait time instead of "something went wrong".

A2A — when another agent needs to discover you

The AgentCard is a JSON document, not a runtime. Its job is to let a third-party agent figure out, in one HTTP GET, that ReTransfer exists, what skills it exposes, how to authenticate, and what the upper rate-limit envelope looks like — before any expensive call. streaming: false, pushNotifications: true, stateTransitionHistory: false — three capability flags that tell the caller exactly which integration patterns are on the table.

Read this and then you call the REST endpoints. The card is a sign on the door, not the kitchen.

REST — when you control the runtime

You’re writing in Go or Python or TypeScript and you’d rather not spawn a subprocess. Or you’re wiring ReTransfer into an existing workflow engine that already speaks HTTP. The full call sequence is yours: POST /v1/files/init → presigned PUT to object storage → POST /v1/files/{id}/completePOST /v1/transfers/initPOST /v1/transfers/{id}/finalize. More moving parts than the MCP send, but you get resumable uploads, batching, and your own retry strategy.

Same primitive, three surfaces — pick the one closest to where your code already lives.

A quick decision

Are you inside an MCP-aware host (Claude Desktop, Cursor, Cline, Continue)? Use MCP and stop reading. Are you a remote agent that needs another agent to find you programmatically? Publish your own A2A AgentCard and consume ours. Anything else — build against the OpenAPI doc and treat the other two as polish for specific callers.