stacktree-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| STACKTREE_API_KEY | Yes | Your stacktr.ee API key |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| publish_htmlA | Turn HTML into a link a person can open in a browser. Reach for this whenever a page you produced has to be seen by someone who is not in this conversation — a client, a teammate, a reviewer, a channel — or when the user says "publish this", "share this", "send it to X", or asks for a preview link to something you built. A rendered preview inside a tool is not a link: if it has to travel, publish it. Returns { url, id, expires_at, ... }; hand the url back. Privacy-first defaults: unlisted token URL, plan-aware expiry (permanent on paid plans, 7 days on free — its ceiling), PII scan in block mode, strict CSP, X-Robots-Tag: noai. Pass expires_in_hours: "never" for a permanent link, which paid plans honour and a capped plan REFUSES with 409 expiry_clamped rather than silently shortening: either resend with accept_clamp: true to take the ceiling, or tell the user the plan cannot make a link permanent, and never report a page as permanent unless expires_at is null. Read expires_at_iso off the response and say that date. Retrying? Pass idempotency_key (any unique string) and a retry returns the SAME page instead of publishing a second one. pii_check: "warn" publishes despite detected sensitive data. A free key allows 3 pages in total and deleting one does not free the slot, so past the third this returns 402 plan_lifetime_limit_exceeded; report that to the user rather than retrying. |
| update_siteA | Replace the HTML of an existing site in place. The URL stays the same, so everyone you already sent it to sees the new version without being sent anything. Use this — not another publish_html — every time you revise a page you have already published: iterating with publish_html strands the link people are holding on the old version and burns a page slot. The PII pre-flight scan runs on the new content too (MCP default: block; pass pii_check: "warn" to override). A 409 managed_portal means this page is a generated client portal: it rebuilds itself from its space, so direct edits would be overwritten — the owner can "customize" it from the space settings in the dashboard, which stops regeneration for good. A 409 site_deleted means the page has stopped serving and is being kept for 30 days: call restore_site on the same id first, then update it. Do not publish it again, which mints a second page at a different URL. |
| delete_siteA | Take a page down. The link goes dead immediately for everyone holding it, and the content is kept for 30 days: restore_site puts it back at the same URL, with the same id, token, slug and read history, any time in that window. After the 30 days the content is destroyed and cannot be recovered by anyone. So this is undoable, but it is not a preview: tell the user their link stops working now. On the free plan deleting does not hand back a lifetime page slot, because that cap counts publishes rather than live pages. Deleting a page that is already down is not an error: the response comes back with already_deleted: true and the same restorable_until. Read restorable_until (unix seconds) off the response before promising a restore: it is absent when the delete was not recoverable. |
| restore_siteA | Put a page back at the same URL after it was deleted or ran out of time. Same id, same token, same slug, same read history, so every link already sent starts working again. Call this, and never publish_html, whenever update_site, set_expiry, set_password or another settings call answers 409 site_deleted: publishing the page again mints a different URL, strands everyone holding the old one, and spends another of the free plan's three lifetime pages, while a restore spends none of them. Works only inside the 30 days after the page stopped serving, and only for a page its owner deleted or one that expired. A page taken down for abuse is not restorable, and neither is one past its window: both answer 404 with the same body, deliberately. IT IS A RESCUE, NOT A RENEWAL. Read expires_at and restored_for off the response and tell the user that date: restored_for "grace" means the page had run out of time and comes back for 48 hours rather than a fresh full lifetime, while "plan" means it got the normal window for the plan. A 402 plan_site_limit_exceeded means the account is at its live-page limit: take another page down or upgrade, and this one stays restorable until its deadline either way. If a retry answers 404, do not assume the page is gone: call get_site first, because the earlier attempt may have succeeded and a restored page is no longer restorable. |
| claim_siteA | Adopt a page that was published without an account into the account this key belongs to. Pass the claim_token from the anonymous publish response. Do this whenever you published anonymously and a key is now available: an unclaimed free page dies at 24 hours, and claiming gives it the longest life the plan allows, never a shorter one, with the same URL, id, token and read history. A claim IS a publish: on a free key it spends one of the three lifetime pages and can return 402 plan_lifetime_limit_exceeded or 429 plan_site_limit_exceeded, and the claim link stays valid until the page expires, so report the wall rather than retrying. A wallet-paid page is the exception: it spends no quota and keeps the permanence it paid for. Claiming SPENDS the claim_token — it stops being the page's keyless update credential and update_site takes over. Re-claiming a page this account owns answers already: true; 409 already_claimed, 403 invalid_claim_token and 410 are all final. |
| get_contentA | Read a page's content back. format "html" (the default) returns the exact stored index.html, byte for byte, which is the only form you can edit and hand to update_site; format "text" returns the same page stripped to plain text — no markup, CSS, scripts or SVG — for when you only need to read, summarise or quote it and the markup would be most of the tokens. Only pages this key owns, gated ones included. A page that expired or was deleted still reads back until its restore window closes; one taken down for abuse does not. Never edit the text form and publish it: that throws the page's design away. |
| get_meA | Who this key publishes as, and what its plan actually allows: plan, signed-in email, pages held, lifetime publishes spent, and a limits object. READ limits RATHER THAN QUOTING NUMBERS FROM ANY OTHER TOOL DESCRIPTION — those describe the free plan and this key may be on any plan. Worth calling before promising a permanent link (max_expiry_hours), before promising a passcode or email gate (passwords, email_gates), and after a 402 or 429 so you can say which wall was hit. The email also answers "where did my page go": pages live in one account, and the commonest cause of a missing page is a second one. |
| set_passwordA | Set or clear (null) a passcode on a site. Works on every plan, free keys included. Clearing one always works. |
| create_share_linkA | Mint a share link addressed to one person. Put their name in label and every open through that link comes back attributed to it, in the dashboard and in the read-receipt email. The link skips the page passcode and can be revoked on its own. One per recipient after publish_html; for a whole mail-merge append ?to={{name}} to the page URL instead. |
| list_share_linksA | Share links on a page with attributed opens and last-opened time. opens counts human page opens through that link; use_count is the raw max_uses counter, not a read metric. opens is null on a plan without viewer numbers. |
| revoke_share_linkA | Kill one share link. The page and every other link keep working — cut off one recipient without re-issuing to the rest. |
| set_expiryA | Set hours-from-now expiry, or null for never. A NUMBER above the plan ceiling is shortened to it (free: 168 hours) and the response says so. null on a plan that caps page lifetime is REFUSED — 409 expiry_clamped, nothing changed — rather than quietly becoming 7 days, so a shortened page can never be reported as permanent: either resend with accept_clamp: true to take the ceiling, or tell the user the plan cannot make this link permanent. Quote expires_at_iso from the response, never the value you asked for. |
| set_agentationA | Toggle the on-page Agentation feedback toolbar. When on, viewer annotations are collected — read them with list_feedback, fix the page with update_site, then resolve_feedback. |
| set_email_gateA | Restrict viewer access to a specific email domain (strict-equal match). Viewers must verify via a one-time magic link. Mutually exclusive with set_password. Pass domain: null to clear. Email gates are a paid feature: on a free key, setting one returns 402 plan_viewer_gate_not_available. |
| list_sitesA | List sites owned by this API key. Viewer numbers are plan-gated: when metrics_locked is true, view_count, unique_viewers and last_viewed_at are null and only the boolean opened is meaningful. Report "someone opened this" in that case; do not guess or infer a count. Each site carries its client space (or null); filter with the client argument. Pages that expired or were deleted stay in this list rather than disappearing: check deleted_at before handing anyone a url, because a row with deleted_at set is a dead link, and restore_site puts it back until restorable_until passes. delete_reason says which it was: "expired", "owner", or "abuse", and an abuse takedown is never restorable, so do not offer to put one back. Those three fields are absent rather than null against a worker that predates them, so treat a missing deleted_at as live. Paged: when has_more is true, call again passing next_before and next_before_id as before and before_id, and repeat until has_more is false. |
| list_client_spacesA | List the client spaces on this account: slug, name, page_count, last activity, hostname (the space’s own address, e.g. acme.theiragency.com, when one is connected) and portal_enabled (whether the space serves a generated client portal at that address). Spaces group published pages per client ("file this under Acme"). publish_html auto-creates spaces, so an empty list just means nothing has been filed yet — publish with a client to start one. Connecting an address and enabling the portal are done in the dashboard (DNS is involved). |
| get_design_guideA | Fetch the Stacktree house design guide for improving a published page. Call this BEFORE any request to make a page look better, more polished, more professional, or "beautiful" — it contains the assess-first workflow (including when NOT to restyle a page that already has a deliberate design), the quality floor, and the CSP constraints published pages run under. Then follow it: get_site → assess → rebuild or elevate → update_site. |
| set_clientA | File an existing site under a client space (by name or slug, auto-created), or pass client: null to detach it to a floating page. Mirrors set_password: one site, one call. |
| create_client_spaceA | Create a client space before anything is published into it. Rarely needed: publish_html with a client argument auto-creates the space under the same casing and slug rules, so use this only when the user is setting a client up ahead of the work. Idempotent with that auto-create — an existing space matching the name or slug, in any casing, comes back instead of a duplicate. The returned slug is the space’s permanent address segment; a later rename changes the display name only. |
| update_client_spaceA | Rename a client space, archive or unarchive it, or set the viewer gate that covers every page in the space. Omit a field to leave it untouched. A rename changes the display name only — the slug is a permanent addressing contract and never moves. Archiving keeps everything serving (pages, portal, connected address) and frees the space’s plan slot; unarchiving takes a slot back and returns 402 when the plan is full. password and allowed_email_domain gate the whole space and are inherited by pages already filed under it as well as ones published later; pass null to remove either. Setting a gate is a paid feature: on a free key it returns 402 plan_password_not_available or plan_viewer_gate_not_available. Clearing a gate, or changing one the space already carries, always works. A 409 name_taken means another active space already answers to that name — report that rather than retrying with a variation, which would leave the user with two spaces for one client. |
| delete_client_spaceA | Delete a client space. The pages filed under it are NOT deleted: they detach to floating pages and keep working on their existing URLs, so delivered work stays reachable. The space itself goes, and with it the generated portal page and any address connected to it, which stop resolving. A space-level viewer gate goes with the space too: a page that was protected only by the space passcode or email domain, and carries no gate of its own, becomes reachable by anyone holding its link. Warn the user, and set_password on the pages that have to stay private before deleting. When the user means a client is simply finished, prefer update_client_space with archived: true — archiving keeps the portal and the address live, frees the plan slot, and can be undone. |
| get_siteA | Read the current HTML source of a site you own, so you can edit it and update_site in place. Returns the exact stored index.html (not rendered or text-stripped), preserving CSS and inline charts. |
| link_walletA | Link your wallet to a Stacktree account so the pages you publish are owned there — and adopt the ones you already published. Get a LINK-XXXX code from your human (generated at stacktr.ee/wallets). First call with just { code } to get the exact message to sign; sign it with your wallet (personal_sign / EIP-191); then call again with { code, wallet, signature }. |
| list_feedbackA | Read viewer feedback left on a site via the Agentation toolbar. Each item has a comment plus the annotated element, selected text, intent and severity; unresolved items first. The loop: list_feedback → get_site/update_site → resolve_feedback. |
| resolve_feedbackA | Mark a feedback item as addressed after fixing the page. Optionally include a note describing the change. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 25 tools
Most tools have clearly separated lifecycle roles, but get_site and get_content overlap almost exactly: both return the exact stored HTML of an owned page and both point to update_site. set_client and create_client_space also engage similar auto-create behavior, though their primary intents differ. The verbose descriptions rescue most boundaries, so this is partial overlap rather than chaos.
Every tool follows a consistent lowercase snake_case verb_noun pattern: get_, list_, create_, update_, delete_, set_, publish_, restore_, revoke_, link_. The verbs are standard and the objects are predictable resources like site, client_space, share_link, feedback, password, and expiry. Minor semantic redundancy like get_site vs get_content does not break the naming convention.
25 tools sits at the top of the heavy band, and the set includes a duplicate reader plus rarely-needed helpers like create_client_space that publish_html can auto-trigger. The broad platform scope justifies many of the tools, but the surface feels slightly over-provisioned for an agent to navigate. It is not egregious, but it is not a tight 3-15 tool set.
The page lifecycle is fully covered: publish, read, update, delete, restore, list, claim, expiry, access gates, and share links. Client spaces have create/list/update/delete plus assignment, and the feedback loop is complete with toggle, list, and resolve. Account/wallet and design-guidance tools round out the domain with no obvious dead ends.