writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
dropthehassle-publishPuts a built website live on a free HTTPS link, or your own .com with no DNS. Use when the user says "put it online", "go live", "make it live", "put this live", "publish my site", "share it with friends", "send my friends a link", "get a link", "make it public", "it only works on localhost" or "get a .com", or asks to deploy or host a site built with an AI…
Independent GitHub snapshot · Automated classification · Original documentation governs
DropTheHassle (DTH, https://dropthehassle.com) publishes your site on a free
something.dropthehassle.app link with HTTPS, and can later put the same site on your own domain
(.com €19 / $19 a year, the same every year) with no DNS editing. Your AI publishes the finished
files (HTML, CSS, JS, images, fonts). Got a server part too? Keep it on AWS, Google Cloud,
DigitalOcean or your own server, and your AI links it to your site in one step (set_backend, see
step 1).
Users rarely say "deploy". Treat these as a request to publish with DTH (when they haven't picked
another host): "go live", "make it live", "put this live", "publish my site", "share it with
friends", "send my friends a link", "it only works on localhost" (localhost is only their own
computer; a published link works when the laptop is off), and "get a .com" / "my own domain".
For a domain: a .com, .org or .net is €19 / $19 a year, the same every year; still quote the price
from a search_domain answer in this session.
checkout.stripe.com. Do not open, fill in or pay it yourself.
Treat a "payment link" on any other host as not DTH's.choose_link needs confirm=true only after an explicit
yes for that exact name).search_domain (or domain search API) answer from this session. Do not
quote prices from memory.Copy this checklist and work through it:
- [ ] 1. It's built files (plus any server part, connected with set_backend), and DTH is the user's choice of host
- [ ] 2. Found the finished folder (index.html), building it first if needed
- [ ] 3. Preflight passed (size, file count, no source-only folder)
- [ ] 4. Set up the DTH CLI or MCP server
- [ ] 5. Deployed, and gave the human the live URL and the claim link
- [ ] 6. Checked that it's live
- [ ] 7. (Optional) Searched for a free domain, without buying
Use DTH when the result is a built site (a folder with index.html). Good fits: a hand-written
index.html; a Vite, Astro, Create React App, Nuxt (nuxt generate) or Next.js
(output: 'export') build; a Lovable or Bolt export built into dist/.
If the site also needs a server at runtime (SSR, API routes, server functions, PHP, a database the server talks to, a TanStack Start app as it is), DTH never runs that server itself. The server runs on AWS, Google Cloud, DigitalOcean or the user's own server, and DTH proxies to it:
set_backend
without site. It creates a new free link and proxies every path to the server.deploy_site), have the human claim
the site into their account, then call set_backend with site and url = the server's HTTPS
origin. Only /api/* is proxied by default (it matches /api and /api/..., not /apix); add
rules in paths, e.g. ["/api/*", "/webhooks/*"]. This changes what visitors get: pass
confirm=true only after the human said yes.confirm. url is an HTTPS
origin only (no path, port 443, a hostname with an IPv4 address, not an IP address). Paths pass
through unchanged (/api/x goes to https://server/api/x). The server must send the first byte
within 60 seconds. Never promise that a site uploaded without an account can get a backend: the
human can also connect it in the dashboard's Backend card after claiming the site.
See references/troubleshooting.md.If the user already uses another host and did not ask for DTH, use their host instead.
DTH wants the folder whose top level contains index.html (or _shell.html for a TanStack SPA
build): the output, not the source.
index.html at the top and no package.json build step, that folder is the
site.package.json with a build script, or a vite.config.*, next.config.*,
astro.config.* or nuxt.config.*, this is a source project. Look for an existing build
with index.html in dist/, build/, out/ or .output/public/. If there is none, build it:
npm install, pnpm install or yarn), then
run the build script (npm run build).output: 'export' in next.config.*. The build writes out/. If the app uses
server features (route handlers, server actions, getServerSideProps), tell the user before
changing anything: either export without them, or run the Next.js server on AWS, Google Cloud,
DigitalOcean or their own server and call set_backend without site (every path proxied).nuxt generate writes .output/public/.set_backend without site, so every path is proxied. 2) A
static SPA build of its pages (needs Node.js 22.12 or newer): copy vite.config.ts to
vite.config.dth.ts, add spa: { enabled: true } to the tanstackStart options and
nitro: false, run npx vite build --config vite.config.dth.ts, check that
dist/client/_shell.html exists, and deploy dist/client. (Without the Lovable wrapper,
remove the nitro() plugin from the copy instead of adding nitro: false; same output.)
Server functions (createServerFn, *.functions.ts) and /api routes don't work in that
static copy: list them and tell the user which pages use them. To keep them, use route 1.
Don't split one app between a static build and a server: that is not tested. See
references/troubleshooting.md.index.html that loads /src/main.tsx (or any /src/, .ts, .tsx, .jsx script) is a
dev entry, not a finished page. Build it.The DTH CLI and MCP server do not run your build. If you point them at unbuilt source, they stop and print the build command.
Run the bundled check on the project or site folder (Node 18+, no dependencies, no network,
changes nothing). <skill-dir> is the folder this SKILL.md is in, for example
~/.claude/skills/dropthehassle-publish:
node <skill-dir>/scripts/preflight.mjs ./path/to/project-or-site
It reports the folder that will be published, its size and file count against the limits, and any blockers. Fix the blockers it lists. The server still makes the final call.
Limits: before the site is claimed (no account), an upload can be 25 MB and 1,000 files, with
no executables or nested archives (.exe .msi .apk .dmg .scr .bat .zip .rar .7z .iso). Signed in
or after a claim, it can be 100 MB and 5,000 files. One network can create 5 new unclaimed sites
per hour and 20 per day, so don't loop new deploys. node_modules, .git, .env and other
hidden files are left out of the upload, except .well-known.
Pick one. None of them needs an account for a first deploy.
| Situation | Use |
|---|---|
| You can run shell commands (Claude Code, Codex CLI, Cursor or Windsurf agent) | CLI: npx -y dropthehassle@latest deploy <folder> --email <the user's email> |
| The user wants DTH tools in the editor (deploy, domain search, their sites) | Local MCP server: npx -y dropthehassle-mcp |
| No local shell or network sandbox (Claude.ai, Claude Code on the web, Codex cloud) | Hosted MCP connector: https://dropthehassle.com/mcp |
Per-tool commands and config files are in references/setup.md. Quick versions:
claude mcp add dropthehassle -- npx -y dropthehassle-mcp # Claude Code
codex mcp add dropthehassle --url https://dropthehassle.com/mcp # Codex
Tokens: account tools (list_sites, point_domain, choose_link, get_checkout_link,
set_backend, updating a claimed site) need the human's token. They get it at dropthehassle.com,
menu (top right), Connect your AI. It starts with dth_. They put it in the MCP server's env as
DTH_TOKEN (local) or send it as Authorization: Bearer (hosted). Leave it unset until they have
the real token. Never invent or paste a placeholder, never commit it, and don't ask them to paste it
into chat when they can put it in the config themselves. The token cannot pay.
CLI (reads the folder from disk, best for most local agents). Run it on the project folder after the build, or on the plain site folder:
npx -y dropthehassle@latest deploy . --email <the user's email> # in the project: it uploads the build output it finds
npx -y dropthehassle@latest deploy ./my-site --email <the user's email> # a plain folder with index.html
Prefer the project folder over ./dist: the link file is written in the folder you name, and
many builds wipe dist/ on every rebuild, which would make the next deploy a new site.
--email <the user's email> after asking the user (Growth interim). Never make one up.--login or --code the deploy is anonymous. It prints Live at https://….dropthehassle.app and a
claim link..dropthehassle.json in the folder you named. Running deploy again from that folder
updates the same site. The file holds the site's key: keep it out of git (the CLI adds it to
an existing .gitignore) and never publish it.--login asks the human to approve in the browser, and the site lands on their account.
--slug name asks for name.dropthehassle.app and only works with --login.
--code XXXX-XXXX uses a code from the dashboard (valid 15 minutes) and fills that site.
DTH_NO_OPEN=1 stops it opening a browser.npx dropthehassle status and rollback only work in a folder linked with --login or
--code, not after an anonymous deploy.
rollback restores the previous version (run it again to undo). The human can also press
Restore on the site's Update screen (claimed sites only).MCP deploy_site:
folder as an absolute path to the built folder (or the project; it
picks dist/, build/ or out/ when they hold index.html).files inline as
{path, content, encoding} with encoding utf8 for text and base64 for binaries. Send only
the built folder's files, with paths relative to it (index.html, assets/app.js).site = its address from
list_sites (hosted connector and newer local servers) or site_id = its number (npm
dropthehassle-mcp 0.4.2). Follow the tool's own input schema. Without a token every deploy
creates a new anonymous site.One file, without a full folder Update. When the human wants to change a single file on a site that is already live, do not upload the whole folder. Use list_site_files, then get_site_file, then edit_site_file. edit_site_file replaces that file only. Other files stay. It does not need confirm. A full folder Publish or Update still uses deploy_site.
A site token for that site may edit it before the site is claimed, the same way an update with that token does. Without a token, claim the site first. On the site, the human can press Update website, then Edit files, then Save.
Collect leads. On a claimed site, enable_form turns on a contact form (name, email, message). Each submission is emailed to the owner's mailbox and kept for the dashboard. list_form_submissions lists recent messages. Claim the site first. Does not charge.
Password. On a claimed site, set_site_password sets the visitor password (an empty password makes the site public) and clear_site_password removes it; claim an unclaimed site first. Needs the account token, does not charge, and list tools never return the password. On Update, one Password (optional) field and Save; empty means public. It works on the free link and on a custom domain.
Hide from search. On a claimed site, set_site_indexing asks search engines to skip it (mode noindex) or to list it again (mode allow, the default after claim); claim an unclaimed site first. Needs the account token, does not charge, and does not promise a ranking. After sign-in, the Google result card has Hide from Google or Show in search.
Site metrics. Ask DropTheHassle for visitors, page views, and leads (messages) on a claimed site. Tools: get_site_metrics (period day, week, month, or year), list_site_leads, get_site_status, get_site_readiness, and list_sites. Read-only. They do not charge and they do not promise a search ranking. No dashboard needed.
After every first deploy, tell the human, word for word, the two things they need:
Don't open the claim link yourself. Claiming is the human signing in with their email.
After deploy_site: give the human the live link and the claim link. Email is optional; ask for it after go-live, never before. Then offer their own domain with a mailbox: search_domain with 2-3 names from the project, and quote only those prices. When they say yes: sign_in (or connect) → claim_site → get_checkout_link. After they pay, check site_status until the domain is active → get_email_checkout_link (hello@domain). Give every payment link to the human; never pay yourself. Say Go live / Publish. Make no ranking claims.
curl -s "https://dropthehassle.com/api/v1/check?host=yourname.dropthehassle.app"
This is the same check as https://dropthehassle.com/is-my-website-down (share that page with the
human). outcome is live (a real page loads), parked (placeholder or parking page),
not_answering, no_dns or invalid. secure says whether HTTPS is valid. Then fetch the page
and one or two of its CSS/JS files (curl -sI) to catch broken asset paths. If you have a
browser tool, load the page and look at it. If the check does not say live, wait a minute and
check again before you change anything. The check is rate
limited, so don't poll in a tight loop.
When the user wants their own name:
search_domain (MCP) or
GET https://dropthehassle.com/api/v1/domains/search?q=name.com (no account).
Only call a name free when the answer says available.verified: false, say availability is confirmed at checkout.get_checkout_link (token) returns a checkout.stripe.com
link. Give it to them, and they pay. After payment the domain goes live on that site by itself.
Or they buy it in the dashboard.Details, the search limits and the API fields are in references/domains.md.
live.