beam.page

Static-site hosting via MCP — ask your AI client to build a site (lands at .beam.page or your custom domain), then update it anytime, from anywhere, just by…

Community: Submitted by a user or imported; check the owner before granting accessOnlineNo sign-inGlobalFreeCan modify data

What it can do

  • Guide: Fetch the platform's capabilities brief — the orient guide. When to use: `topic="orient"` once at the start of a session to load the capabilities map. Day-to-day shape and runbook detail — bill
  • Whoami: Look up the current caller's profile — tenant, email, name, account type (guest or Google), limits, usage, and terms-acceptance status. When to use: call before any capability-gated decision s
  • Project: Create / update / delete / list / get projects (each project is a microsite on `<slug>.beam.page`). When to use: for every project-scoped lifecycle action. `op=list` to answer "what sites do

What data it sees

Do you need an account

No: the server works without sign-in

Static-site hosting via MCP — ask your AI client to build a site (lands at .beam.page or your custom domain), then update it anytime, from anywhere, just by asking again.

How to connect

How to connect beam.page to Claude

The server address to paste into Claude settings:

https://beampage--lola2-limited.run.tools

  1. Open Claude (claude.ai or the desktop app).
  2. Go to Settings → Connectors.
  3. Click Add custom connector.
  4. Paste the server address copied below into Remote MCP server URL and click Add.
  5. In a chat, click + → Connectors and switch the new connector on.

Custom connectors are available on Free, Pro, Max, Team and Enterprise plans (Free is limited to one). On Team and Enterprise an organization Owner adds the connector first under Organization settings → Connectors.

Authorization

After you click Add, a sign-in window for the service opens. Sign in with your own account and approve access. Claude never sees your password.

How to connect beam.page to ChatGPT

The server address to paste into ChatGPT settings:

https://beampage--lola2-limited.run.tools

  1. Open ChatGPT in a browser (chatgpt.com). A Plus, Pro, Business, Enterprise or Edu plan is required.
  2. Turn on developer mode once: Settings → Apps → Advanced settings → Developer mode.
  3. Open Settings → Connectors and click Create.
  4. Fill in the form: Name (anything), Description (one line about what the service does), MCP server URL (copy it below).
  5. Under Authentication choose OAuth if the service requires sign-in, otherwise None. Click Create.
  6. In a new chat open + → Apps/Connectors and enable the connector.

OpenAI has renamed this section before (Connectors → Apps/Plugins). If the label differs, search settings for "developer mode". On Business/Enterprise workspaces an admin must allow custom connectors first.

Authorization

On first use ChatGPT opens the service's sign-in window. Sign in and approve access.

How to connect beam.page to Cursor

The server address to paste into Cursor settings:

https://beampage--lola2-limited.run.tools

Fastest: click Open in Cursor below and confirm the prompt.

Manually:

  1. In Cursor open Settings → Cursor Settings → MCP and click Add new global MCP server.
  2. Paste the JSON copied below into ~/.cursor/mcp.json (per project: .cursor/mcp.json in the repo root).
  3. Save the file. The server appears in the MCP list; authorize it there if asked.

Authorization

If the service needs sign-in, an authorize button appears next to the server in the MCP list.

Server tool list (20)

Raw names from tools/list. Only developers need these.

guideFetch the platform's capabilities brief — the orient guide. When to use: `topic="orient"` once at the start of a session to load the capabilities map. Day-to-day shape and runbook detail — billing state, contact-form worked example, custom-domain attach flow, error recovery — lives in the live API responses' `brief.next`, `_comment`, and `capabilities.actions` blocks. Call the relevant endpoint and read the response. How: pass `topic="orient"`. The orient guide is served by the apex site at `llm.txt`. Limitations: read-only, idempotent; returns isError=true on network failure. The only allowed topic is `orient`; anything else returns a validation error. See §The API teaches you as you go in guide(topic="orient").
whoamiLook up the current caller's profile — tenant, email, name, account type (guest or Google), limits, usage, and terms-acceptance status. When to use: call before any capability-gated decision so you read `limits` and `accountType` from the live response rather than assuming. How: dispatches to `GET /me` with no parameters. Limitations: read-only, idempotent; surfaces upstream auth errors unchanged. See §Check what exists in guide(topic="orient").
projectCreate / update / delete / list / get projects (each project is a microsite on `<slug>.beam.page`). When to use: for every project-scoped lifecycle action. `op=list` to answer "what sites do I own?"; `op=get` for depth on one; `op=create` to mint a new subdomain; `op=update` to edit the context; `op=delete` to tear it all down. How: pick `op` from the enum; each op lists its required/optional params in the oneOf description. Slug is globally unique, lowercase, 2–63 chars, [a-z0-9-]. `context` is free text shown in the management UI and returned by `op=get`. Limitations: `op=delete` is destructive and irreversible. The per-account project cap is enforced server-side — read it from `whoami().limits.maxProjects` before offering to create. Responses carry `views` (lifetime / 7d / 30d) per project and per page; on `op=get` the response always surfaces a `views.setup` hint with the `<script src="/assets/beacon.js" data-project="<slug>" data-api-host="https://api.beam.page" defer></script>` snippet — paste that line into the <head> of every page you want counted (the default templates already include it). See §Building projects in guide(topic="orient") for the project shape and slug rules.
pageCreate / update / delete / read sub-pages within a project. When to use: for every page-level lifecycle action. `op=create` for a new sub-page (`/about`, `/menu`, `/app`); `op=update` to edit notes or replace the structured metadata object; `op=delete` to remove a sub-page and its files; `op=get` to inspect one page. How: pick `op` from the enum. `op=update` with only `notes` edits notes via `PUT /pages/<slug>`; with only `metadata` replaces the metadata via `PUT /pages/<slug>/metadata`; with both it does both and returns a combined result. Limitations: `metadata` replaces (does not merge) the existing object, capped at 200 KB. `op=delete` on `_root` is rejected by the platform. `op=update` with neither `notes` nor `metadata` fails validation. Responses on `op=get` carry `views` (lifetime / 7d / 30d) for the page; the page response also carries a top-level `_comment` enumerating every operation available on the page. See §Building projects in guide(topic="orient") for notes vs. metadata, the `_root` alias, and the <base href="/<slug>/"> rule for sub-pages.
upload_textUpload a text file (HTML, CSS, JS, JSON, XML, SVG, TXT) to a page or to the project's shared `/assets/` bucket, inline or via URL-fetch. When to use: when you already have the bytes as UTF-8 text (HTML you generated, JSON you built, an SVG you composed) — pass `content`. When the bytes already live at a public HTTPS URL the platform should fetch — pass `url`. Overwrites an existing file of the same name without prompting — the response confirms the upload. How: `scope=page` with `slug` + `filename` + one of `content` / `url` → `PUT /projects/<id>/pages/<slug>/files/<filename>`. `scope=asset` with `filename` + one of `content` / `url` → `PUT /projects/<id>/assets/<filename>`. Limitations: `content` caps at 4 MB and is raw UTF-8 — do NOT pass base64 binary; it won't be decoded. URL-fetch is HTTPS only, 4 MB, 10 s timeout, no redirects, private IPs rejected. `content` and `url` are mutually exclusive; one of them is required. For binary bytes in hand (images, PDFs, fonts) use `upload_binary`; for a multi-file folder tree use `upload_zip`. See §Building projects → Files in guide(topic="orient") for the full upload-method table.
upload_binaryRequest a presigned S3 PUT URL for a binary file (image, PDF, font, video) on a page or as a shared asset. The client then PUTs the bytes directly to the returned URL. When to use: when you have the binary bytes in hand and the runtime can execute outbound HTTPS PUTs. If the runtime cannot drive an outbound PUT, this tool returns a URL the caller cannot use — for already-public HTTPS bytes use `upload_text` with `url=...`; for the case where the user has the bytes on a device you cannot reach (laptop, phone, tablet, borrowed machine) use `drop_zone` to issue a browser link. How: `scope=page` with `slug` + `filename` → `POST /projects/<id>/pages/<slug>/files/<filename>/upload-url`. `scope=asset` with `filename` → `POST /projects/<id>/assets/<filename>/upload-url`. Response is `{uploadUrl, expiresIn, headers, publicUrl, curlCommand, brief}`; echo every header in `headers` exactly on the PUT — mismatch fails with `SignatureDoesNotMatch`. `publicUrl` is where the file will be served once the projection catches up; `curlCommand` is a paste-ready PUT line. Limitations: URL expires in 15 minutes. Returns an envelope, not the uploaded bytes — the actual PUT happens client-side. See §Building projects → Files in guide(topic="orient") for the full upload-method table.
upload_zipRequest a presigned S3 PUT URL for a ZIP archive that the platform extracts entry-by-entry onto a page. When to use: when you have a multi-file folder tree to ship — SPA builds, photo galleries, multi-page HTML trees. Anything more than a handful of files. For a single binary file use `upload_binary`; for text bytes you already have use `upload_text`. How: `POST /projects/<id>/pages/<slug>/upload-zip-url` returns `{uploadUrl, expiresIn, headers, publicUrl, curlCommand, brief}`; then PUT the ZIP bytes to `uploadUrl` echoing every response header exactly. `publicUrl` is the page URL where the extracted site will be served; `curlCommand` is a paste-ready PUT line. Extraction is asynchronous: the S3 200 from PUTting the bytes lands before the archive is extracted. After PUTting, poll the page — page(op="get") or GET /projects/<id>/pages/<slug> — to confirm: a successful extract shows the new file set; a failed extract surfaces a lastZipFailure object (machine-readable reason + human-readable summary) on the page response. Limitations: **ZIP upload REPLACES the page's full file set** — anything on the page not in the archive is deleted, same shape as Netlify / Vercel / GitHub Pages. 500 MB decompressed cap; 100× compression-ratio guard; `index.html` required at the ZIP root; URL expires in 15 minutes. Requires a runtime that can execute outbound HTTPS PUTs. See §Building projects → ZIP in guide(topic="orient") for the allowlist and failure modes.
read_contentRead a page-scoped file or a project-scoped asset's full contents. When to use: before an `edit` batch (copy exact bytes into the `old` field), or to verify what was deployed. How: `scope=page` with `slug` + `filename` → `GET /projects/<id>/pages/<slug>/files/<filename>`. `scope=asset` with `filename` → `GET /projects/<id>/assets/<filename>`. Limitations: text files return as `{content, contentType}`; binary files return as `{base64, contentType}`. Read-only, idempotent. See §Search and edit in guide(topic="orient").
searchFind every text file in a project that matches a literal substring or regex pattern, with line numbers and surrounding context. When to use: before a multi-page edit, to enumerate every page that mentions a phone number / email / URL / brand name; before deleting an asset, to confirm no page references it; for backlink, broken-link, and 'pages without an og:image' audits. How: pass `projectId` and `query`. Defaults: `mode="literal"`, `caseSensitive=false`, `scope="all"`. Switch `mode` to `"regex"` for structural patterns (Python `re` syntax). Restrict with `scope` to `"pages"` or `"assets"`. Dispatches to `POST /projects/<id>/search`. Limitations: content-only — to find files by name, use the project or page index. Binary files (PNG, JPG, PDF, WebP, ICO, HEIC) are skipped silently; only the text content types HTML/CSS/JS/JSON/XML/SVG/TXT are scanned. Per-call cap: 200 occurrences total, 50 per file; matching lines are trimmed to 200 chars centred on the first match. Per-user rate limit: 60 calls/minute (visible on GET /me as `searchCallsPerMinute`). Read-only — never mutates state. Cross-project search, fuzzy / typo-tolerant search, and fielded search are out of scope. See §Search and edit in guide(topic="orient").
delete_contentDelete a page-scoped file or a project-scoped asset. When to use: when the file is actually unwanted — prefer `upload_text` overwrite or `edit` for corrections. How: `scope=page` with `slug` + `filename` → `DELETE /projects/<id>/pages/<slug>/files/<filename>`. `scope=asset` with `filename` → `DELETE /projects/<id>/assets/<filename>`. Limitations: destructive. The main page's `index.html` cannot be deleted — overwrite it with `upload_text` instead. Deleting a shared asset removes it from every page that references `/assets/<filename>`. See §Building projects → Files in guide(topic="orient").
compressResize / transcode / quality-tune an image already in the project. Reads bytes from one slot, writes a sized variant to another (or back to the same slot for in-place compression). When to use: after any image upload, before referencing it in HTML — drop-zone bytes especially since modern cameras (phone, mirrorless, tablet) all produce full-resolution sources; HEIC photos always (HEIC won't render in most desktop browsers, so the default auto-format pick transcodes HEIC → WebP). Call repeatedly with different `target.filename` values to produce responsive thumbnail / medium / desktop triples from one source. How: pass `projectId`, `source`, and `target`. Both refs use the {scope, slug?, filename} triple already established by `read_content` and `delete_content` (slug required when scope=page; '_root' for the main page). The optional `maxDimension`, `format`, and `quality` dials override the defaults. Dispatches to `POST /projects/<id>/compress`. Defaults: `maxDimension: 2048` (longest side; aspect preserved; smaller sources are not up-scaled), `format: 'auto'` (HEIC/HEIF → WebP; JPEG → WebP; PNG → PNG; GIF → GIF; WebP → WebP), `quality: 85`. EXIF orientation is baked into the output pixels and EXIF metadata is stripped (privacy + bytes). Limitations: compress operates only on bytes already in the project — passing `url` / `content` / `base64` in `source` is rejected with a pointer at the two-step `upload_text({url:...}) → compress(...)` pattern. Inputs must be JPEG, PNG, GIF, WebP, HEIC, or HEIF; SVG is rejected with vector guidance; video MIMEs are rejected with the same shape. Source must decode to ≤64MP. Output respects `maxFileSizeBytes` (4MB); too-large outputs return 413 with a hint to lower `quality` or `maxDimension`. Each variant counts against the existing per-page or per-project file cap (no new physical limit). Per-user rate limit: 60 calls/minute (visible on GET /me as `compressCallsPerMinute`). Animated WebP / AVIF / video are out of scope. See §Building projects → Images / compressing in guide(topic="orient").
editFind-and-replace inside existing files — preferred over re-uploading the whole file for small changes (saves ~25,000 output tokens per edit vs. re-upload). When to use: surgical edits to a page file or an asset. Run `search` first when the change spans multiple files so every match is enumerated before you compose `targets`. How: pass `projectId`, `edits` (non-empty array of `{old, new}` applied in order), and a target. - Single-target, page file: `{page: "<slug>", file: "<filename>"}`. Use `page: "_root"` to address the main page (the API form of the `/` slug used in URLs). - Single-target, asset: `{asset: "<filename>"}`. - Multi-target: `targets: [{page, file} | {asset}, ...]` — same edit set applied across many files in one call. Dispatches to `POST /projects/<id>/edit`. Atomicity: validation is fully transactional; the commit pass is best-effort per-target. Phase 1 reads every target and applies each edit in memory, checking that each `old` matches exactly once. Phase 2 only runs if Phase 1 succeeded for every target — then the per-target S3 PUT + `FILE_UPLOADED` / `ASSET_UPLOADED` event pair fires in order. If Phase 2 hits a transient error on target K (1 ≤ K ≤ N), targets 1..K-1 are committed and K..N are not; the response is 503 with `committed[]` / `uncommitted[]` arrays and a `brief.next` pointing the agent at a retry scoped to the uncommitted targets only. Validation failures (no `old` match, etc.) remain all-or-nothing — no writes land. Matching is byte-exact UTF-8. Typographic mismatches silently fail — curly quotes (`“”‘’`) vs straight (`"'`); `&mdash;` vs `—`; non-breaking space (`\u00A0`) vs regular space. On a no-match error, GET the file and copy the bytes verbatim into `old` — don't retype. See §Search and edit in guide(topic="orient").
redirectList / create / delete 301 redirects on a project. When to use: rename a page and keep inbound links working, point the bare subdomain at `/app/`, or clean up obsolete mappings. How: pick `op` from the enum. Both `from` and `to` are relative paths starting with `/` — cross-domain redirects (absolute `https://` URLs) are rejected 400 by the API. Limitations: self-loops (`from == to`) rejected 400. Conflicts are symmetric — creating a redirect that shadows an existing file or page fails 409, and creating a file or page where a redirect lives fails the same way. Chains (`/a → /b → /c`) are allowed; redirects count against the owning page's `maxFilesPerPage` quota. See §General app stuff → Redirects in guide(topic="orient").
custom_domainAttach / detach / read a custom domain on a project — Lone Creator feature, up to 3 custom domains per subscription, one per project. When to use: `op=attach` once the user has added a CNAME at their DNS host pointing at the `dnsTarget` (read it from a previous `op=get` or from `whoami().customDomains[].dnsTarget`); `op=get` to poll an in-flight attach (status advances `attach_requested → provisioning → live` over ~5 min) or to surface health on a live row; `op=detach` to free the hostname so the user can move it elsewhere. How: pick `op` from the enum. `attach` requires `projectId` + `hostname` (lowercased FQDN like `www.mybiz.com`); `get` and `detach` require only `projectId`. Limitations: Lone Creator subscription only; free / guest cannot attach. Up to 3 custom domains per subscription, one per project (a subscriber with three projects can put a unique hostname on each). Apex domains rejected 400 (use `www.` and a registrar redirect); reserved beam.page suffixes rejected 400; per-project 409 if this project already has one; per-subscription 409 if at the 3-domain cap; attach blocked on archived projects (409). Detach is final — re-attach issues a fresh ACM cert. Read the live `GET /projects/<id>/custom-domain` response for the full attach state, the `subPhase` polling enum, canonical-URL advice on the live response, and the `failureReason` / `failureMessage` fields when an attach lands in `status: failed`.
snapshotList / take / restore point-in-time snapshots of a project. When to use: `op=take` before a risky refactor or large edit; `op=list` to see what's available (nightly, initial, manual); `op=restore` to roll back after a bad change. Call `op=list` first to pick a valid `snapshot_id` before `op=restore`. How: pick `op` from the enum; every op requires `projectId`. Limitations: snapshots are Google-only — guest-owned projects have none. `op=take` is rate-limited to 1/project/min; `op=restore` to 1/project/5min. Restore is a full state replacement with three rules: (1) files in S3 but not in the snapshot are deleted; (2) pages that existed at snapshot time are re-created if absent and updated if present, with their snapshot-time notes and metadata; (3) pages created after the snapshot are removed, and pages deleted before the snapshot stay deleted. 30-day retention. See §Snapshots in guide(topic="orient"); the snapshot list response carries a top-level `_comment` enumerating every operation.
regenerate_sitemapForce a rebuild of the project's `sitemap.xml` and `cross_page_metadata.json` from the current page projection, then invalidate CloudFront for both files. When to use: only when out-of-band writes (direct `aws s3 sync`, an archive restore that drifted, an in-flight projection bug) have left the served sitemap ahead of or behind the current page set. Day-to-day page create / delete / restore already triggers regeneration automatically — you do not need this tool on the happy path. How: `regenerate_sitemap({projectId})` → `POST /projects/<id>/sitemap/regenerate`. The handler emits `SITE_REGEN_REQUESTED` and the site projection picks it up asynchronously. Limitations: rate-limited to 1 call per project per 5 minutes (429 on the second). Slugs whose page row has no `index.html` are filtered out of the sitemap (they 404 on the live site). Archived projects are blocked with 409. Cross-tenant calls 404 the same as any other tenant-isolated endpoint. See §General app stuff → Sitemap in guide(topic="orient").
drop_zoneOpen a short-lived browser drop-zone so a non-technical user can ship photos into this conversation. The drop-zone is just a browser link — it loads on any device the user has to hand. When to use: when the user has the photos on a device you cannot reach and you need the bytes themselves rather than a public URL. Examples: a laptop's download folder, a phone's camera roll, a tablet, files on a borrowed desktop, a Dropbox they have not shared. `op=issue` mints the browser link; share the `message` field verbatim with the user. Then `op=pickup` to read the photos (idempotent — call again to see new arrivals). `op=cancel` shuts the door early. How: `op=issue` with `projectId` (and optional `ttlSeconds`) → `POST /projects/<id>/dropzones`; `op=pickup` with `projectId` and `dropZoneId` (URL + metadata only by default; pass `preview: true` for inline thumbnails + `image` content blocks) → `GET /projects/<id>/dropzones/<dropZoneId>`; `op=cancel` → `DELETE /projects/<id>/dropzones/<dropZoneId>`. Limitations: bearer-by-URL — anyone with the link can upload; warn the user not to forward it. Default TTL 10 min (max 600 s, min 60). Up to 10 photos per link, 4 MB each, JPG/PNG/GIF/WebP/HEIC. Pickup returns each photo as URL + metadata only by default (`filename`, `mimeType`, `size`, `uploadedAt`, `signedFetchUrl`, `urlExpiresAt`) — no inline preview bytes, no `image` content blocks. Pass `preview: true` for each photo's inline `base64_preview` + `previewMimeType` plus one MCP `image` content block per photo. Bytes are wiped ~30 min after expiry — pick up promptly. The drop-zone uploads do NOT land in the project's `/assets/` or page files; pick them up here, then place each photo on the live site. Each photo in the pickup response carries a short-lived opaque `signedFetchUrl` — pass that URL to `upload_text({url: signedFetchUrl})` and the platform fetches the bytes server-side onto your chosen page or asset slot in one call. For binary bytes already in hand use `upload_binary`. See §Building projects → Drop-zone in guide(topic="orient").
fetch_liveFetch live content from a project's public CDN URL — the visitor's view, not the API. When to use: verify a deploy, debug what's rendering, or quote live content back to the user. Distinct from the `page` tool: `page` reads the API-side metadata, `fetch_live` reads the deployed HTML/CSS/JS/images. How: pass `projectId` (the slug). Optional `path` (default `/`), `limit` (default 8192 bytes), `offset` (default 0). Large pages are paginated — chunk with `limit`+`offset`. Limitations: for image content types the default response is URL + metadata only (`url`, `contentType`, `size`, debug headers) — no inline bytes flood the transcript. Pass `preview: true` to additionally receive one MCP `image` block with a server-resized JPEG thumbnail (max 800 px on the long edge). Text content returns the sliced body plus a JSON meta block carrying `status`, `contentType`, `finalUrl`, `headers` (Cache-Control / Age / ETag / X-Cache / Content-Length), and `{totalSize, truncated}`. A 3xx response surfaces as a typed envelope naming `status` and `location`; redirects are not followed — call fetch_live again with the new path if you want the target's body. Retries once on network error. Read-only, idempotent. See §The API teaches you as you go in guide(topic="orient").
feedbackSubmit feedback to the platform team — a bug report, a feature suggestion, or a flag on something that's broken. When to use: when the user surfaces a platform-level problem (not a problem with their content) or has a suggestion worth passing back. How: pass `message`. Dispatches to `POST /feedback`. Limitations: `message` required and non-empty, max 5000 chars. Stored and read by the team. See §Feedback in guide(topic="orient").
apiWARNING: last-resort escape hatch. Prefer a dedicated tool whenever one exists — `guide, whoami, project, page, upload_text, upload_binary, upload_zip, read_content, delete_content, compress, edit, redirect, custom_domain, snapshot, regenerate_sitemap, fetch_live, feedback` — they have stronger intent matching, cleaner argument schemas, and client-side validation so a missing field is caught before a REST call is charged. When to use: only when the endpoint you need has no dedicated tool. The auth-flow endpoints (`/auth/guest`, `/auth/google`, `/auth/convert`, `/auth/refresh`, `/auth/accept-terms`) are the main legitimate callers — and Claude's MCP connector already covers those via its `authenticate` / `complete_authentication` tools, so even that case is rare. How: `api({method, path, body?})` with `method` in `{GET, POST, PUT, DELETE}` and `path` starting with `/`. Limitations: path rejected if it contains `@`, `//`, `\`, or `:`. Auth is forwarded from the MCP request. 25 s timeout. Marked destructive + openWorld because an arbitrary REST call can do anything. See §Access (REST vs MCP) in guide(topic="orient").