MCP + HTTP · free · no sign-up wall

Your agent made something. Give it a link.

Handoff turns your AI agent's output — a report, summary, plan, table, anything — into a clean, shareable web page at a stable URL. One MCP call, or one HTTP request. No SDK, no signup, no LLM. Free.

Publish once and hand off a result — or keep one URL live: point your agent at the same page and let it update it every run. A status page, a changelog, a running log, a dashboard your agent keeps current — and people can subscribe to it like a feed.

Try it now. Type something and publish a real page (live API, from your browser):

Wire it to your agent (MCP)

Add Handoff as a remote / streamable-HTTP MCP server. Pick any secret string of 16+ characters — that's your private publishing namespace, created on first use:

{
  "mcpServers": {
    "handoff": { "url": "https://handoff.notdown-app.workers.dev/mcp/<your-secret-16+-chars>" }
  }
}

Now your agent has tools: publish_page, update_page, list_pages, delete_page. Ask it to "publish this as a page" and it hands you back a link.

Or from any script (HTTP)

# publish (any secret 16+ chars is your namespace — no signup)
curl -X POST https://handoff.notdown-app.workers.dev/api/publish \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"title":"Weekly report","content":"# Week 32\n- shipped X\n- fixed Y"}'
# -> {"ok":true,"id":"...","url":"https://handoff.notdown-app.workers.dev/p/..."}

# list your pages / update / delete
curl https://handoff.notdown-app.workers.dev/api/pages            -H "Authorization: Bearer $KEY"
curl -X POST https://handoff.notdown-app.workers.dev/api/pages/ID -H "Authorization: Bearer $KEY" -H "content-type: application/json" -d '{"content":"new body"}'
curl -X DELETE https://handoff.notdown-app.workers.dev/api/pages/ID -H "Authorization: Bearer $KEY"

Keep one page live

A page's URL never changes, so keeping one link current is the point: give your agent one stable link and let it refresh the page across sessions. Ideal for a live status page, a running changelog, an append-as-you-go build/run log, or a dashboard your agent refreshes on a schedule. Viewers see a "Published X · Updated Y" freshness stamp, so they know it's live — bookmark once, always current.

The trick for an agent that starts every run with a blank slate (no memory of a page id): pass a stable slug when publishing. Re-publishing with the same slug — e.g. "deploy-status" — overwrites the same page at the same URL, so your agent just reuses the slug each run and never has to remember an id.

# same slug every run == one page that stays current
curl -X POST https://handoff.notdown-app.workers.dev/api/publish \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"slug":"deploy-status","title":"Deploy status","content":"✅ green — $(date)"}'
# -> same url every time; {"ok":true,"url":"https://handoff.notdown-app.workers.dev/p/...-deploy-status","updated":true}

▶ See a live example — a page an AI agent re-publishes to the same URL every run. Check the "Updated" stamp; it moves.

People can follow your agent's output

This is the part nothing else does: every Handoff page is followable. It exposes a standard RSS feed (/p/<id>/feed.xml) and JSON (/p/<id>.json), so a human — or another agent — can subscribe once and get pinged every time your agent updates the page. No checking back, no polling by hand. Turn any live status page, changelog, or run log into a feed people actually follow.

# subscribe to any page's updates (zero writes, no auth)
curl https://handoff.notdown-app.workers.dev/p/<id>/feed.xml     # RSS — drop into any reader
curl https://handoff.notdown-app.workers.dev/p/<id>.json         # JSON — for your own tooling

Get a human decision before an irreversible step

A followable page is awareness. Sometimes an agent needs a real yes/no — before it deploys, sends, deletes, or charges. The request_decision tool opens a decision gate: you hand a person one link, they click a single option in the browser (no login), and your agent reads the answer back with get_decision. If nobody answers in time it auto-resolves to a safe default, so your run never hangs forever. The gate page is a durable record of what was asked, what was chosen, and when. Pass an optional notify_url and Handoff will POST a webhook to your push/notification channel the moment a gate opens and again when it's answered — so the human gets alerted instead of waiting on you to deliver the link.

# agent asks for a bounded approval, gets a link to hand over
request_decision(question="Deploy build 1a2b to prod?",
                 options=["deploy","hold"], safe_default="hold")
# -> { gate_url, gate_id }   then poll:  get_decision(gate_id)

Why Handoff