Pembangun
// build guide

Build Guide.

Digylearn — Build & Publish Guide (MCP)

This guide walks through building and publishing a complete site using the Digylearn MCP tools. All tools run authenticated as the site owner; tenant/site resolution happens automatically from your account.

Typical build flow

digy_create_site  ->  digy_write_page_code  ->  (digy_create_collection)  ->  digy_publish_site

digy_write_page_code is the recommended default for authoring pages (full HTML + CSS + JavaScript, code mode). Use digy_update_page only for simple static content. You can repeat page-authoring, digy_create_collection, digy_create_record, and asset tools as needed before publishing. A site only becomes publicly reachable after digy_publish_site.

1. Create a site

digy_create_site({ "name": "My Portfolio", "slug": "my-portfolio" })

The slug must be unique within your workspace and alpha_dash (letters, numbers, dashes). The site starts unpublished.

2. Write pages

There are two ways to author a page. write_page_code (code mode) is the recommended default for building a real site — it supports full HTML + CSS + JavaScript and is not subject to the sanitized-mode restrictions. Reserve update_page (sanitized mode) for simple content-only pages where you are certain no JavaScript, class-based CSS, or forms are needed.

Why prefer code mode? Sanitized mode strips <script>, <style>, <form>, and on* handlers on render, so any page that later turns out to need interactivity must be rebuilt from scratch — wasting time and context. Starting in code mode avoids that rework.

2a. Interactive pages — write_page_code (code mode) — recommended default

For pages that need client-side logic — carts, tabs, modals, forms, quantity steppers, dynamic totals, generated deep links — use write_page_code. It stores separate HTML, CSS, and JavaScript fields and renders them as-is behind a strict Content-Security-Policy. Scripts, styles, forms, and event handlers all work. Use this as your default unless the page is purely static.

digy_write_page_code({
  "site_slug": "my-portfolio",
  "page_slug": "menu",
  "title": "Menu",
  "html": "<div id=\"app\"><button id=\"btn\">Add to cart</button></div>",
  "css": "#app { font-family: sans-serif; } #btn { background: #10b981; color: white; }",
  "javascript": "document.getElementById('btn').addEventListener('click', () => { alert('Added!'); });"
})

write_page_code is upsert (creates the page if missing, otherwise overwrites) and publishes immediately — the page is live in code mode the moment the tool returns. Use get_page to read back the source for idempotent re-edits.

Content-Security-Policy (code mode). Code-mode pages run under a strict CSP. Plan around these constraints:

Directive Allows Blocks
script-src inline JS (<script>, onclick, etc.), /js/page-sdk.js, and external scripts from cdn.jsdelivr.net, cdnjs.cloudflare.com, unpkg.com, esm.sh, code.jquery.com, cdn.tailwindcss.com all other external script sources
style-src inline <style> and inline style=""; external CSS from any https:// URL non-https CSS
img-src https:, data:, blob: (uploaded asset URLs work) non-https images
connect-src same-origin only (the Page SDK API works) external fetch/XHR to other domains
form-action same-origin only cross-origin form POSTs (use JS navigation instead)

WhatsApp / external handoffs: opening a WhatsApp deep link is a navigation, not a fetch — it is allowed. Build the link in JavaScript and trigger it with location.href = 'https://wa.me/…?text=…' or an <a href> click. Do not fetch() external APIs (blocked by connect-src).

Size limits (per page):

Field Limit
html 1 MB
css 1 MB
javascript 5 MB
head 200 KB
Combined 10 MB

<head> markup rules. The optional head field may contain only title, meta, style, link, and script elements:

  • on* event-handler attributes are rejected.
  • <meta http-equiv> and <meta name="referrer"> are rejected (platform-managed).
  • <link rel> must be one of stylesheet, icon, preconnect, preload, with an https:// href.
  • Head <script> must have an src from an approved CDN (cdn.jsdelivr.net, cdnjs.cloudflare.com, unpkg.com, esm.sh, code.jquery.com, cdn.tailwindcss.com) and no inline content — put your custom code in the javascript field, not in head.

Caching & publishing. Published pages are cached server-side (HTTP ETag/304 + a 24-hour file cache) to keep large game pages fast under load. Republish a page to see changes immediately — any publish or draft save rotates the cache key automatically. Previews are always fresh (Cache-Control: no-store). For HTML5 games, minify and bundle your JS before publishing so the initial render stays quick.

2b. Simple static pages — update_page (sanitized mode)

For content-only pages (text, images, links) where you are certain no JavaScript, class-based CSS, or forms are needed, update_page is lighter (a single content field). The HTML is sanitized on render: scripts, styles, forms, and event handlers are stripped for safety.

digy_update_page({
  "site_slug": "my-portfolio",
  "page_slug": "home",
  "title": "Home",
  "content": "<h1>Hello world</h1><p>Welcome to my site.</p>"
})

Sanitized mode keeps: all standard content tags (<p>, <h1>–<h6>, <a href="https://…">, <img src="https://…">, <ul>, <table>, <div>, <span>, etc.), and inline style="" attributes.

Sanitized mode strips (removed on render):

Removed Why
<script> No JavaScript
<style> No class-based CSS (inline style="" still works)
<form>, <input>, <button>, <textarea>, <select>, <option> No forms
<iframe>, <object>, <embed>, <applet> No embedded frames/plugins
<link>, <meta>, <base> No document-level markup
on* attributes (onclick, onload, …) No event handlers
javascript:, data:, vbscript: URL schemes No script URLs

If a page might ever need interactivity, start with write_page_code (2a) to avoid rebuilding it later.

Reading & deleting pages

  • Use digy_get_page to read a page back. The response includes render_mode ("sanitized" or "code"); code-mode pages also return html, css, javascript, and head so you can re-edit idempotently.
  • Use digy_delete_page to remove a page.
  • Use digy_get_site / digy_list_sites to inspect your sites.

3. Add data collections (optional)

Collections are like database tables for structured data (e.g. products, testimonials, form submissions).

digy_create_collection({
  "site_slug": "my-portfolio",
  "name": "Testimonials",
  "slug": "testimonials",
  "access_rules": { "read": "public", "write": "public" }
})

access_rules controls the public Page SDK API (see digy_get_sdk_reference):

Key public authenticated private
read anonymous OK logged-in visitors only never via public API (owner only)
write anyone can submit logged-in visitors only never via public API (owner only)

Default when omitted: { "read": "public", "write": "private" }. Change rules later with digy_update_collection_rules.

Manage records as the owner with digy_create_record, digy_list_records, digy_update_record, and digy_delete_record. Record updates support a mode of "merge" (default — shallow-merge fields) or "replace" (overwrite the whole data object).

MCP vs browser SDK — record capabilities

Both layers can now read, create, update, and delete records. The browser SDK enforces the collection access_rules on every call; the MCP tools run as the owner, so they bypass access_rules and can mutate any collection.

Operation MCP tool (digy_*) Browser SDK (PageSDK.records.*) Access enforced by
List/read list_records records.list SDK: read rule · MCP: owner (any)
Create create_record records.create SDK: write rule · MCP: owner (any)
Update update_record records.update SDK: write rule · MCP: owner (any)
Delete delete_record records.delete SDK: write rule · MCP: owner (any)

Public deletes via the SDK are hard deletes (no trash/undo). Restore and soft-delete are owner-only.

Reserved page slugs

These slugs are platform-reserved and cannot be used for pages (or sites) — they collide with platform routes:

api  admin  dashboard  assets  storage  login  register  logout  mcp

Pick descriptive alternatives (e.g. sign-in, media, control-panel, data-api).

4. (Optional) Enable visitor login

digy_configure_site_auth({ "site_slug": "my-portfolio", "enabled": true, "auth_methods": ["email"] })

Then create visitor accounts with digy_create_end_user (requires the end_users plan feature). Visitor login is handled by the browser SDK (PageSDK.auth.login) on the published site — it never exposes tokens to JavaScript.

5. Publish the site

digy_publish_site({ "site_slug": "my-portfolio" })

Publishing charges hosting credits (one month upfront, renewable every 30 days). The site becomes live at:

https://{subdomain}.{root_domain}/{site_slug}
https://{subdomain}.{root_domain}/{site_slug}/{page_slug}
  • Insufficient credits? Publishing fails with the required/balance amounts — top up via the dashboard.
  • Already live? digy_publish_site is a safe no-op.
  • Take it offline with digy_unpublish_site (credits are not refunded).

Interactive patterns (code mode)

These require write_page_code — they are not possible with update_page (sanitized mode strips scripts, styles, and forms).

Images

Upload images with digy_upload_asset (returns a public url), then reference them in either mode:

<img src="https://{subdomain}.{root_domain}/storage/tenants/{tenant}/{site}/assets/photo.jpg" alt="Menu item">

Asset URLs survive sanitized mode (only javascript:/data: schemes are stripped — https:// is fine). In code mode all of https:, data:, and blob: are CSP-allowed for images.

To use an AI-generated image (e.g. from ChatGPT/DALL-E) or a stock photo, call digy_import_asset_from_url with the public HTTPS URL — the image is downloaded and copied permanently into your site storage. Import promptly after generation, since some AI image URLs expire. The returned url can then be referenced in <img src> exactly like an uploaded asset.

Cart / quantity steppers / live totals

Store cart state in a plain JS object, render it with class-based CSS, and update the DOM on every change. Example skeleton:

const cart = {}; // { "item-id": qty }

function addItem(id, name, price) {
  cart[id] = (cart[id] || 0) + 1;
  render();
}
function removeItem(id) {
  if (cart[id]) { cart[id]--; if (cart[id] <= 0) delete cart[id]; }
  render();
}
function total() {
  return Object.entries(cart).reduce((sum, [id, qty]) => sum + priceOf(id) * qty, 0);
}
function render() {
  // update DOM: cart count badge, line items, total
}

Tabs / show-hide sections

Use buttons that toggle a CSS class (hidden / active) on section containers — no navigation needed.

Modal checkout

A hidden <div> overlay shown via a class toggle; collect table number / name / phone in <input> fields (allowed in code mode), then build the order message.

Send order to WhatsApp

WhatsApp deep links open by navigation (CSP-allowed), not fetch. Build the text and redirect:

function sendToWhatsApp(phone, text) {
  const url = 'https://wa.me/' + phone + '?text=' + encodeURIComponent(text);
  window.location.href = url; // navigation — allowed under CSP
}

Persist orders (optional)

To also save the order server-side, create a collection with write: "public" (or authenticated) and submit via the Page SDK:

await PageSDK.records.create('orders', {
  table: 5, name: 'Ali', items: cart, total: total()
});

This is a same-origin fetch — allowed under connect-src 'self'.

URL structure

{subdomain}.{root_domain}/{site}              -> serves the "home" page (or first page)
{subdomain}.{root_domain}/{site}/{page}       -> serves a specific page
/api/public/sites/{site}/...                  -> Page SDK JSON API (same origin)

{subdomain} is your workspace subdomain (set at registration). {root_domain} is the platform root domain.

Credits & publish billing

  • Publishing debits hosting_credits_per_month (default 5) credits and starts a 30-day hosting window (hosting_charged_until).
  • Renewal: a scheduled job re-charges on/after the due date. Insufficient balance suspends the site (suspended_for_payment = true, published = false); a successful renewal republishes it.
  • Unpublishing takes the site offline immediately but does not refund credits and does not reset the hosting window.
  • Track balance with digy_get_credit_balance.

Plan limits

Limits and feature flags are configured by the platform admin and gated across both the dashboard and MCP:

  • pages — max pages per site (digy_update_page/create blocked past the limit).
  • total_entries — combined cap on records + end users per site (digy_create_record/digy_create_end_user blocked past the limit).
  • storage_mb — total asset bytes per site.
  • Features: collections, end_users, media_library, analytics, csv_export — toggling a feature off blocks the related tools.

Use digy_get_site / digy_get_site_analytics to inspect a site, and digy_get_build_guide / digy_get_sdk_reference to retrieve these docs at runtime.

Disampaikan daripada sumber yang sama dengan tool MCP <code class="text-lime">digy_get_build guide</code>.

Kembali ke gambaran keseluruhan →