@drop2run/mcp 0.13.0 → 1.0.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 +30 -3
- package/dist/index.js +118 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
# @drop2run/mcp
|
|
2
2
|
|
|
3
|
-
An MCP server that lets any MCP client — Claude, Cursor, VS Code, Codex, Gemini CLI — publish what it made to
|
|
3
|
+
An MCP server that lets any MCP client — Claude, ChatGPT, Cursor, VS Code, Codex, Gemini CLI — publish what it made to
|
|
4
4
|
[Drop2Run](https://dropto.run): an HTML page, a markdown note, a folder of documents or a built site. It
|
|
5
5
|
returns a live HTTPS URL.
|
|
6
6
|
|
|
7
7
|
Full documentation at [dropto.run/docs/mcp](https://dropto.run/docs/mcp).
|
|
8
8
|
|
|
9
|
+
## In a chat on the web — no package at all
|
|
10
|
+
|
|
11
|
+
Claude on the web and on a phone, ChatGPT and other hosted chats cannot run this package, so the same
|
|
12
|
+
tools are hosted at a URL:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
https://mcp.dropto.run/mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Claude** — connect it from
|
|
19
|
+
[Claude's connector directory](https://claude.ai/directory/connectors/drop2run) and sign in to Drop2Run
|
|
20
|
+
when asked. To add it by URL instead, open
|
|
21
|
+
[this link](https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Drop2Run&connectorUrl=https%3A%2F%2Fmcp.dropto.run%2Fmcp)
|
|
22
|
+
or *Settings → Connectors → Add custom connector* and paste the URL.
|
|
23
|
+
**Claude Code** — `claude mcp add --transport http drop2run https://mcp.dropto.run/mcp`. **ChatGPT** — add
|
|
24
|
+
it as a custom connector, where your plan offers developer mode.
|
|
25
|
+
|
|
26
|
+
The hosted server has no `login` / `login_code` (the chat signs in for it) and no `publish_dir` (there is
|
|
27
|
+
no disk), and `publish_files` takes text files only. For a folder on disk, a PDF or an image, run the
|
|
28
|
+
package below.
|
|
29
|
+
|
|
9
30
|
## Setup
|
|
10
31
|
|
|
11
32
|
**Claude Desktop** — download [`drop2run.mcpb`](https://dropto.run/drop2run.mcpb) and open it. Claude
|
|
@@ -87,6 +108,7 @@ lose it.
|
|
|
87
108
|
| `list_sites` | Lists the sites on the account |
|
|
88
109
|
| `list_folders` | Lists the account's folders, each with the path `folder` accepts |
|
|
89
110
|
| `delete_site` | Deletes a site permanently, subdomain included — requires the subdomain repeated as `confirm` |
|
|
111
|
+
| `change_subdomain` | Moves a site to a new subdomain; the old one keeps redirecting — requires the current subdomain as `confirm` |
|
|
90
112
|
| `get_site` | Reads a site's settings, whether it is paused, and its versions with the live one marked |
|
|
91
113
|
| `update_site` | Changes a site's name, serving mode, password, invite-only, comments, forms, scheduled takedown or folder |
|
|
92
114
|
| `list_people` | Lists who a site is shared with, what each may do, and whether only they can open it |
|
|
@@ -104,8 +126,8 @@ Both publish tools take an optional `site` — a subdomain or site id to publish
|
|
|
104
126
|
new site is created.
|
|
105
127
|
|
|
106
128
|
Both also take an optional `subdomain`, which names the new site instead of letting the server generate
|
|
107
|
-
one. It is for a person who asked for a particular address: the name becomes the URL,
|
|
108
|
-
|
|
129
|
+
one. It is for a person who asked for a particular address: the name becomes the URL, and a site made
|
|
130
|
+
under a guessed one has to be deleted, or moved with `change_subdomain`, by hand. `site` and `subdomain` are
|
|
109
131
|
opposites — one publishes over a site that exists, the other creates one that does not — so passing both
|
|
110
132
|
is refused rather than resolved.
|
|
111
133
|
|
|
@@ -114,6 +136,11 @@ by path such as `Clients/Acme`, or by the id `list_folders` shows. Like `subdoma
|
|
|
114
136
|
site, so it cannot be combined with `site`. It never creates a folder: one that does not exist is refused
|
|
115
137
|
with the list of those that do, and nothing is published.
|
|
116
138
|
|
|
139
|
+
`change_subdomain` moves a site to a new address. The old one keeps working by redirecting to the new
|
|
140
|
+
one, and nobody else can take it. Each site gets two changes, and a change back to an old name counts
|
|
141
|
+
too, so it asks for the current subdomain as `confirm`. Visitors who signed in to the site or typed its
|
|
142
|
+
password are asked again on the new address. `get_site` shows the changes left and the former names.
|
|
143
|
+
|
|
117
144
|
`update_site` changes only the settings it is given. An empty `name`, `password` or `expiresAt` clears
|
|
118
145
|
that setting, and `folder: "root"` moves the site back to the top level. A takedown pauses the site by
|
|
119
146
|
default; `expiryAction: "delete"` removes it instead, and asks for the subdomain as `confirm` the way
|
package/dist/index.js
CHANGED
|
@@ -88,10 +88,18 @@ function missingCredentialsMessage(surface = "cli") {
|
|
|
88
88
|
].join("\n");
|
|
89
89
|
}
|
|
90
90
|
function sizeOf(content) {
|
|
91
|
-
|
|
91
|
+
if (ArrayBuffer.isView(content)) return content.byteLength;
|
|
92
|
+
if (isArrayBuffer(content)) return content.byteLength;
|
|
93
|
+
if (typeof content.size === "number") return content.size;
|
|
94
|
+
throw new TypeError("File contents must be a Uint8Array, an ArrayBuffer or a Blob.");
|
|
95
|
+
}
|
|
96
|
+
function isArrayBuffer(value) {
|
|
97
|
+
return Object.prototype.toString.call(value) === "[object ArrayBuffer]";
|
|
92
98
|
}
|
|
93
99
|
async function bytesOf(content) {
|
|
94
|
-
|
|
100
|
+
if (ArrayBuffer.isView(content)) return content;
|
|
101
|
+
if (isArrayBuffer(content)) return new Uint8Array(content);
|
|
102
|
+
return new Uint8Array(await content.arrayBuffer());
|
|
95
103
|
}
|
|
96
104
|
class DeployError extends Error {
|
|
97
105
|
/**
|
|
@@ -1654,6 +1662,7 @@ async function findSite(credentials, site) {
|
|
|
1654
1662
|
return found;
|
|
1655
1663
|
}
|
|
1656
1664
|
const SITE_ID_SHAPE = /^[0-9a-z]{26}$/i;
|
|
1665
|
+
const FORMER_NAME_CANDIDATES = 3;
|
|
1657
1666
|
async function lookUpSite(credentials, site) {
|
|
1658
1667
|
const wanted = site.trim().toLowerCase();
|
|
1659
1668
|
if (wanted === "") return void 0;
|
|
@@ -1663,6 +1672,13 @@ async function lookUpSite(credentials, site) {
|
|
|
1663
1672
|
);
|
|
1664
1673
|
const bySubdomain = sites.find((candidate) => candidate.subdomain.toLowerCase() === wanted);
|
|
1665
1674
|
if (bySubdomain !== void 0) return bySubdomain;
|
|
1675
|
+
for (const candidate of sites.slice(0, FORMER_NAME_CANDIDATES)) {
|
|
1676
|
+
const detail2 = await call(
|
|
1677
|
+
credentials,
|
|
1678
|
+
`sites/${encodeURIComponent(candidate.siteId)}`
|
|
1679
|
+
);
|
|
1680
|
+
if (detail2.formerSubdomains?.some((former) => former.subdomain === wanted)) return candidate;
|
|
1681
|
+
}
|
|
1666
1682
|
if (!SITE_ID_SHAPE.test(wanted)) return void 0;
|
|
1667
1683
|
const response = await fetch(
|
|
1668
1684
|
`${credentials.apiBaseUrl}/sites/${encodeURIComponent(wanted.toUpperCase())}`,
|
|
@@ -1746,7 +1762,19 @@ async function getSite(credentials, siteId) {
|
|
|
1746
1762
|
createdAt: deploy2.createdAt,
|
|
1747
1763
|
completedAt: deploy2.completedAt,
|
|
1748
1764
|
filesKept: deploy2.filesKept
|
|
1749
|
-
}))
|
|
1765
|
+
})),
|
|
1766
|
+
subdomainChangesLeft: site.subdomainChangesLeft ?? null,
|
|
1767
|
+
formerSubdomains: (site.formerSubdomains ?? []).map((former) => former.subdomain)
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
async function changeSubdomain(credentials, siteId, subdomain) {
|
|
1771
|
+
const changed = await call(credentials, `sites/${encodeURIComponent(siteId)}/subdomain`, {
|
|
1772
|
+
method: "POST",
|
|
1773
|
+
body: JSON.stringify({ subdomain })
|
|
1774
|
+
});
|
|
1775
|
+
return {
|
|
1776
|
+
...changed,
|
|
1777
|
+
formerSubdomains: changed.formerSubdomains.map((former) => former.subdomain)
|
|
1750
1778
|
};
|
|
1751
1779
|
}
|
|
1752
1780
|
async function updateSiteSettings(credentials, siteId, change) {
|
|
@@ -1938,6 +1966,10 @@ function describeMode(result) {
|
|
|
1938
1966
|
if (result.mode === "spa") return "Served as a single-page app.";
|
|
1939
1967
|
return "Served as a static site.";
|
|
1940
1968
|
}
|
|
1969
|
+
async function ownSiteAt(credentials, subdomain) {
|
|
1970
|
+
const found = await lookUpSite(credentials, subdomain);
|
|
1971
|
+
return found?.subdomain.toLowerCase() === subdomain.trim().toLowerCase() ? found : void 0;
|
|
1972
|
+
}
|
|
1941
1973
|
async function resolveSite(credentials, site, newSite) {
|
|
1942
1974
|
const { subdomain, folder } = newSite;
|
|
1943
1975
|
if (site !== void 0 && subdomain !== void 0) {
|
|
@@ -1952,6 +1984,13 @@ async function resolveSite(credentials, site, newSite) {
|
|
|
1952
1984
|
}
|
|
1953
1985
|
if (site === void 0) {
|
|
1954
1986
|
const folderId = folder === void 0 ? void 0 : await resolveFolder(credentials, folder);
|
|
1987
|
+
const held = subdomain === void 0 ? void 0 : await ownSiteAt(credentials, subdomain);
|
|
1988
|
+
if (held !== void 0) {
|
|
1989
|
+
if (held.liveDeployId === null) return held;
|
|
1990
|
+
throw new Error(
|
|
1991
|
+
`You already have a site at ${held.subdomain}, and it is live. To replace what it serves, publish to it as an existing site (the \`site\` argument, or \`--site\` on the command line); to make a new site, choose another subdomain.`
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1955
1994
|
return await createSite(credentials, subdomain, folderId);
|
|
1956
1995
|
}
|
|
1957
1996
|
const found = await lookUpSite(credentials, site);
|
|
@@ -2234,6 +2273,12 @@ function accessOf(site) {
|
|
|
2234
2273
|
if (site.invitedOnly === false) return "public";
|
|
2235
2274
|
return "no password (whether it is invite-only could not be read)";
|
|
2236
2275
|
}
|
|
2276
|
+
function addressLines(site) {
|
|
2277
|
+
return [
|
|
2278
|
+
...site.formerSubdomains.length > 0 ? [` formerly ${site.formerSubdomains.join(", ")} (redirect here)`] : [],
|
|
2279
|
+
...site.subdomainChangesLeft === null ? [] : [` address ${site.subdomainChangesLeft} change(s) left`]
|
|
2280
|
+
];
|
|
2281
|
+
}
|
|
2237
2282
|
function describeSite(site, folderPath) {
|
|
2238
2283
|
const unavailable = (available) => available ? "" : " (not on this plan)";
|
|
2239
2284
|
const versions = site.deploys.slice(0, VERSIONS_LISTED).map((deploy2) => {
|
|
@@ -2253,6 +2298,7 @@ function describeSite(site, folderPath) {
|
|
|
2253
2298
|
` comments ${site.comments ?? "unknown"}${unavailable(site.commentsAvailable)}`,
|
|
2254
2299
|
` takedown ${site.expiresAt === null ? "none" : `${site.expiresAt} (${site.expiryAction})`}${unavailable(site.scheduledExpiryAvailable)}`,
|
|
2255
2300
|
` folder ${site.folderId === null ? "(top level)" : folderPath ?? site.folderId}`,
|
|
2301
|
+
...addressLines(site),
|
|
2256
2302
|
"",
|
|
2257
2303
|
site.deploys.length === 0 ? "No versions yet." : "Versions, newest first:",
|
|
2258
2304
|
...versions,
|
|
@@ -2435,7 +2481,7 @@ async function signInWithCode(waitSeconds, replace = false) {
|
|
|
2435
2481
|
"Call this tool again to keep waiting. Nothing has been changed yet."
|
|
2436
2482
|
].join("\n");
|
|
2437
2483
|
}
|
|
2438
|
-
const SERVER_VERSION = "0.
|
|
2484
|
+
const SERVER_VERSION = "1.0.0";
|
|
2439
2485
|
const REPLACES_A_SITE = {
|
|
2440
2486
|
readOnlyHint: false,
|
|
2441
2487
|
destructiveHint: true,
|
|
@@ -2461,6 +2507,12 @@ const REMOVES_A_SITE = {
|
|
|
2461
2507
|
idempotentHint: false,
|
|
2462
2508
|
openWorldHint: true
|
|
2463
2509
|
};
|
|
2510
|
+
const MOVES_AN_ADDRESS = {
|
|
2511
|
+
readOnlyHint: false,
|
|
2512
|
+
destructiveHint: true,
|
|
2513
|
+
idempotentHint: true,
|
|
2514
|
+
openWorldHint: true
|
|
2515
|
+
};
|
|
2464
2516
|
const CHANGES_A_SITE = {
|
|
2465
2517
|
readOnlyHint: false,
|
|
2466
2518
|
destructiveHint: true,
|
|
@@ -2621,7 +2673,9 @@ function structuredSite(site) {
|
|
|
2621
2673
|
createdAt: deploy2.createdAt,
|
|
2622
2674
|
filesKept: deploy2.filesKept,
|
|
2623
2675
|
live: deploy2.deployId === site.liveDeployId
|
|
2624
|
-
}))
|
|
2676
|
+
})),
|
|
2677
|
+
subdomainChangesLeft: site.subdomainChangesLeft,
|
|
2678
|
+
formerSubdomains: [...site.formerSubdomains]
|
|
2625
2679
|
};
|
|
2626
2680
|
}
|
|
2627
2681
|
const SITE_OUTPUT = z.object({
|
|
@@ -2689,9 +2743,10 @@ function instructionsFor(surface) {
|
|
|
2689
2743
|
"",
|
|
2690
2744
|
"A new site gets a generated name unless `subdomain` asks for one. Pass it when the person",
|
|
2691
2745
|
"named the address they want, and otherwise leave it out — a name is what somebody will be",
|
|
2692
|
-
"given as a URL, it
|
|
2693
|
-
"be deleted by hand. `site` and `subdomain` are
|
|
2694
|
-
"exists, the other creates one that does not. Never
|
|
2746
|
+
"given as a URL, changing it later spends one of the two changes a site gets, and a site",
|
|
2747
|
+
"created under a guessed one has to be renamed or deleted by hand. `site` and `subdomain` are",
|
|
2748
|
+
"opposites: one publishes over a site that exists, the other creates one that does not. Never",
|
|
2749
|
+
"pass both.",
|
|
2695
2750
|
"",
|
|
2696
2751
|
"A new site goes at the top level of the dashboard unless `folder` files it somewhere. Pass",
|
|
2697
2752
|
"it when the person named a folder, as its path (Clients/Acme) or its id; list_folders shows",
|
|
@@ -2711,9 +2766,14 @@ function instructionsFor(surface) {
|
|
|
2711
2766
|
"Publishing over an existing site replaces what it serves, so ask before doing that to a",
|
|
2712
2767
|
"site the person did not name.",
|
|
2713
2768
|
"",
|
|
2714
|
-
"delete_site is permanent and
|
|
2715
|
-
"the subdomain and pass what they say as `confirm` — do not
|
|
2716
|
-
"know, because being told it is the point of the step.",
|
|
2769
|
+
"delete_site is permanent, and the subdomain stays reserved: nobody, the person included, can",
|
|
2770
|
+
"use it again. Ask the person for the subdomain and pass what they say as `confirm` — do not",
|
|
2771
|
+
"fill it in from what you already know, because being told it is the point of the step.",
|
|
2772
|
+
"",
|
|
2773
|
+
"change_subdomain moves a site to a new address. The old one keeps redirecting and nobody else",
|
|
2774
|
+
"can take it, but each site gets only a couple of changes, a change back to an old name",
|
|
2775
|
+
"included. Use it only when the person named the new address, and pass the site's current",
|
|
2776
|
+
"subdomain, as they say it, as `confirm`.",
|
|
2717
2777
|
"",
|
|
2718
2778
|
"An existing site's settings are read with get_site and changed with update_site: its name,",
|
|
2719
2779
|
"serving mode (static, spa, docs), a password, whether forms accept submissions, a scheduled",
|
|
@@ -2738,8 +2798,10 @@ function instructionsFor(surface) {
|
|
|
2738
2798
|
].join("\n");
|
|
2739
2799
|
}
|
|
2740
2800
|
function publishFilesDescription(surface) {
|
|
2741
|
-
|
|
2801
|
+
const body = surface === "local" ? "Publishes one or more files written here — markdown, HTML, CSS, JSON, source code, any text — as a site, and returns its URL. A single page goes at index.html; a single .md, .markdown or .pdf file is a site on its own, served through the documents viewer. Text only: a PDF or an image has to be published from disk with publish_dir." : "Publishes one or more files written here — markdown, HTML, CSS, JSON, source code, any text — as a site, and returns its URL. A single page goes at index.html; a single .md or .markdown file is a site on its own, served through the documents viewer. Text only: this connector cannot publish a PDF, an image or an Office file.";
|
|
2802
|
+
return `${body} ${RESERVE_FIRST}`;
|
|
2742
2803
|
}
|
|
2804
|
+
const RESERVE_FIRST = "To hold an address before the content exists — the person asks for the site first and the files later — publish a placeholder index.html with `subdomain`, then publish the real files over it with `site`. There is no way to create an empty site.";
|
|
2743
2805
|
function registerPublishFiles(server, context) {
|
|
2744
2806
|
server.registerTool(
|
|
2745
2807
|
"publish_files",
|
|
@@ -2755,7 +2817,7 @@ function registerPublishFiles(server, context) {
|
|
|
2755
2817
|
).min(1).describe("The files to publish."),
|
|
2756
2818
|
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site."),
|
|
2757
2819
|
subdomain: z.string().optional().describe(
|
|
2758
|
-
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, it
|
|
2820
|
+
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, changing it later spends one of the few changes a site gets, and an unused site made under a guessed name has to be deleted by hand."
|
|
2759
2821
|
),
|
|
2760
2822
|
folder: FOLDER_INPUT
|
|
2761
2823
|
},
|
|
@@ -2772,7 +2834,7 @@ function registerSiteTools(server, context) {
|
|
|
2772
2834
|
"delete_site",
|
|
2773
2835
|
{
|
|
2774
2836
|
...titled("Delete a site", REMOVES_A_SITE),
|
|
2775
|
-
description: "Takes a site down permanently — its files, its history and its subdomain. Nothing here undoes it, and the subdomain
|
|
2837
|
+
description: "Takes a site down permanently — its files, its history and its subdomain. Nothing here undoes it, and the subdomain stays reserved — nobody can use it again. Requires `confirm` to repeat the site's own subdomain exactly; ask the person for it rather than filling it in from what you already know, because that is the step this asks for.",
|
|
2776
2838
|
inputSchema: {
|
|
2777
2839
|
site: z.string().min(1).describe("Subdomain or site id to delete."),
|
|
2778
2840
|
confirm: z.string().min(1).describe(
|
|
@@ -2793,11 +2855,46 @@ function registerSiteTools(server, context) {
|
|
|
2793
2855
|
}
|
|
2794
2856
|
await deleteSite(credentials, found.siteId);
|
|
2795
2857
|
return report(
|
|
2796
|
-
`Deleted ${found.subdomain}. Its files and history are gone
|
|
2858
|
+
`Deleted ${found.subdomain}. Its files and history are gone; the subdomain stays reserved and cannot be used again.`,
|
|
2797
2859
|
{ siteId: found.siteId, subdomain: found.subdomain }
|
|
2798
2860
|
);
|
|
2799
2861
|
})
|
|
2800
2862
|
);
|
|
2863
|
+
server.registerTool(
|
|
2864
|
+
"change_subdomain",
|
|
2865
|
+
{
|
|
2866
|
+
...titled("Change a site's address", MOVES_AN_ADDRESS),
|
|
2867
|
+
description: "Moves a site to a new subdomain. The old one keeps working by redirecting to the new one, and nobody else can take it. Each site gets only a couple of changes, and a change back to an old name counts too, so use this only when the person named the new address. Requires `confirm` to repeat the site's current subdomain exactly; ask the person rather than filling it in. Visitors who signed in to the site or typed its password are asked again.",
|
|
2868
|
+
inputSchema: {
|
|
2869
|
+
site: SITE_INPUT,
|
|
2870
|
+
subdomain: z.string().min(1).describe("The new subdomain, as the person gave it."),
|
|
2871
|
+
confirm: z.string().min(1).describe(
|
|
2872
|
+
"The site's current subdomain, repeated exactly. A mismatch refuses the call rather than guessing which site was meant."
|
|
2873
|
+
)
|
|
2874
|
+
},
|
|
2875
|
+
outputSchema: {
|
|
2876
|
+
siteId: z.string().describe("Identifier of the site."),
|
|
2877
|
+
subdomain: z.string().describe("The subdomain it answers on now."),
|
|
2878
|
+
url: z.string().describe("Its address now."),
|
|
2879
|
+
subdomainChangesLeft: z.number().int().nullable().describe("How many more changes its plan allows, or null when it allows any number."),
|
|
2880
|
+
formerSubdomains: z.array(z.string()).describe("The subdomains it used to answer on, newest first; each redirects here.")
|
|
2881
|
+
}
|
|
2882
|
+
},
|
|
2883
|
+
({ site, subdomain, confirm }) => withCredentials(context, async (credentials) => {
|
|
2884
|
+
const found = await findSite(credentials, site);
|
|
2885
|
+
if (confirm.trim().toLowerCase() !== found.subdomain.toLowerCase()) {
|
|
2886
|
+
throw new Error(
|
|
2887
|
+
`Refusing to move ${found.subdomain}: \`confirm\` said "${confirm}". Repeat the current subdomain exactly to go ahead.`
|
|
2888
|
+
);
|
|
2889
|
+
}
|
|
2890
|
+
const changed = await changeSubdomain(credentials, found.siteId, subdomain);
|
|
2891
|
+
const left = changed.subdomainChangesLeft === null ? "" : ` ${changed.subdomainChangesLeft} address change(s) left.`;
|
|
2892
|
+
return report(
|
|
2893
|
+
`${found.subdomain} is now ${changed.url}. The old address redirects there.${left}`,
|
|
2894
|
+
{ ...changed, formerSubdomains: [...changed.formerSubdomains] }
|
|
2895
|
+
);
|
|
2896
|
+
})
|
|
2897
|
+
);
|
|
2801
2898
|
server.registerTool(
|
|
2802
2899
|
"list_sites",
|
|
2803
2900
|
{
|
|
@@ -2851,7 +2948,11 @@ function registerSiteTools(server, context) {
|
|
|
2851
2948
|
scheduledTakedown: z.boolean(),
|
|
2852
2949
|
comments: z.boolean()
|
|
2853
2950
|
}).describe("Which settings the account's plan includes."),
|
|
2854
|
-
versions: z.array(VERSION_OUTPUT).describe("Its versions, newest first.")
|
|
2951
|
+
versions: z.array(VERSION_OUTPUT).describe("Its versions, newest first."),
|
|
2952
|
+
subdomainChangesLeft: z.number().int().nullable().describe(
|
|
2953
|
+
"How many more times change_subdomain may move it, or null when there is no limit."
|
|
2954
|
+
),
|
|
2955
|
+
formerSubdomains: z.array(z.string()).describe("The subdomains it used to answer on, newest first; each redirects to it.")
|
|
2855
2956
|
}
|
|
2856
2957
|
},
|
|
2857
2958
|
({ site }) => withCredentials(context, async (credentials) => {
|
|
@@ -3261,7 +3362,7 @@ function createServer() {
|
|
|
3261
3362
|
path: z.string().min(1).describe("Absolute path of the folder to publish."),
|
|
3262
3363
|
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site."),
|
|
3263
3364
|
subdomain: z.string().optional().describe(
|
|
3264
|
-
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, it
|
|
3365
|
+
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, changing it later spends one of the few changes a site gets, and an unused site made under a guessed name has to be deleted by hand."
|
|
3265
3366
|
),
|
|
3266
3367
|
folder: FOLDER_INPUT
|
|
3267
3368
|
},
|
package/package.json
CHANGED