Deploy a site
Three ways to ship the same single HTML file — each gives you a live URL at name.myhtml.site.
- Dashboard — paste or drop your HTML in the New site box. You'll see a live
name.myhtml.sitepreview and whether the name is free before you deploy. - REST API — one call with your API key:
curl -X POST https://myhtml.io/api/v1/sites \
-H "Authorization: Bearer mh_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "slug": "pong", "html": "<!doctype html>…" }'A CLI (npx myhtml-cli) and the MCP server are launching soon. Free sites publish via a quick safety scan (the “slow lane”); paid plans publish instantly. Pick a name that's taken and you'll get a clear error — names are first-come and never overwrite someone else's site.
The mh SDK
When the edge serves your page it injects a short-lived, origin-bound site token and the SDK, so window.mh is just there. The token scopes every call to your site, so there are no API keys in your HTML and nothing for a visitor to steal. The surface:
mh.db— a per-site JSON database (shared + per-visitor + key-value).mh.files— file uploads served from your subdomain.mh.ai— call Claude from your page (opt-in; see below).mh.visitor()— the current visitor's anonymous id.mh.realtime— websockets/presence (phase 2).
mh.db — the database
Every site gets its own document database — no setup, no schema, no connection string. A collectionholds JSON documents you create, query, update, and delete. Behind the scenes it's stored as JSONB and scoped to your site; you just call the SDK.
// mh is auto-injected at serve time — no <script> config, no keys.
const scores = mh.db.collection('scores');
// Create a document (returns it with a generated id):
const doc = await scores.create({ player: 'alice', points: 42 });
// List — filter (shallow equality), sort ('-field' = desc), limit, cursor:
const { docs, nextCursor } = await scores.list({
filter: { player: 'alice' },
sort: '-points',
limit: 10,
});
await scores.get(doc.id); // one doc
await scores.update(doc.id, { points: 99 }); // shallow-merge patch
await scores.delete(doc.id);Shared vs. per-visitor
Collections and mh.db.set/get are shared — every visitor of your site reads the same data (great for leaderboards, guestbooks, galleries). mh.db.user is private to each visitor, keyed by an anonymous per-browser id (great for saved progress or preferences).
// Shared — every visitor of this site sees these:
await mh.db.collection('guestbook').create({ name: 'sam' });
// Site key-value sugar (also shared):
await mh.db.set('theme', 'dark');
const theme = await mh.db.get('theme');
// Per-visitor — private to each browser (anonymous visitor id):
await mh.db.user.set('progress', { level: 7 });
const me = await mh.db.user.get('progress');list() filters are shallow equality ({ status: 'done' }); sort is '-field' for descending. Calls are rate-limited per site and per visitor, and you own all of it — wipe or delete the site anytime from your dashboard.
mh.files — uploads
Upload a File or Blob straight from the page and get back a URL served from your own subdomain at /_f/<key>.
const { url } = await mh.files.upload(file); // → https://<you>.myhtml.site/_f/<key>
await mh.files.list();
await mh.files.delete(path);mh.ai — AI on a page
Live — enable it per sitemh.ai.chat()lets a page call Claude with no API key in your code — for a roast of a high score, a “make it vegan” button, an NPC, a summariser. It's off by default on every site. You turn it on per site under Settings → AI, and choose who pays:
- Off — pages can't use AI (the call returns a clear error).
- On — you pay — visitors use AI on your credits, capped per day so a viral page can't drain your wallet.
- On — visitor pays — each visitor signs in and uses their own credits.
// Turn AI On for the site first (Dashboard → site → Settings).
const res = await mh.ai.chat([
{ role: 'user', content: 'Roast my high score of 42.' },
]);
console.log(res.content);Calls run through our metered proxy (a fast, cost-effective model by default), count toward your site's daily cap, and are rate-limited per visitor. “Visitor pays” arrives with visitor sign-in (phase 3), and mh.realtime (websockets + presence) is phase 2.
REST API
Base URL https://myhtml.io/api/v1. Authenticate with a session cookie (dashboard) or an API key from Dashboard → API keys.
curl -X POST https://myhtml.io/api/v1/sites \
-H "Authorization: Bearer mh_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "slug": "pong", "html": "<!doctype html>…" }'| Method | Path | Purpose |
|---|---|---|
| POST | /sites | Create + first deploy (errors if name taken) |
| GET | /sites | List your sites |
| GET | /sites/check?slug= | Is a name available? |
| POST | /sites/:slug/deploy | Re-deploy an existing site |
| PATCH | /sites/:slug | Update settings (AI, visibility) |
| DELETE | /sites/:slug | Delete a site (storage purged) |
| GET | /sites/:slug/data/:collection | Read a site's mh.db collection |
| GET / POST | /keys | List / create API keys |
| GET | /usage | Usage vs plan limits |
Agents: the MCP server
Launching soonThe MCP server will wire myhtml into Claude (or any MCP client) in one line — create_site, update_site, list_sites, read_site_data, and more, authenticated with your API key. Until it ships, agents use the REST API above (it covers everything).
claude mcp add --transport http myhtml https://mcp.myhtml.io/mcpThe free-site badge
Free sites carry a small badge that the edge injects automatically. Upgrade to a paid plan to remove it.