MCP.so
Sign In

Sitelas MCP ServerVerifiedFeatured

@Sitelas

Basic information

Transport

Streamable HTTP

Authentication

OAuth

Category

Productivity

Author

Sitelas

Submitted by

Usman Haider

About Sitelas MCP Server

The website builder for AI agents. Build a real website from a template in your chat, preview it, publish when you approve, then keep running it: edit pages, swap photos, and read and answer contact-form messages.

Connection details

https://sitelas.com/api/mcp

Setup

claude mcp add sitelas --transport http https://sitelas.com/api/mcp

Sitelas MCP Server tools

52

List all websites owned by the current user. Returns subdomain, publish status, plan tier, creation date, and URLs for each. Share `url` — it always resolves. `live_url` is null for a site that has never been published: that site has no public address yet, so say so and offer publish rather than handing over a link. Use get_site for per-site detail.

Fetch details of a single site: pages (slug/title/order/edit URL, plus each page's own search settings and old redirecting URLs when it has any), theme summary, search settings (site-wide title/description/share image/noindex/language, the stated business details, ownership verification codes, connected analytics/ad tracking IDs, and the name and social profiles read from the site), publish status, and URLs. Share `url` — it always resolves. `live_url` is null when the site has never been published: it has no public address yet, so say so and offer publish rather than handing over a link. Does NOT return the node tree (use get_page (with view: "structure")) or version history (use get_site_changes (with include_versions: true)). By default returns a slim theme (slug + palette tokens + font families) to keep the response compact; pass include_theme_config=true if you need the complete theme config object. Pass `include` for more in the same call: 'publish_state' (what is live, edited or draft-only, page by page), 'brief' (the site's design memory), 'data_files' (files embeds load).

Summarise what changed: pages added or removed, pages whose content changed, and whether the theme or shared header/footer changed. By default the draft against what is live — what is waiting to be published. `since: 'previous'` includes the last publish; `from` + `to` compares any two of live / draft / previous. `include_versions` adds the three snapshots. Read-only.

Create a new Sitelas site by cloning a template archetype. The template provides the design skeleton (page composition, section layouts, typography rhythm); you customize it for the specific brief via brand_overrides at create time + ONE bulk patch_nodes call afterwards (the only patch primitive — no per-node fallback). The site is saved as a DRAFT — not publicly live until the user approves and you call publish. Returns `url` (preview URL, best shared after customization patches are applied), `site_id` for subsequent edits, `manifest` (every patchable node id + current text + slot, replaces get_site for discovery), `customization_brief` (prose guidance for this archetype), `overrides_applied`, `alternative_templates` (other templates and who each suits, for an owner who wants a different design), and `next_step` (a suggested follow-up summary). Standard customization pattern after this call: read the manifest, build one patches array covering every text/image/cta change across all pages and siteElements, send one patch_nodes call. If the owner described pages, home sections, a hero style or light/dark, pass `layout` and the response's `layout_plan.steps` lists the calls that add what the template lacks.

Persist a fixture YOU composed as a new Sitelas site. Use this when the archetype-based create_site_from_template can't produce the structure you need — primarily for URL cloning (post-capture_url, when the source's section composition doesn't map cleanly to any shipped archetype), or for genuinely bespoke briefs that no archetype satisfies. For typical generation, prefer create_site_from_template — its archetypes are designer-tuned and produce higher-quality output by default. The fixture is validated (create_site_from_fixture (with dry_run: true) autofixes minor issues, throws on hard structural errors), then saved as a DRAFT (live URL 404s until publish). Returns the preview URL, site_id, manifest of patchable nodes, and warnings (e.g. unreachable pages). Pass dry_run: true to validate a fixture without creating a site.

Replace an existing site's DRAFT. With template_slug: switch it to another template — the owner said 'not my style' and picked another design. The whole draft is replaced (pages, theme, header and footer), so redo the copy and photos for the business afterwards. With fixture: replace everything with a fixture you composed; with page_updates: replace only the listed pages' trees. Saved as a draft — the live URL is unchanged until publish. Share `url` (the preview) and relay `next_step`; never say it is live.

Permanently delete a site — its subdomain row, all version snapshots, all content_items, all submissions, the alias history, attached custom domains (removed from Vercel), any active billing for the site (cancelled), and any media in the storage bucket. IRREVERSIBLE. Use this when the user explicitly asks to scrap a site (e.g. 'delete my-portfolio-3947, I don't want it' or 'I created the wrong template, throw it away'). Requires `confirm: true` to be deliberate. Do NOT call this on user-said-something-vague — ask 'are you sure you want to delete {subdomain} permanently?' first and only call after explicit yes. There is no undo. If the billing cancellation fails, the entire delete is aborted so the user is never charged for a deleted site.

Choose the template a new site starts from. RANK (with a brief): pass audience plus keywords (the business in the owner's words — corroboration is computed from them) and needs (what the site must do); it scores every template and explains the ranking. Check the winner's `match_quality`: 'corroborated' = the brief's words appear in its use cases; 'structural' = it won on audience/goal alone — usually still build from it, call it a starting point, and push harder on brand, imagery and copy. If `out_of_scope` is not empty the owner asked for something Sitelas does not build (logins, a cart, live data, an app): say so plainly BEFORE creating and offer its `instead`. If `audience_check` is set, re-run with the audience it names. When the owner described the site's shape (pages, hero, light or dark) pass it as `layout`, and after creating run every call in `layout_plan.steps`. BROWSE (no brief — 'what designs are there?', 'what else?'): no arguments gives an overview of every audience and its templates; `audience` opens that shelf in recommended order; `query` searches ('bakery', 'dark portfolio'); `slugs` (up to six) gives full detail to compare a shortlist. Read-only: create_site_from_template creates the site.

Return the full prop schema for a single primitive: types, defaults, options, required fields, allowed children, and `styleStrip` — a map of {style prop: actionable hint} for any inline-style props this primitive's renderer intentionally drops (e.g. on `section`, layout props like `display`/`flexDirection` are stripped because Section is a semantic wrapper; use a Stack/Grid child instead). Read styleStrip BEFORE setting style on a node — eliminates the silent-drop trial-and-error. Without `type` it lists every primitive.

Return example fixtures in the shape Sitelas expects — the first page's sections (up to 8), each collection's fields with one example item, and theme and template metadata: a Rosetta Stone for which fields are required vs optional, how responsiveStyle nests, what shape data.items/data.tiers/etc actually take in practice. Study one for shape; compose your own for content. The fastest path to a correct first call on an unfamiliar category is calling this once with that category, reading the skeleton, then composing from get_primitive_schema + design principles — not avoiding examples on principle. The goal is never to replicate the reference; it's to get the shape right on call #1.

Capture a public URL for cloning. Returns everything needed to build a faithful replica: (1) full-page screenshot for visual context — your vision can attend to specific regions; you don't need per-section crops; (2) recursive rendered DOM tree (up to 500 nodes, 10 deep) with computed styles per node — the fastest way to pick exact colors, font sizes, spacing, and layouts without guessing; (3) ordered section structure with detected content type (hero / grid / cta / gallery / text / …), headings, bounds, and layout tokens (flex/grid/padding/gap); (4) detected brand tokens — primary/background/text colors, heading + body fonts, all fonts, typographic scale, section paddings, gradients and hero background images; (5) real image URLs (reuse them via upload_image (with url) so the clone has actual imagery, not stock placeholders); (6) nav links + OG metadata (title, description, og:image). Usage: capture → study screenshot (you have vision) + element_tree → compose a fixture from primitives mirroring structure.sections[i] → call create_site_from_fixture (the bespoke-composition path). Rate-limited to 5 captures per minute. Read-only as far as your Sitelas account is concerned: it fetches the target page and returns what it found, and saves nothing. Rehosting any image it lists is upload_image (with url)'s job, not this one's.

Read what is on the site's DRAFT. view 'content' (default) lists every editable node id with its text, plus form fields, FAQ items, image nodes and collection items — call it when you come back to a site and before patch_nodes. view 'structure' gives the container nesting with ids (for moving, inserting or removing containers, and the only way to see how header/footer nodes nest). view 'copy' lists all rewriteable copy on the whole site for a full rewrite. To check what visitors see, use read_rendered_page; for pages, theme and publish state, get_site. Read-only.

THE DEFAULT WAY TO CHECK YOUR WORK. Fetch a page's SERVER-RENDERED text via the preview URL — the visible copy, headings, and (for collection/list pages) the expanded items — in ~35ms with no browser. Call it on every page you touched, before showing the user anything. It is what catches the failure you cannot otherwise see: template copy that survived your edit. A consultant template's funding-stage dropdown on a bike shop, a US Medicare FAQ on a UK dental practice — both shipped because nobody read the page back, and neither looks like an error in a diff. Prefer this over screenshot_page, which runs a real browser, takes ~40x longer, and answers a different question (how it LOOKS, not what it SAYS). Read-side pair to get_page (with view: "structure"): that shows STRUCTURE, this shows RENDERED CONTENT. Reads the draft via the preview URL. Read-only. `live: true` checks the published site instead (and its dead links); `item` renders one collection item's draft.

Capture a JPEG when the question is genuinely VISUAL — layout, spacing, hierarchy, whether the hero reads as designed, whether the mobile breakpoint collapses cleanly (pass viewport). Returns the page's `text` and `title` alongside the image, fetched concurrently at no extra wall-clock, so you get what the page SAYS as well as how it LOOKS from one call — READ that text, because leftover template copy is the most common way a site ships wrong and it is invisible in an image. Still runs a real browser at ~5s, so when the question is only about wording prefer read_rendered_page at ~357ms. Always captures via the preview URL (preview-{slug}.{rootDomain}), which renders the current draft (or 302s to live when in sync). Read-only.

Review the draft. Default focus 'health' finds what to fix before publishing — broken links, missing alt text and SEO titles, empty sections, and template content that survived (copy, form options, FAQ and collection items), and text visitors cannot read — it measures contrast on the rendered pages, which is the only check that catches light text left on a light card after a colour change. Run it after a build, a big edit or a set_theme colour change, and fix what it reports. focus 'design' returns design metrics for a self-critique; 'explain' explains the design (pass node_id for one node); 'next' suggests up to 3 next steps. Read-only: it reports and changes nothing.

Apply many node-level patches to a site in ONE call, batched into one new draft version. The primary patch primitive — there is no single-node tool. Each patch entry provides the node_id plus one or more shorthand fields: 'text' for copy (auto-routes to data.text for heading/button/link/nav-link/banner-bar, data.content for text/rich-text/blockquote), 'href' for link destinations, 'src'/'alt' for images, 'slot' to set or correct a node's semantic role. For props not covered by shorthands (level on heading, variant on button, items on accordion), use 'data_patch' — its keys are validated against the primitive schema (prevents silent failures where the renderer ignores your key). Patches are independent: valid ones are saved even if others fail, and each failure comes back in 'rejected' with the reason, so send a small follow-up call for those rather than resending everything. Typical post-create flow: read the manifest, build an array of {node_id, text} entries for every placeholder copy string, send ONE call. Order doesn't matter — patches merge independently. Each node_id may appear at most once per batch; merge multi-field patches for the same node into one entry. Node ids are searched in pages, then header/footer/pageLayout, then COLLECTION DETAIL TEMPLATES — so the labels and CTAs around a detail page's {{placeholders}} (get_page (with view: "content")'s collections[].template_nodes) are patchable like anything else; the response names those under collection_templates_touched, because one such patch changes every item page in that collection at once. Returns `dead_links` when the site is left with an internal link whose target route does not exist — check it before reporting the work done, because a card whose text is right and whose href is stale looks finished and 404s on click. A patch that changes a text colour, a background or a variant is followed by a contrast check of the rendered pages it touched (a few seconds), and text it left unreadable comes back in `unreadable_text` — fix it before you reply.

Add many nodes to a site in ONE call, batched into one new draft version. Each insertion is {parent_id, position, node}: parent_id locates an existing parent anywhere in the fixture (pages + siteElements), position is 'start' | 'end' | {after_id}, and node is the subtree to splice in. Use this for structural addition — adding a new section to a page, inserting a nav link, adding a card to a grid, restoring a container that was accidentally removed. To add one more of something that already exists (a menu item, card, team member, timetable column, link), send copy_of: <its id> instead of node — the copy matches exactly; then patch_nodes its text using the returned id_map. To show a collection's entries on ANOTHER page ('put my three latest projects on the About page'), copy_of the collection-list — or the grid holding it — from a page that already shows them, then set the copy's `limit`: a list stays in step with the collection, while hand-copied cards go stale the moment an entry changes. Safe alternative to patch_fixture's JSON-Patch 'add' ops: no hand-constructed paths, validated parent + unique-id checks, and the subtree goes through the same primitive schema as any other node. Use get_primitive_schema + get_primitive_schema to shape the new node's data. Tag each content node with its semantic role — "extra": { "slot": "hero-title" } — so the next read of get_page (with view: "content") can say what the node IS rather than reporting a null role; an unknown slot name is rejected with the closest valid ones, and untagged content comes back as a warning. If a parent or after_id is missing, or a new id collides, the whole batch is rejected. Up to 50 insertions per call. A node may also be sent as a JSON string (`node` as a string, or a single top-level `parent_id` + `node_json`).

Add a complete, hand-fixture-quality section to a page from a FLAT description — no node-tree authoring. Pick a `kind`, pass `page_slug` + the copy fields (eyebrow, heading, body, CTAs) and, for list-based kinds, an `items[]` array of flat cards/rows. The tool builds a correct section > container > stack > [...] subtree (theme tokens, Lucide icons, responsive grids, real spacing, every text/CTA independently editable) and splices it into the page, saved as one draft. Kinds & use-when: hero (page-opening headline+CTAs); features (icon/title/body card grid); content (long-form block); cta (centered band CTA); gallery (image grid); testimonials (quote cards); stats (count-up numbers); pricing (tier cards, highlight one); faq (Q&A list); logo-strip ('trusted by' logos); pull-quote (centered editorial quote); team-grid (people cards); split-image-text (image + copy split); process-steps (numbered steps); feature-split (copy + bullet list + image); newsletter (inline email signup band); comparison (with-us vs without); metrics-band (stats on accent band); banner-cta (full-width accent CTA); contact-form (copy + name/email/message form). Use this — not patch_fixture — to populate a page or add a section in-editor. For surgical edits to existing nodes use patch_nodes; for bespoke structure use insert_nodes (claude.ai only).

Delete many nodes from a site in ONE call, batched into one new draft version. Locate each node_id across pages, siteElements and collection detail templates, and splice it out of its parent's children array. Use this for structural subtraction — dropping a testimonials section, removing a nav link, cutting one card from a grid, removing a header/footer column — whenever the brief calls for the template's layout minus something. Safe alternative to patch_fixture's JSON-Patch 'remove' ops for the same purpose: no hand-constructed paths, validated against the actual tree, and rejects root-node removal so you can't nuke a whole page. If a node id isn't found, the whole batch is rejected (fix and retry). Up to 50 removals per call.

Move a node to a new position in its page/element tree — reorder it within its current parent (e.g. push a section to the top) or re-parent it under a different container in the SAME tree. This is the safe reordering primitive: it takes node ids (not brittle JSON-Patch index paths) and is validated against the actual tree. position is 'start' / 'end' / { after_id }. Rejects moving a root node, moving a node into itself or its own descendant, and cross-tree moves (use add_page (with copy_of) / insert_nodes for those). Writes one new draft version.

Apply an RFC 6902 JSON Patch (array of add/remove/replace ops) to the latest fixture snapshot for a site, then save the result as a draft version. Use this when you need precise multi-field edits across the fixture (theme tokens + a couple of nodes + a header swap) without shipping the full fixture again. Paths are JSON Pointers (e.g. '/theme/config/colors/primary' or '/pages/0/tree/children/2'). Returns the preview URL. Call screenshot_page if you want to see the result.

Add a new ONE-OFF page to an existing site (About, Contact, Pricing, a new landing page, etc.). Creates a bare shell (one section with one heading) and returns its root + section ids so you can fill it with insert_nodes / patch_nodes. Writes a new draft version. Pass `copy_of` with an existing page's slug to start from a copy of that page instead. DO NOT use add_page for repeating detail pages that share structure (`/projects/<slug>`, `/blog/<slug>`, `/work/<slug>`, `/publications/<slug>`, `/searches/<slug>`, etc.). Those resolve via the site's `collections[]` definition + Mustache substitution into a single shared template — they are NOT `pages[]` entries. To add an item to an existing collection, use `add_content_item` (with the matching `content_type`). To stand up a new collection on a site that doesn't yet have one, use `create_collection` (which composes the collections[] entry, an index page, and optional seed items in one call). add_page is wrong every time for collection detail pages: it bakes the row as a static tree, the index page's `<collection-list>` won't reflect it, and sitemap/SEO won't see it.

Update a page's metadata: rename its slug, change its title, toggle reorder (`sort_order`), or merge into its `metadata`. For a page's search title, description, share image or noindex, use `update_seo` with `page_slug`. At least one field must be passed. The page tree itself is NOT touched — use insert_nodes / patch_nodes for tree edits. To change the nav, edit the nav-link nodes directly (insert_nodes / patch_nodes / remove_nodes). Renames: the home page ('/') cannot be renamed (it'd break routing). new_slug must satisfy /^\/[a-z0-9/-]*$/, not collide with any existing page, and not shadow a reserved Sitelas route (`edit`). By default, every node whose `data.href` / `data.url` equals the old slug is rewritten to the new slug in the same draft write — so internal links stay valid after the rename. The response reports the count and the rewritten node ids. Pass `rewrite_links: false` to skip the auto-rewrite (only useful when the caller intends to surgically patch the hrefs themselves). Sub-pages move with the parent by default — pass `cascade: false` to leave them behind — and every moved URL 308-redirects from its old path once published, so existing links and search results survive. Reach for this whenever a page's slug no longer matches what the page became: adapting a template to a different line of work renames the visible labels but leaves the paths saying the old thing. Writes a new draft version; call publish to push live.

Delete a page from a site. Cannot delete the home page ('/'). By default also prunes nav-link entries (header / footer / pageLayout / page bodies) that point at the deleted slug so the deletion leaves no broken nav links — set prune_nav_links:false to keep them. A nav-link is kept (with a warning) if removing it would empty its nav-links container. Other links to the deleted page (plain links, buttons, cards) are not changed: they come back in `dead_links` with where each one is — re-point or remove them before you reply; that is part of deleting the page. The page also comes off the live site immediately (like Delete in the editor); nav-link changes stay in the draft until published. Returns the preview URL.

Set SEO metadata for a site or a specific page. Per-page mode (with `page_slug`) writes into the page's `metadata.seo` and ships as a new draft version — call `publish` to push live. Site-wide mode (no `page_slug`) writes into the site's `metadata.seo` and is live immediately, as defaults under each page's own values (see each field). Supported fields: title, description, og_image_url, robots (index|noindex), language (BCP 47 — a page's own for a translated page), and — site-wide only — business (schema.org type, address, telephone, email, logo, same_as for the home page's structured data; use it when the owner asks to appear as a local business in search) and verification (Google Search Console, Bing, Meta or Pinterest ownership tag). Omitted fields are kept; `null` removes one. `get_site` returns the current values, including each page's own.

Connect the owner's own Google Analytics 4, Google Tag Manager, Meta Pixel or Plausible to their published site. Takes an ID — or the snippet the owner pasted, from which the ID is extracted — never arbitrary script. Omitted providers are kept; `null` disconnects one. Live on every public page at once, no publish needed (never on preview links). Use only IDs the owner gave you. Sitelas has its own visitor analytics (get_analytics); this is for owners who want their numbers in those tools, or to measure ads. Where the owner's visitors are in the EU/UK, tell them these tools may need a cookie consent banner.

Make the draft live. With no page_slug or item it promotes the whole draft — the step that turns a draft into a real site; until it runs the live URL does not resolve. With page_slug it publishes one page; with item, one collection item. Publish ONLY on the owner's explicit yes after they have seen the preview — never auto-publish, and don't read 'change X' as 'change X and publish'. If it returns billing_gate, pass its message on as it is and ask the owner what to do; never unpublish another site to make room. Reversible with undo (what: 'last_publish'). The returned `url` is verified to render.

Take the whole site offline (its address 404s for visitors), or with page_slug just one page. Drafts are kept, so it can be published again. This changes what visitors see — only when the owner explicitly asks to take something down, and never to make room for another site's publish.

One-step undo. what: 'last_publish' reverts the most recent publish (restored=false when there is nothing to undo); what: 'draft' discards every unpublished change so the draft matches the live site (reset=false when the site was never published; needs confirm: true, so ask first). Neither touches the other: discarding the draft keeps the publish undo available.

List every content collection declared on a site, with its per-item field schema (`fields[]`). Schemas are per-site — there is no platform-wide catalog. Each entry includes the collection's `contentType` slug, its `pathPrefix`, whether it's enabled, and the typed `fields[]` (each with `key`, `type`, optional `required`, `label`, `description`, `options` for select fields), PLUS `observed_fields` — keys that actually appear on the collection's items but aren't in the declared schema (the schema is open, so include these when adding an item so it matches the existing ones). Call this before `add_content_item` to discover which fields a collection supports — or before `create_collection` to confirm a contentType isn't already taken. WHAT EACH `type` HOLDS, because the name does not say: text / textarea / rich-text (HTML) / url / date / select -> string; number -> number; boolean -> boolean; tags -> string[]; **image -> an object { "url": "…", "alt": "…" }, NOT a bare string** (a plain URL is accepted on write and stored as { url }; send `alt` when you have it). That last one is the one that gets guessed wrong.

Stand up a new collection on an existing site in one call: append a `collections[]` entry (with `contentType`, `pathPrefix`, `template`, and optional `items[]`), optionally create the index page that hosts the matching `<collection-list>`, and seed initial items into the `content_items` table. After this call: - `/{path_prefix}/{slug}` resolves each item via Mustache substitution - the index page's `<collection-list>` iterates the seeded items - `add_content_item` / `edit_content_item` / `delete_content_item` manage rows afterward. Use this when the user asks 'add a blog to my site', 'set up case studies', 'add a careers section', etc. Schemas are per-site — pick a `contentType` slug that's unique within the site (call list_content_types to see what's already declared) and ship the `fields[]` schema inline. Writes a new draft version; call publish to push live.

Delete a whole collection (a blog, projects, menu, team…): its schema, its listing page (the page at its path prefix) and EVERY item in it. Use this when the user wants a whole section of posts or entries gone. To remove one entry use delete_content_item; never remove a collection with patch_fixture, which leaves all its items behind. The items and the listing page come off the live site immediately, like Delete in the editor; the schema removal stays in the draft until publish. Links to the collection elsewhere (menu, footer, buttons) are not changed: they come back in `dead_links` with where each one is — re-point or remove them. Also cleans up items left behind by a collection that is no longer declared. Permanent.

List content items (e.g. blog posts, projects) for a site filtered by content_type slug. Optionally filter by visibility ('public' | 'private' | 'all'; default 'all'), with limit/offset paging. Returns each item's id, slug, full content JSON, visibility, and timestamps. Use add_content_item / edit_content_item / delete_content_item to mutate the collection.

Create a new content_items row for a site under a given `content_type` slug — this is the RIGHT tool for adding any repeating detail page (`/projects/<slug>`, `/blog/<slug>`, `/searches/<slug>`, `/publications/<slug>`, `/work/<slug>`, `/talks/<slug>`, `/teaching/<slug>`, etc.) when the site already declares a matching `collections[]` entry. The row is written to the site's DRAFT version, so it shows on the index page's `<collection-list>` and its detail URL `/<prefix>/<slug>` in PREVIEW right away (via the collection's template + Mustache substitution); it goes public on the next `publish`. DO NOT use `add_page` for detail pages of an existing collection — that creates a static page that the index won't reflect and sitemap won't auto-emit. IMAGES IN `content`: source them yourself with `find_images` or `upload_image (with url)` — that is the normal path and needs nothing from the person. An `image`-typed field holds an OBJECT: { "url": "https://…", "alt": "what is in the picture" }. A bare URL string is accepted and stored as { url }, but send `alt` when you can — it is the only description a screen reader gets, and the template may render it. Check the field's declared type with `list_content_types` before composing: a `url`-typed field takes the plain string instead. But if they want THEIR OWN picture on an item (their product shot, their photo of the venue), do not try to carry the file: create the item with the text, then give them `get_editor_link` with `page_slug` set to that item's path (e.g. 'blog/my-first-post') so they can drop the original straight onto it. `content` is validated against the content_type's JSON schema (lenient/open): `required` fields must be present and non-empty, declared fields must match their type, extra fields are allowed. Include every field the collection's `template` references — missing fields render as empty strings in the detail page. `visibility` defaults to 'public' (anonymous-readable). SLUG BEHAVIOR (idempotent): re-calling with the SAME slug + identical content is a safe no-op — it returns `success` with `already_existed:true` (no error), so a retried write after a dropped connection won't double-post. Re-using the same slug with DIFFERENT content is a real collision and throws ConflictError — pick another slug or use `edit_content_item` to change the existing row. If the site doesn't yet have a matching `collections[]` entry, add one first via `create_collection` (composes the collections[] entry + optional index page + items[] in one call). Each site defines its own schemas inline at `collections[i].fields` — there is no global content-type catalog; what shapes exist for a site is whatever the site's draft_fixture declares.

Update fields on an existing content item. Identify it by `item_id`, OR — when you only know its name — by `site_id` + `content_type` + (`slug` | `title`); `title` resolves fuzzily (exact then contains) and errors with the available titles when there's no unique match. `patch` may include `content` (shallow-merged into existing content JSON), `slug` (rename — the old URL 308-redirects to the new one once published), and/or `visibility`. At least one field is required. When `content` is patched, the MERGED result is validated against the content_type schema (lenient: required present + declared types; extra fields allowed) and rejected with paths if it would leave the item missing a required field. An `image`-typed field holds an OBJECT { "url": "…", "alt": "…" } — a bare URL string is accepted and stored as { url }. `content` is SHALLOW-merged, so patching an image field replaces the whole object: re-send `alt` alongside `url` or it is dropped. Ownership is verified via the parent site's user_id. Throws on slug collision.

Delete a content item by item_id, or by site_id + content_type + its exact slug. It also comes off the live site immediately. Ownership is verified via the parent site's user_id. Returns the deleted item's slug and content_type for confirmation.

Change how every card of a collection list looks, in one call: `design` swaps the whole card (a library design from set_card_design (with no design) — each takes the site's own fonts and colours and keeps every field the card shows — a plain arrangement, or 'as-designed' to put the original back), and `style` adjusts it (surface, corners, padding, photo shape, text alignment, hover). Prefer this to rewriting a card template with patch_nodes/insert_nodes (with parent_id and node_json): it cannot drop a field, break the card's link or lose the way back. Writes one draft version. With neither `design` nor `style` it changes nothing and returns the designs and style choices that fit this list's card, with its current style.

Change the site's colours and fonts in the draft — any subset; what you leave out is kept, and text that would become unreadable is repaired and reported. Also sets the brand name and tagline, re-applies a saved look (preset_name), and saves the result as a named look (save_as) for later. For one section's surface or one button, prefer that node's own data (section background, button variant) over the theme. After a colour change it measures the preview, and text that no longer reads on its background (accent text on a primary card, a button's own text colour) is given a theme colour that reads and listed in text_recoloured — tell the owner what changed. That check adds up to ~20s. Returns the preview URL.

Search a curated stock photography library (Pexels) for real imagery to use in a site. Call this BEFORE composing any section that needs imagery — hero, feature-grid with visuals, testimonial cards with portraits, gallery sections, media-and-text sections. Returns large-size URLs, thumbnails, dimensions, average color, and suggested attribution. Chain with upload_image (with url) to persist the chosen image to the site's storage namespace (recommended — external URLs can rot). Be specific with queries; 'brooklyn bakery sourdough on wooden counter' returns much better results than 'bakery'. Rate-limited to 30 searches per minute per user. Read-only: it queries Pexels and returns results. Nothing reaches the site until you call upload_image (with url) with a URL from them.

Replace many image nodes at once: for each {node_id, query} patch, search Pexels, upload the top match to the site's CDN, and patch the node's src + alt — all in ONE tool call, batched into ONE new draft version. Use this after create_site_from_template to swap every placeholder image at once instead of doing 2N sequential calls (find_images + upload_image (with url) per image). Build the patches array from the manifest's image_nodes (each entry has a suggested_query you can use directly, override when the brief calls for something more specific). Up to 20 images per call. Each patch's node_id must exist in the current fixture; missing nodes are reported as skipped (not fatal — other patches still apply).

Store an image on Sitelas's CDN (resized, WebP, blur placeholder) and return its URL — from `url` (the normal case), or `base64` for a small image you made yourself. Pass `image_target` to also put it on its spot in the same call; without it, place the URL with patch_nodes or edit_content_item. Local agents with the file on disk use mode: 'link', PUT the bytes, then call again with staging_path. When the PERSON has the photo on their device, this is the wrong tool at any size: call get_editor_link with image_target and give them the one-time photo link.

List the images in a site's library, newest first, with their public URLs. Two main uses: collecting what arrived after you sent someone to the editor with `get_editor_link` (pass `since` with the moment you sent it, then place the results with `patch_nodes`), and checking what a site already has before uploading something it may already hold. Returns storage paths and byte sizes alongside the URLs. Place what you find with `patch_nodes`, never `patch_fixture` or `replace_site`. If the image arrived because you sent someone a `get_editor_link`, they may still be in the editor right now; the full-replace tools overwrite the entire draft with no conflict check and would discard their work silently. `patch_nodes` re-applies onto concurrent edits instead.

Return a link to the site owner's own visual editor, optionally opening straight to one page or collection item. Give this to the person whenever the next step is theirs rather than yours: - They want to use THEIR OWN image. Pass `image_target` naming the spot and they get a one-time photo link instead of the editor: one page, one drop, placed on that spot in the draft, no sign-in. Without a spot they get the editor. This is the dividing line for every image on the site, sections and collection items alike: you source pictures with `find_images`, `replace_images_from_search` and `upload_image (with url)` and place them yourself, but a file that exists only on their device — their logo, their team photo, their product shots — comes in through the editor. Point them at the exact page or item (`page_slug` takes 'blog/my-post' as happily as 'about') rather than the site root. - They want to make a change themselves, or asked where to edit something. - They asked to see their site, or seem unaware there is a visual builder at all — many people who connect this connector never find out. The link opens their editor signed in as them. Do not promise they are already signed in: the session cookie may not exist on the device they open it from, in which case they are sent to sign in first and land in the editor afterwards. `share_with_user` is worded for that. The editor link is nothing secret — an ordinary product URL, authenticated by the person's own account. A photo link is a one-time capability for that one spot (30 minutes, one upload, draft only): give it to the person and to no one else.

Store a JSON or CSV data file for the site and get a public URL that an embed's script can fetch() — for a browser-side widget that needs a dataset (a product finder, a price table, a store locator). Never paste the data into the embed itself, and never hide it in an image. Sources, exactly one: url (a public link — published Google Sheet, Drive/Dropbox share link, any direct file), content (inline text up to 100,000 characters), or mode:"link" (the owner uploads the file in their browser). Up to 2MB stored. Same name replaces the file at the same URL. A CSV is stored as an array of row objects (as:"csv" keeps it as given). Returns the url, row count, the data's shape and a ready fetch line.

List form submissions for a site. Returns summaries (name, email, form_type, `source` — which page/form the submission came from, e.g. a specific job position — is_read, preview of message, and `attachments` — any uploaded files as {field, name, url} with DURABLE download links safe to paste into an exported Google Sheet / CSV) plus a `monthly_counter` block (used / limit / over_limit / locked / month / plan). The allowance is per calendar month; a month's locked submissions stay locked after it ends. Capture is NEVER gated on any plan: every submission is stored and forwarded to the site's webhook regardless of the count. Each one is also emailed to the site owner's account address — on by default, switched per site from the dashboard's Submissions panel; it can't go to a different address (for that, set the form's integration.webhookUrl). What the allowance controls is READING. On Free, submissions past a month's allowance come back with `locked: true` — created_at and form_type only, with name, email, description and attachments withheld; say plainly that the submission arrived and was kept. Do NOT imply anything was lost or dropped, and do not pitch a plan change on the back of it, and do not try to recover a locked submission through read_submission or the contact tools — they withhold it too. Pro never locks. Filter by form_type, unread_only, or since-timestamp. `form_type` is free-form text set by whoever built the form — the built-in presets are contact, waitlist, newsletter, rsvp, quote, application and custom, but templates ship their own (e.g. booking, tour-request, commission-enquiry), so read the form_type values off an unfiltered call before filtering on one. Use `read_submission` to fetch the full payload of one.

Fetch the full content of a form submission by id — name, email, message, every field value, and the form's schema at submission time. By default also marks the submission as read. Set mark_read=false to leave the is_read flag alone.

List contacts who have interacted with a site (filled a form, subscribed to newsletter). Filter by text, tags, or activity thresholds (min_submissions, subscribed_only, since). Ordered by most recent activity. Pass `email` for everything about one person: their record and their timeline ('tell me everything we know about Sarah').

Fetch pageview and unique-visitor analytics for a site over a date range. Returns totals, a daily timeseries, top paths, and top referrers. Default range is trailing 28 days ending today. Use `range_days` for a trailing window, or `since` + `until` for explicit bounds. Pass `include_activity: true` to add form submissions and new contacts for the same window ('how's my site doing?'). Analytics is a Pro feature: on Free it returns billing_gate — say so once and don't ask again.

Attach a custom hostname (e.g. `example.com`, `www.example.com`, `app.example.com`) to a Sitelas site. Registers the domain with the CDN, stores it against the site, and returns the EXACT DNS records the user needs to add at their registrar — apex domains get an A record (76.76.21.21) plus a www CNAME, subdomains get a single CNAME. Records are returned both structured (`dns_records`) and as a copy-pasteable human-readable list (`dns_instructions`). Relay `dns_instructions` and `next_step` verbatim. After DNS propagates (5–30 min typical), call `check_custom_domain (with domain)` to flip the verified flag. Custom domains are a Pro feature: on a Free site (list_sites and get_site give each site's `plan`) it returns billing_gate — say so once, before offering to connect one.

Without domain: list the custom domains attached to a site — hostname, whether DNS is verified, the verification token, when it was attached. With domain: re-check its DNS and mark it verified when the records resolve; on failure it returns the exact records expected (surface `expected_records` and `message` verbatim). A verified domain on a never-published site still 404s — say so and offer to publish. set_custom_domain attaches a domain; remove_custom_domain detaches one.

Detach a custom domain from a site (apex + www) and delete it from Sitelas's records — for a typo'd domain, a swap to another, or to stop serving a hostname (the *.sitelas.com address keeps working). Requires confirm: true. Afterwards the owner removes the DNS records at their registrar; Sitelas can't.

Set or incrementally update the durable site brief (vibe, category, brand voice, palette/font names, tagline, target audience) used as cross-session memory. Merges field-by-field into the existing brief — omitted fields are preserved, and the decisions[] log is left untouched. Call this once after picking a direction, then again whenever the direction shifts. Pass `decision` (and `why`) to append a design decision to the same memory.

Overview

What is Sitelas?

professionally designed template with your copy and photos.

How it works

  1. Describe it. "A one-page site for my bike repair shop, warm and hand-made, with a contact form."
  2. Preview. The agent sends a preview link. Nothing goes live until you approve.
  3. Publish. Your site goes live at yourname.sitelas.com, or your own domain on Pro.
  4. Keep running it from the chat. Rewrite pages, add or remove pages, swap photos, read new contact-form messages and draft replies, then publish the changes.

What you can ask for

  • "Rewrite every page in a warmer voice."
  • "Add a Pricing page and remove the Blog."
  • "Make the colours earthier and use a serif for headings."
  • "Show me this week's form messages."

What it builds

Marketing websites for small businesses, freelancers, creators and startups: local services, restaurants, portfolios, clinics, agencies, waitlists. It is not an app builder: no online store checkout, bookings or visitor log-ins.

Connect

Pricing

Free: one live site with Sitelas branding. Pro: $9 a month or $90 a year per site, with a custom domain, no branding and analytics.

Docs: https://sitelas.com/docs/agents · Templates: https://sitelas.com/templates

Frequently asked questions

1

What is the Sitelas remote MCP server?

The Sitelas remote MCP server is a hosted Model Context Protocol endpoint at https://sitelas.com/api/mcp, so AI assistants can connect to it without installing or running anything locally.

2

How do I connect to the Sitelas MCP server?

Add the endpoint https://sitelas.com/api/mcp to any MCP-compatible client such as Claude Code, Cursor, or VS Code. The setup snippets on this page configure each client in one step.

3

Does the Sitelas MCP server require authentication?

Yes. Sitelas uses OAuth: the first time you connect, your MCP client opens a browser window to sign in and authorize access, then reuses the credentials for future sessions.

4

Which transport does the Sitelas MCP server use?

Sitelas exposes a Streamable HTTP endpoint, the transport used by remote MCP servers and supported by all major MCP clients.

Comments