SuperAPI AI Directory

stacktree-publish

General & other

View repository

Updated 10-16 04:28

About this skill

stacktree-publish

Publish an HTML artifact to stacktr.ee and return the URL into the conversation.

When to invoke

  • User asks to publish, share, host, or drop an HTML / Markdown artifact you just generated.
  • User wants a link to a hosted version of a page they just saw (e.g. a dashboard, mock, table).
  • You just produced an HTML artifact and the user will benefit from a browser view.

Do not use this for code that isn't a complete static page (e.g. fragments, JSX components without a host page). Wrap the fragment in a minimal HTML shell first.

How to publish

This skill is installed alongside the stacktree MCP server by npx stacktree-install. That means you already have stacktree tools available — call them directly, no shell scripts required.

The tools you will use most:

Tool Purpose
publish_html Upload HTML. Returns { url, id, expires_at, ... }.
update_site Replace HTML in place — URL stays stable across revisions.
set_password Add or clear a passcode gate. Paid plans only.
set_email_gate Restrict viewers to a specific email domain. Paid plans only.
set_expiry Set hours-from-now expiry, or null for never.
set_agentation Toggle the on-page Agentation feedback toolbar.
list_sites List sites owned by this API key.
list_client_spaces List the client spaces pages are filed under.
set_client File an existing page under a client, or detach it.
create_client_space Create a client space up front (publishing auto-creates one anyway).
update_client_space Rename, archive/unarchive, or gate a whole client space.
delete_client_space Delete a space; its pages detach and keep working.
delete_site Hard delete a site.

Steps

  1. Make sure the artifact is a complete HTML document (<!doctype html>...</html>). If you only have a body fragment or markdown, wrap it in a minimal HTML shell first.
  2. Call publish_html with the HTML content as the content argument. Optional arguments worth knowing:
    • password — passcode gate, paid plans only
    • expires_in_hours — number, or 'never' (clamped to the plan ceiling)
    • agentation: true — enables the on-page feedback toolbar
    • public_slug — opt into {slug}.stacktr.ee/ (otherwise unlisted)
    • pii_check: 'off' | 'warn' | 'block' — default block from MCP
    • client — file the page under a client space (see "Client spaces" below)
  3. The tool returns a JSON object including url and expires_at. Surface the URL inline in your reply, plus when the link expires and any PII warnings.
  4. If the user iterates on the same artifact later in the session, call update_site instead of publish_html — pass the previous id or unlisted_token so the URL stays stable across revisions.

Examples

User: "Publish this dashboard." → Call publish_html with the HTML, reply with the returned URL inline.

User: "Update the same one — gate it to @yourco.com." → Call update_site with the existing slug and new HTML, then set_email_gate with the domain.

User: "Make it expire in 24h." → Call set_expiry with expires_in_hours: 24.

Client spaces

When the user names a client, customer, or project the page is for ("publish this for Acme"), pass client on publish_html — the space is auto-created, no setup call needed. For a client that already exists, reuse the exact spelling from list_client_spaces so "Acme Co" and "acme" don't fork into two spaces. To file or detach a page that is already published, call set_client (client: null detaches). A page without a client is a normal floating page — don't invent a client the user didn't name.

A space can carry its own address (acme.theiragency.com) and a generated client portal — an index of everything delivered, newest first, served at that address's root. Both are set up in the dashboard (DNS is involved). What matters to you: when a space has an address, the publish response includes client_url — the page's link on the client's own domain. Prefer handing client_url to the user over the stacktr.ee link; it's the address their client bookmarks. The portal rebuilds itself on every publish into the space, so filing a page is all it takes to appear there.

Managing the spaces themselves is a separate, rarely needed set: create_client_space sets a client up before any work ships, update_client_space renames one, archives or unarchives it, and sets the password / allowed_email_domain gate that covers every page in the space, and delete_client_space removes it. When a client is simply finished, archive rather than delete: archiving keeps the pages, the portal and the address serving while freeing the plan slot, and it is reversible. Deleting never deletes pages either — they detach to floating pages on their existing URLs — but the portal and the address stop resolving.

If update_site returns 409 managed_portal, the page is that generated portal: it regenerates from its space, so direct edits would be overwritten. Don't retry — tell the user they can "customize" the portal from the space's settings in the dashboard, which stops regeneration and makes it an ordinary editable page.

Privacy

Every URL is unlisted by default (stacktr.ee/p/{22-char-token}/) and not crawlable. Pass public_slug only when the user wants a discoverable URL.

If the artifact contains values that look like API keys, emails, SSNs, or credit cards, the response surfaces a PII warning. Pass it through to the user before sharing the link.

Every served page also carries a strict CSP and X-Robots-Tag: noai, noimageai, noindex, so a published page is not indexed and is marked off-limits for training. If the user asks for permanence, a public slug, or relaxed PII checking, surface the option rather than quietly disabling a default.

Expiry and plan limits

Expiry defaults are plan-aware: omitted on a paid plan means permanent; on the free plan every page caps at 7 days — expires_in_hours: 'never' is clamped down to the ceiling rather than refused. Read expires_at off the response and tell the user when the link dies. Do not tell them it is permanent just because you asked for permanent.

The free plan allows 3 pages in total. The count is lifetime, so deleting a page does not free the slot. Past the third, publish_html returns HTTP 402 with error: 'plan_lifetime_limit_exceeded'; set_password and set_email_gate return plan_password_not_available and plan_viewer_gate_not_available on a free key. Report the limit plainly and stop. Do not retry, and do not work around it by republishing anonymously.

Reaching for update_site on an existing page instead of publishing a new one is also the cheaper move: a replace keeps the URL and does not count as a new publish.

Making a page look better

When the user asks to improve, polish, redesign, or "make beautiful" a published page, call get_design_guide FIRST and follow its workflow exactly. The short version: assess before restyling (a page that already has a deliberate design gets elevated in its own voice or left alone — never flattened to a house look), keep every fact/row/link intact, respect the CSP (no external fonts under strict CSP — use system stacks), then update_site in place so the shared link keeps working. Tell the user the direction you chose and that all content survived.

Handing over a design canvas

Claude Code's /design command leaves a directory behind: one <Name>.dc.html per artboard plus a canvas.json holding the layout, the titles and the sticky notes. That is the design and the argument for it, which is what a client is actually shown — but the artboards are not publishable as they stand. They load a ./support.js that has no on-disk existence, so opened raw they render with literal {{ }} holes and one row per loop.

Render them first:

node scripts/design-to-stacktree.mjs <canvas-dir> --out review.html \
  --title "Client portal directions" --for "Meridian Group"

It runs each artboard's renderVals(), expands the loops and interpolation, scopes the CSS so the artboards share one document, and writes a single static page: directions in canvas order, each under its title with its canvas note beside it as the rationale. Then publish_html that file, passing client so it lands in the client's space and comes back with a client_url on the agency's own address.

Do not publish the seeded canvas file itself. It carries a couple of megabytes of editor code, and Save inside it only works on claude.ai, so off-platform it is a viewer that looks like an editor.

Getting a key

Normally npx stacktree-install mints a key and writes it into the MCP config, so there is nothing to do. The rest of this section is for the cases where that has not happened: a bare environment, or an agent running with no human.

STACKTREE_API_KEY=stk_live_...            # generate at https://app.stacktr.ee/api-keys
STACKTREE_API_URL=https://api.stacktr.ee  # optional override

If STACKTREE_API_KEY is not set and a human is available, ask them to set it before retrying. The two sections below are for when nobody is.

No key and no human? Pay for one

A funded wallet can buy hosting outright, no dashboard and no sign-up.

One page, no key. POST the HTML to https://agents.stacktr.ee/api/publish. You get a 402; pay $0.50 and the page publishes to a private link returned in the response. Nothing to provision. The paying wallet is recorded with the page, so a human can claim it from the dashboard later, or the agent can self-link it with the link_wallet tool.

Repeat use, so buy a key. POST https://api.stacktr.ee/provision, pay the 402 for $1.00, and get back a persistent stk_live_ key. Set it as STACKTREE_API_KEY and carry on. Buy capability the same way with POST /unlock?feature=…: custom_domain is $5.00 per 30 days, higher_limits is $25.00 per 30 days.

Both endpoints settle over x402 (USDC on Base, EIP-3009 — you sign an authorization, you do not pay gas) or MPP, which settles over that same rail; Stripe Shared Payment Token is accepted where that rail is enabled. GET /provision returns the current rail list with a live flag on each — read it rather than assuming, and do not offer a rail it reports as not live. Full flow at https://stacktr.ee/x402.

For a one-off, the front door is simpler. Provision a key when you will publish more than twice. When there is a human, the dashboard key is simplest of all.

No wallet, but your human is at the terminal? Show a pay QR

The fastest path when a paid action comes up mid-task:

  1. POST https://api.stacktr.ee/pay/sessions with { "feature": "provision" } (no auth needed) or { "feature": "custom_domain" } etc. with the API key.
  2. The response carries a qr field ready to print in a terminal, plus a short url and the amount. Print both, say what it costs and why.
  3. The human scans with their phone and pays by card.
  4. Poll poll.url at poll.interval_seconds (3s). On provision the poll returns the API key once — set it as STACKTREE_API_KEY. On an unlock it confirms the feature is active. Then continue without prompting again.
  5. Prefer one scan over several: pass "amount_minor": 1000 to charge $10 and leave the balance prepaid. Later paid actions draw from it silently, so no more QRs until it runs out.

Fallback (no MCP server)

If for some reason the stacktree MCP server isn't available in this session — you don't see publish_html in your tool list — the skill includes a shell-script fallback at scripts/publish.sh. It reads STACKTREE_API_KEY from the environment and POSTs to the public REST API. Use it only when MCP isn't an option; the MCP path is preferred.

It takes HTML on stdin and accepts --client / --client-path (file the page under a client space, same as the client argument to publish_html), --password, --expires-in-hours / --expires-never, --public-slug, --pii-check, --burn-after-read, --agentation, and --update <id> to replace a page in place.