Docs

Connect your agent

Connect aft.page to any MCP-compatible client. The server URL is always https://mcp.aft.page/mcp — no account, no API key. The URL is the whole config.

To skip the hand-editing, paste Set up aft for me: https://aft.page/setup into your agent. It reads https://aft.page/setup.md and writes the config for whichever client it's running in.

Pick your client

Every client below points at the same remote MCP server. There is no key to paste and no sign-in step: deploy tools work anonymously, and you only log in later if you want to claim a site and keep it.

Claude Code (plugin)

The plugin bundles the deploy skill and the MCP connection, and updates itself — you never reinstall it to get new guidance.

/plugin marketplace add aft-page/plugins
/plugin install aft-page@aft-page

Restart your Claude session so the MCP server and tools take effect, then ask “Deploy this to aft.page”. In auto mode Claude Code won't let an agent install a plugin on its own, so type it yourself. Use the plugin or the claude mcp add line below, not both — the plugin already includes the server.

Any agent with the plugins installer

npx plugins add aft-page/plugins

One install for Claude Code, Cursor, Codex, Copilot CLI, VS Code, Grok, and Kimi. Restart the agent, then ask it to deploy.

Claude Code · Cursor · Windsurf (MCP by hand)

Claude Code writes the config for you:

claude mcp add --transport http --scope user aft-page https://mcp.aft.page/mcp

By hand the entry is the same everywhere — Claude Code ~/.claude.json (user) or .mcp.json (project), Cursor ~/.cursor/mcp.json, Windsurf ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "aft-page": {
      "type": "http",
      "url": "https://mcp.aft.page/mcp"
    }
  }
}

Windsurf takes serverUrl instead of url. There is no headers block — no token, no OAuth app to create. Reload the host, then ask “Deploy this to aft.page”.

Claude.ai · ChatGPT · Grok — connectors

Paste the URL and nothing else:

https://mcp.aft.page/mcp
  • Claude — Settings → Connectors → Add custom connector.
  • ChatGPT — turn on Developer mode (Settings → Security and login), then add the URL as a custom server.
  • Grok — grok.com/connectors → New connector → Custom.

Start a new conversation after adding it so the tools load. Deploys from a connector land on a live URL like any other; keep one by opening its claim link and attaching it to an account.

No MCP — CLI

curl -fsSL https://aft.page/install | sh
aft deploy

No login to ship. Agents adding --json get one result object, no prompts, exit 0 = live. CLI guide.

Deeper per-client notes live under /with/claude, /with/cursor, /with/codex, and /mcp.

Guides

Once your agent is connected, the same deploy paths work by hand. Same result: https://{slug}.aft.page.

API (one-shot)

curl -X POST https://api.aft.page/v1/deploy \
  -H 'Content-Type: text/html' \
  -H 'X-Aft-Client: curl' \
  --data '<h1>Hello from aft.page</h1>'

Multi-file: JSON { "files": [{ "path", "content" }] } or multipart. Optional ?slug=vite-hello. A collision gets a numeric suffix — an existing slug is never overwritten by a new, unrelated deploy. Full schemas: /mcp.md.

What we support

aft.page does not run npm run build. You (or your agent) detect the project, build locally if needed, then upload ready files.

Kind Ship Live
Plain HTML One file via Drop / MCP deploy hello.aft.page
Multi-file static HTML + CSS + JS share-checklist
Vite / React / Vue / Svelte / Astro dist/ only vite-hello.aft.page
Next.js static export out/ only —
Next SSR / Worker Next.js (SSR) + upstream next-hello.aft.page

Never upload src/, node_modules, or .next/. Always include index.html for static sites. Per-framework build steps, verified vs. untested frameworks, and what still needs a runtime we don't run yet: Frameworks guide.

Logs

Owner and editors: project page → Logs, or GET /v1/sites/{slug}/logs. Request-level hits and errors for the live site, plus recent deploy failures. No IP addresses. Kept 7 days.

{
  "slug": "my-app",
  "views": { "today": 12, "d7": 340 },
  "lastServedAt": "2026-08-12T09:14:02.000Z",
  "events": [
    { "createdAt": "2026-08-12T09:14:02.000Z", "method": "GET",
      "path": "/api/widgets", "status": 500, "bytes": 214, "country": "US" }
  ],
  "deployFailures": [],
  "observability": {
    "window": "12h",
    "totals": { "ok": 40, "fourxx": 2, "fivexx": 1, "bytes": 12000, "requests": 43 },
    "buckets": [],
    "countries": [{ "country": "US", "n": 30 }, { "country": "IN", "n": 13 }]
  }
}

events is capped at the most recent 100 rows; only document-shaped hits and non-2xx responses stick around — noisy favicon/robots-style paths are filtered before they're stored.

Source

Owner, editors, and viewers: project page → Source, or GET /v1/sites/{slug}/files. File list for the current deploy (or an older one via ?deployId=); click a text file to preview its contents. This is a private read of what you uploaded — it's not a public URL, and the live site is unchanged by looking at it.

{
  "slug": "my-app",
  "deployId": "dep_abc123def456",
  "files": [
    { "path": "index.html", "bytes": 1820 },
    { "path": "assets/app.js", "bytes": 44210 }
  ]
}

Limits

Static worker / next
Files 500 500
Per file 25 MB 25 MB
Total 100 MB 100 MB

Live for 60 minutes. Claim within 7 days to keep it. Claim guide.

Examples