aft.page
Deploy HTML, a folder, or a public GitHub repo to a live HTTPS URL. No account needed; claim the site to keep the same URL across updates.
Hosted MCP Server
npx add-mcp 'https://mcp.aft.page/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
What it is
aft.page hosts small software — one-pagers, prototypes, agent-built HTML, static SPAs — at a shareable HTTPS URL in seconds. The name comes from the aft of a ship: the back of the vessel — take what you built and put it out into the world.
Prefer the MCP tools below. If MCP is unavailable, call the API directly.
Quick start
Thin remote MCP — one deploy tool (html or files), plus aft_deploys, aft_rollback, aft_health. No account. Point your host at:
https://mcp.aft.page/mcp
MCP host config (preferred)
Add a server named aft-page with the remote URL (Cursor, Claude Desktop, and other Streamable HTTP clients):
{
"mcpServers": {
"aft-page": {
"url": "https://mcp.aft.page/mcp"
}
}
}
Reload the host, then ask:
Deploy this to aft.page
The agent should call deploy and return a live URL.
Tools
aft.page does not build your app. Detect first: plain HTML → upload those files; Vite/React → npm run build then dist/ only; Next static export → out/. Never upload src/ or node_modules.
deploy
One tool. Pass html or files — not both. Goes live immediately. One-file pages use html; multi-file / SPA build output uses files (always include index.html).
| Input | Type | Required | Notes |
|---|---|---|---|
html | string | one of | Full document; prefer <!DOCTYPE html>…</html> |
files | array | one of | Built static files. Paths relative; UTF-8 or base64. |
preferred_slug | string | no | Hint like about-me. First hit never overwrites — collision gets a suffix. Required with edit_token. |
edit_token | string | no | From first deploy / .aft/state.json. When set, MCP PATCHes this slug — same URL, new rollback row. |
Returns text + structured content:
Live: https://{slug}.aft.page
Claim: https://aft.page/claim?token=…
slug: …
deploy: dep_…
files: 1
Claim within 24 hours or drop again.
Limits: 500 files · 25 MB each · 100 MB total. Paths: no .., no leading /, no \.
aft_deploys
List rollback-able deploys for a slug. Needs edit_token from .aft/state.json. Same D1 history as the project UI after claim.
aft_rollback
Point the live slug at a prior deploy_id. Same URL. Unclaimed: edit_token. After claim, the anonymous token is retired: the owner mints one with POST /v1/sites/{slug}/deploy-token while signed in and hands it to the agent (DELETE revokes it).
aft_health
Ping the deploy API. No inputs. Returns ok and the API base.
Prompt: deploy_to_aft
Optional prompt that steers the agent:
- Detect first: plain HTML vs JS app (Vite/React/Next).
- Call
deploywithhtmlorfiles. - Vite/React:
npm run build, thendist/only. Next static export:out/. - Pass
preferred_slugwhen sensible. Persist.aft/state.json; later deploys sendedit_token. - Return the live HTTPS URL as the deliverable.
- Do not invent account or repository setup steps.
Arg: html_or_files (optional context string).
API (no MCP)
Base: https://api.aft.page. CORS: * on deploy + site GET.
GET /health
{ "ok": true }
POST /v1/deploy
Query: optional ?slug=preferred-name. Optional header X-Aft-Client: mcp|web|extension|curl|cli|mac (metrics). Three body shapes:
A) Raw HTML
POST /v1/deploy?slug=hello
Content-Type: text/html; charset=utf-8
<!DOCTYPE html><html><body><h1>Hi</h1></body></html>
B) JSON files (agents)
POST /v1/deploy
Content-Type: application/json
{
"files": [
{ "path": "index.html", "content": "<h1>App</h1>" },
{ "path": "style.css", "content": "body{margin:0}" }
]
}
C) Multipart — fields file0 + file0_path, …
Success (200)
{
"ok": true,
"slug": "hello",
"deployId": "dep_…",
"url": "https://hello.aft.page",
"files": 1,
"bytes": 42,
"editToken": "aft_edit_…",
"claimUrl": "https://aft.page/claim?token=…",
"owned": false,
"notice": "Claim within 24 hours or drop again."
}
Errors include no_files, too_many_files, file_too_large, payload_too_large, bad_path, reserved_slug, slug_exhausted, internal. Invalid ?slug= patterns are ignored (random unique slug); only reserved names return reserved_slug.
Serving
| URL | Behavior |
|---|---|
https://{slug}.aft.page/ | Primary — index.html |
https://{slug}.aft.page/{path} | Static asset |
If the requested object is missing, the Worker soft-falls back to that deploy’s index.html (SPA-style). Content-Type follows the file extension when the upload type is missing or application/octet-stream (so CSS is text/css).
Slugs
- Prefer valid
?slug=/preferred_slugwhen provided. - Else allocate a random unique slug.
- Clients (paste UI, extension, agents) may derive a hint from
<title>/<h1>and pass it as?slug=— the Worker does not parse HTML for a name. - First POST never overwrites — append a short word suffix on collision. PATCH with
edit_tokenupdates that slug in place. - Reserved:
www,api,app,admin,docs,status, …
Claim / manage
https://aft.page/claim?token=…
The live URL is the product. After deploy, open claimUrl to add email or Google. Unclaimed sites also show Claim on the live slug. After claim, Manage opens the project page (invite, rollback, secrets, custom domains — first one free). Keep editToken to prove you deployed.
Limits (today)
| Limit | Value |
|---|---|
| Files per deploy | 500 |
| Per-file size | 25 MB |
| Total payload | 100 MB |
| Auth | None (anonymous) |
| Unclaimed retention | Deleted after 30d idle. Visit, PATCH, or claim to keep. |
| Overwrite | Never |
| Custom domains / private invite | After claim · first one free (Domains tab) |
Examples
Minimal HTML via curl
curl -sS -X POST "https://api.aft.page/v1/deploy?slug=curl-smoke" \
-H "content-type: text/html; charset=utf-8" \
--data-binary @index.html
Agent rule of thumb
- Prefer MCP
deploy(htmlorfiles). - Return the live URL.
- Optionally link the preview shell.
- Do not invent auth steps that do not exist yet.
Troubleshoot
| Symptom | Fix |
|---|---|
| MCP tools missing | Reload host; check absolute path; Node 20+ |
reserved_slug | Change title / slug |
| Size errors | Stay under 25 MB / file, 100 MB total |
| Unstyled CSS | Ensure path ends in .css; re-deploy |
| Site 404 | Wait for DNS; confirm the slug |