Stacktree MCP Server
Publish HTML to a private, unguessable URL from any MCP client. Gate by password or email domain; replace in place.
Documentation
Stacktree — docs
Source: https://stacktr.ee/docs
[
Stacktree docs
](https://stacktr.ee/)
[Agents](https://stacktr.ee/agents)
[Docs](https://stacktr.ee/docs)
[Use cases](https://stacktr.ee/use-cases)
[Pricing](https://stacktr.ee/pricing)
[Blog](https://stacktr.ee/blog)
[Dashboard](https://app.stacktr.ee)
Search docs⌘K
Getting started
- [Connect an agent](#connect)
[CLI installer](#installer)
- [Claude.ai](#claude-ai)
- [Claude Code · Codex](#claude-code)
- [MCP config file](#mcp-config)
- [Slack](#slack)
Reference
- [HTTP API](#api)
[Auth](#auth)
- [POST /sites](#post-sites)
- [PUT /sites/:id](#put-sites)
- [PATCH /sites/:id](#patch-sites)
- [List · raw · delete](#more-sites)
- [Share tokens](#share-tokens)
- [API keys](#keys)
- [MCP server](#mcp)
- [Agent payments (x402)](#payments)
- [Custom domains](#domains)
- [OAuth](#oauth)
- [Limits](#limits)
Machine-readable
- [llms.txt](https://stacktr.ee/llms.txt)
- [pricing.md](https://stacktr.ee/pricing.md)
- [auth.md](https://stacktr.ee/auth.md)
- [x402.md](https://stacktr.ee/x402.md)
[Changelog](https://stacktr.ee/changelog) · [Blog](https://stacktr.ee/blog)
[Dashboard](https://app.stacktr.ee) · [Pricing](https://stacktr.ee/pricing)
Documentation
The publish primitive for agent-made HTML.
private by default MCP-native replace-in-place
For a guided walkthrough with your actual API key inlined into snippets, open app.stacktr.ee/connect.
Connect an agent
npx stacktree-install — recommended
One command wires every agent at once — Claude Code, Cursor, Codex, OpenCode, Amp — and drops the stacktree-publish skill in for Claude:
`npx stacktree-install`
It signs you in via a one-time code at app.stacktr.ee/connect/cli and mints an API key automatically; pass an existing key as an argument (npx stacktree-install stk_live_…) to skip sign-in.
Claude.ai — custom connector
No CLI, no API key copy-paste.
- Open [claude.ai/settings/connectors](https://claude.ai/settings/connectors) → Add custom connector.
- Paste https://api.stacktr.ee/mcp as the Remote MCP server URL.
- Leave OAuth Client ID/Secret blank — Stacktree auto-registers via Dynamic Client Registration (RFC 7591).
- Click Add → Claude.ai redirects you to Stacktree to sign in and approve. Tools are then available in any conversation.
Claude Code · Codex — CLI
One-line install. Identical syntax between the two:
`claude mcp add stacktree -- npx -y stacktree-mcp
codex mcp add stacktree -- npx -y stacktree-mcp`
Both expose the same 8 tools (see MCP server). Set STACKTREE_API_KEY in your shell — generate one at app.stacktr.ee/api-keys.
Prefer a skill? Installs SKILL.md + helper script into your agent's skills directory:
`npx skills@latest add stevysmith/stacktree-skill
export STACKTREE_API_KEY=stk_live_...`
Source: github.com/stevysmith/stacktree-skill · the whole collection at stacktr.ee/skills
MCP config file — Cursor / Claude Desktop / Windsurf / Zed
`{
"mcpServers": {
"stacktree": {
"command": "npx",
"args": ["-y", "stacktree-mcp"],
"env": { "STACKTREE_API_KEY": "stk_live_..." }
}
}
}`
Drop into ~/.cursor/mcp.json, ~/Library/Application Support/Claude/claude_desktop_config.json, ~/.codeium/windsurf/mcp_config.json, or the context_servers key in Zed's settings.
Slack
Add the Slack app (one approval; installing mints the workspace its own free identity, no Stacktree account needed). Then ⋮ → Host on Stacktree on any message with an .html or .md file — channels or DMs — posts a private link back into the conversation. Re-uploading the same filename republishes to the same URL. /stacktree link migrates the workspace's sites into a dashboard account. Details: stacktr.ee/slack.
HTTP API
Auth
Three methods, all resolve to the same user context:
Authorization: Bearer stk_live_…API key
Create at [app.stacktr.ee/api-keys](https://app.stacktr.ee/api-keys) — or let an agent buy its own over [x402](#payments).
Authorization: Bearer <clerk-session-jwt>session
Clerk session token, for dashboard-originated calls.
Authorization: Bearer <oauth-jwt>OAuth
Access token from `/oauth/token`, used by custom connectors.
POST/sites
Upload a single HTML/markdown file or a zip. multipart/form-data. Anonymous uploads work — no auth header — and live 24 hours.
filefilerequired
.html / .htm / .md / .zip
public_slugstring
Opt-in public subdomain; authed only.
passwordstring
Basic-auth gate on serve.
expires_in_hoursnumber | "never"
Default: 24h anonymous, never authed (the MCP layer overrides this to 7 days).
burn_after_read"true"
Delete after first view.
agentation"true"
Inject feedback toolbar on serve.
csp_strict"false"
Disable strict CSP (default on).
e2e"true"
Treat upload as ciphertext; the decryption key lives in the URL fragment and is never sent to Stacktree.
pii_checkoff | warn | block
Default warn (MCP layer overrides to block).
`curl -F [email protected] \
-F password=hunter2 \
-F expires_in_hours=72 \
-H "Authorization: Bearer stk_live_..." \
https://api.stacktr.ee/sites`
`{
"id": "…",
"url": "https://stacktr.ee/p/abc123…/",
"visibility": "unlisted",
"expires_at": 1781234567,
"file_count": 1,
"size_bytes": 1234,
"has_password": true,
"agentation": false
}`
PUT/sites/:idOrSlug
Replace a site's files in place — the URL never changes. Same multipart fields as POST. Authed only; 401 if not the owner. E2E-encrypted sites must be replaced with e2e=true uploads (no silent downgrade to plaintext).
`curl -X PUT -F [email protected] \
-H "Authorization: Bearer stk_live_..." \
https://api.stacktr.ee/sites/my-deck`
PATCH/sites/:idOrSlug
Update settings without re-uploading files. JSON body.
passwordstring | null
Set or remove the password gate.
expires_in_hoursnumber | null
`null` cancels expiry.
allowed_email_domainstring | null
Viewers verify an email on that domain before the page renders.
public_slugstring | null
Claim or release a public subdomain.
agentation · burn_after_read · csp_strictboolean
Toggle the serve-time behaviors documented under POST.
`curl -X PATCH \
-H "Authorization: Bearer stk_live_..." \
-H "Content-Type: application/json" \
-d '{"agentation": true, "expires_in_hours": null}' \
https://api.stacktr.ee/sites/my-deck`
List · fetch · raw · delete
GET/sites
List your sites.
GET/sites/:idOrSlug
One site, with file manifest + absolute `preview_url`.
GET/raw/:token
Page HTML stripped of head/scripts — clean text for re-feeding into an agent. Honors password gates.
DELETE/sites/:idOrSlug
Hard delete: removes R2 objects and metadata.
Share tokens
`POST https://api.stacktr.ee/sites/:idOrSlug/share-tokens { "label": "alice", "max_uses": 5, "expires_in_hours": 168 }
GET https://api.stacktr.ee/sites/:idOrSlug/share-tokens
DELETE https://api.stacktr.ee/share-tokens/:tokenId`
Returns a URL with ?t=… appended. Bypasses the password gate when valid; revocable per-token; optional max-use counter and expiry.
API keys
`POST https://api.stacktr.ee/api-keys { "label": "claude desktop" }
GET https://api.stacktr.ee/api-keys
DELETE https://api.stacktr.ee/api-keys/:id`
MCP server
Streamable HTTP MCP server at https://api.stacktr.ee/mcp (spec 2025-11-25). Connect from any MCP host that supports OAuth-based remote servers — see Connect an agent.
Tools
publish_html→ { url, id, expires_at, … }
One call, one private URL.
update_site→ { url, file_count, size_bytes }
Replace in place; the URL survives every revision.
set_password→ { ok: true }
Password-gate a site.
set_expiry→ { ok: true }
Schedule or cancel auto-delete.
set_email_gate→ { ok: true }
Restrict viewers to an email domain.
set_agentation→ { ok: true }
Toggle the on-page feedback toolbar.
list_sites→ { sites: [...] }
Everything the key owns.
delete_site→ { ok: true }
Hard delete.
Privacy-first MCP defaults
Agents act autonomously without a human reviewing every flag. The MCP layer applies tighter defaults than the raw API:
- 7-day expiry (raw API: never for authed). Pass expires_in_hours: "never" for permanence.
- PII scan in block mode (raw API: warn). Pass pii_check: "warn" to publish despite detected sensitive data.
- Unlisted token URL, strict CSP, X-Robots-Tag: noai — same defaults as the raw API.
WebMCP (in-browser)
The dashboard registers the same verbs on document.modelContext where the browser supports it (Chrome origin trial), so an in-browser agent helping a signed-in human can call them without an API key. How and why.
Want the same pattern in your own app? The palette and WebMCP registration are built on agentk, our open-source cmdk extension: define tools once as JSON Schema, humans get generated forms, agents get the schemas.
Agent payments (x402)
A fully autonomous agent — no human, no account — can buy a persistent identity and unlock paid features over x402, the open HTTP 402 payment standard (v1 and v2 accepted, USDC on Base, gasless for the payer). Machine-readable version: x402.md.
GET/provision
Lists the accepted payment rails.
POST/provision
402 → pay $1.00 → persistent `stk_live_` key, no account.
GET/unlock
The à-la-carte catalog: make-permanent $1, custom domain $5/30d, higher limits $5/30d.
POST/unlock
402 → pay → feature entitlement on your key.
POST/pay/sessions
No wallet? Returns a pay link + terminal QR; a human pays by card in two taps. Poll `GET /pay/sessions/:code/poll`.
Paying above the price in a pay session (up to $20) leaves a prepaid balance on the key that later paid actions draw from silently. Balances never expire and are refundable on request.
Custom domains
Pro+ feature (or the custom_domain x402 unlock). Bring your own hostname (docs.acme.com et al), point a CNAME at our Cloudflare for SaaS fallback origin, prove ownership via a TXT record, and traffic to that hostname serves your site over HTTPS.
POST/custom-domains
`curl -X POST https://api.stacktr.ee/custom-domains \
-H "Authorization: Bearer stk_live_..." \
-H "Content-Type: application/json" \
-d '{"hostname":"docs.acme.com","site_id":"abc123"}'`
Response includes a verify_token and the two DNS records you need to add:
`{
"hostname": "docs.acme.com",
"site_id": "abc123",
"verified": false,
"instructions": {
"cname": { "name": "docs.acme.com", "value": "proxy.stacktr.ee", "type": "CNAME" },
"txt": { "name": "_stacktree-verify.docs.acme.com", "value": "verify_", "type": "TXT" }
}
}`
POST/custom-domains/:hostname/verify
After adding the DNS records, call verify. We DNS-lookup the TXT record; on match we register the hostname with CF for SaaS and SSL provisioning begins (~60 s).
`curl -X POST https://api.stacktr.ee/custom-domains/docs.acme.com/verify \
-H "Authorization: Bearer stk_live_..."`
Gotcha — DNS-only CNAME. If your DNS is on Cloudflare, the CNAME must be set to DNS only (grey cloud), not Proxied (orange). A proxied CNAME makes Cloudflare claim the hostname for your own zone and Stacktree's SaaS routing never sees the SNI.
Re-bind or delete
`PATCH https://api.stacktr.ee/custom-domains/:hostname # { "site_id": "..." } — re-bind
DELETE https://api.stacktr.ee/custom-domains/:hostname # unregister + drop row`
List your domains with GET https://api.stacktr.ee/custom-domains. Unverified rows are auto-pruned after 7 days.
OAuth (custom connector authors)
For MCP host implementers — if you're using a maintained client (Claude.ai, Cursor, etc.) skip this section.
Discovery
`GET https://api.stacktr.ee/.well-known/oauth-authorization-server
GET https://api.stacktr.ee/.well-known/oauth-protected-resource`
Both return standard RFC 8414 / RFC 9728 metadata documents.
Flow
OAuth 2.1 with PKCE (S256 required) and Dynamic Client Registration (RFC 7591). Endpoints:
POST/oauth/register
DCR — rate-limited to 10/IP/hour.
GET/oauth/authorize
Bounces to Clerk-gated consent page on app.stacktr.ee.
POST/oauth/token
Code → access token (HS256 JWT, 30-day TTL).
POST/oauth/revoke
RFC 7009 revocation.
Callback for hosted Claude surfaces: https://claude.ai/api/mcp/auth_callback.
Limits
LimitAnonymousFreeProAgent
Uploads / 24h20 per IP501,000unlimited
Per-site size10 MB25 MB250 MB1 GB
Active sites1 / 24h window5unlimitedunlimited
Files / archive1,0001,0001,0001,000
Default expiry24hnever (API) · 7d (MCP)never (API) · 7d (MCP)never (API) · 7d (MCP)
Custom slug—✓✓✓
Unbranded social previews——✓✓
Custom domains——550
DCR rate limit: 10 client registrations / IP / hour. Sites auto-purge from R2 + D1 within 1 hour of expiry.
No plan? The higher_limits unlock ($5 / 30 days over x402) lifts a free identity to Agent-tier limits — 1 GB per site, unlimited daily publishes, no active-site cap.
Full markdown summary of the Stacktree marketing surface: https://stacktr.ee/llms-full.txt