Documentation
One hosted MCP endpoint. No dashboard, no local process, nothing to clone.
How this works
PagePilot is an MCP server that happens to have a landing page. Everything — publishing, listing, deleting — happens through MCP tools your agent calls. There is no dashboard and no login, on purpose: a UI would be one more authenticated surface guarding the same bucket.
The server runs inside this deployment at /api/mcp. Your agent talks to it over HTTP with your API key. Your R2 credentials never leave the server.
Your API key
Nobody issues this key — you choose it. It is the only credential your agents ever see, and the same value guards every tool.
openssl rand -hex 32Set it as PAGEPILOT_API_KEY in your deployment's environment variables, then use the same value when adding the MCP server below. Your R2_* credentials come from Cloudflare and stay server-side.
Connect your agent
A remote MCP server, so there is no path to get wrong and no runtime to install.
Claude Code
one command
claude mcp add --transport http pagepilot \
https://pagepilot.rafay99.com/api/mcp \
--header "Authorization: Bearer YOUR_API_KEY"Then just ask: “Write up the plan as an HTML page and publish it.”
Anything that reads mcp.json
Cursor, OpenCode, Command Code, Claude Desktop
{
"mcpServers": {
"pagepilot": {
"type": "http",
"url": "https://pagepilot.rafay99.com/api/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}Raw HTTP
any client that speaks MCP over Streamable HTTP
curl -X POST https://pagepilot.rafay99.com/api/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Tools
deploy_page
- html (string) — Full HTML content (required)
- title (string) — Label shown in the list
Returns: id, title, url, createdAt
list_pages
Returns: The 200 newest pages
delete_page
- id (string) — Page to remove
Returns: Confirmation
Who can read a page
Anyone holding the link. There is no login and no cookie — the 48-bit id in the URL is the credential, so a page is exactly as private as its link.
That means nobody can find your pages by guessing or crawling, but whoever you send a link to can forward it, and Slack, Discord and Notion all fetch a URL server-side to build previews. Pages send X-Robots-Tag: noindex and robots.txt disallows /p/, so a leaked link still can't become a search result. When you want a page gone, call delete_page — the URL dies immediately.
Your PAGEPILOT_API_KEY guards publishing, listing and deleting. Without it nobody can enumerate your vault or write to it, even though individual links are readable.
Run your own
1. Create an R2 bucket
- Cloudflare Dashboard → R2 → Create Bucket. Use a bucket dedicated to PagePilot.
- Leave public access disabled — no public development URL, no public custom domain on the bucket. Otherwise every page is fetchable straight from R2 and everything above is decoration.
- Create an API token with Object Read & Write on that bucket
- Copy the Access Key ID, Secret Access Key and your Account ID
2. Deploy to Vercel
Import the repo with Root Directory set to apps/web, then set:
R2_BUCKET=your-bucket-name
R2_ACCOUNT_ID=your-32-char-hex-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
PAGEPILOT_API_KEY=the-key-you-generated-above
# Only for a custom domain; otherwise defaults to your Vercel URL
PUBLIC_URL=https://pages.example.com3. Local development
Put the same values in .env.local, run bun run dev, and point an agent at http://localhost:3000/api/mcp. bun run test round-trips real pages through your bucket.