htmlhost-mcp 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,6 +83,8 @@ htmlhost-mcp
83
83
  | `pull_site` | Download a site's files to a local directory |
84
84
  | `delete_site` | Permanently delete a site |
85
85
  | `whoami` | Show the current authenticated user |
86
+ | `list_versions` | List a site's published versions and what each changed |
87
+ | `restore_version` | Make an earlier version live again (Pro) |
86
88
 
87
89
  ### `deploy`
88
90
 
@@ -138,6 +140,24 @@ confirm (required) Must be true — safety check
138
140
 
139
141
  Returns the authenticated user's email, name, handle, and plan.
140
142
 
143
+ ### `list_versions`
144
+
145
+ ```
146
+ slug (required) The site slug
147
+ ```
148
+
149
+ Returns the site's versions, newest first, with time, source, size, what changed, and which one is live. `usable: false` means the owner's plan can't restore them.
150
+
151
+ ### `restore_version`
152
+
153
+ ```
154
+ slug (required) The site slug
155
+ version (required) Version number from list_versions
156
+ confirm (required) Must be true — safety check
157
+ ```
158
+
159
+ Publishes that version again as a new version, so it can be undone the same way. Needs the site owner to be on Pro.
160
+
141
161
  ## Architecture
142
162
 
143
163
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htmlhost-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for htmlhost.co — deploy, list, and manage sites from AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.mjs CHANGED
@@ -48,7 +48,7 @@ class ApiError extends Error {
48
48
  function headers(extra = {}) {
49
49
  const token = getToken();
50
50
  if (!token) throw new ApiError("Not logged in. Run `htmlhost login` in a terminal first.", 0);
51
- return { Authorization: `Bearer ${token}`, ...extra };
51
+ return { Authorization: `Bearer ${token}`, "X-HTMLHost-Client": "mcp", ...extra };
52
52
  }
53
53
 
54
54
  async function withRetry(fn, label = "request", retries = 3) {
package/src/server.mjs CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  mimeFromExt,
27
27
  } from "./api.mjs";
28
28
 
29
- const VERSION = "1.0.0";
29
+ const VERSION = "1.1.0";
30
30
 
31
31
  /**
32
32
  * Helper: wraps a handler to return proper MCP content and catch errors.
@@ -314,5 +314,56 @@ export function createServer() {
314
314
  })
315
315
  );
316
316
 
317
+ // -------------------------------------------------------------------
318
+ // Tool: list_versions
319
+ // -------------------------------------------------------------------
320
+ server.registerTool(
321
+ "list_versions",
322
+ {
323
+ title: "List site versions",
324
+ description:
325
+ "List the published versions of a site, newest first, with what each one changed. " +
326
+ "`usable: false` means the owner's plan can't restore them (Pro feature).",
327
+ inputSchema: z.object({
328
+ slug: z.string().describe("The site slug"),
329
+ }),
330
+ },
331
+ wrap(async ({ slug }) => {
332
+ if (!isLoggedIn()) return "Not logged in. Run `htmlhost login` in a terminal first.";
333
+ const cleanSlug = slug.replace(/\.htmlhost\.co$/, "");
334
+ return apiGet(`/api/sites/${encodeURIComponent(cleanSlug)}/versions`);
335
+ })
336
+ );
337
+
338
+ // -------------------------------------------------------------------
339
+ // Tool: restore_version
340
+ // -------------------------------------------------------------------
341
+ server.registerTool(
342
+ "restore_version",
343
+ {
344
+ title: "Restore a site version",
345
+ description:
346
+ "Make an earlier version of a site live again. It's published as a new version " +
347
+ "(the current one stays in history), so this can be undone by restoring again. Needs Pro.",
348
+ inputSchema: z.object({
349
+ slug: z.string().describe("The site slug"),
350
+ version: z.number().int().positive().describe("The version number to restore (from list_versions)"),
351
+ confirm: z.boolean().describe("Must be true to confirm replacing the live site"),
352
+ }),
353
+ },
354
+ wrap(async ({ slug, version, confirm }) => {
355
+ if (!isLoggedIn()) return "Not logged in. Run `htmlhost login` in a terminal first.";
356
+ if (!confirm) return "Restore not confirmed. Set confirm to true to proceed.";
357
+ const cleanSlug = slug.replace(/\.htmlhost\.co$/, "");
358
+ const data = await apiPost(`/api/sites/${encodeURIComponent(cleanSlug)}/versions/${version}/restore`, {});
359
+ return {
360
+ message: `Restored v${version}; it's live as v${data.version}.`,
361
+ url: data.url ? (String(data.url).startsWith("http") ? data.url : `https://${data.url}`) : `https://${cleanSlug}.htmlhost.co`,
362
+ version: data.version,
363
+ restoredFrom: version,
364
+ };
365
+ })
366
+ );
367
+
317
368
  return server;
318
369
  }