@shipstatic/mcp 1.1.1 → 1.2.0-beta.1

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
@@ -137,6 +137,16 @@ The hosted endpoint exposes `deployments_upload` only. The local install exposes
137
137
 
138
138
  Key the *attempt*, not the try — a run id, a commit sha, or a uuid generated before the first call. A key that changes on every retry does nothing.
139
139
 
140
+ ### Deployments that clean themselves up
141
+
142
+ `deployments_upload` accepts a `ttl` in seconds. The deployment expires when the time is up and the platform reclaims it — handy for previews and throwaway iterations you would otherwise have to remember to delete.
143
+
144
+ ```json
145
+ { "path": "/path/to/dist", "ttl": 3600 }
146
+ ```
147
+
148
+ It needs `SHIP_TOKEN`: a keyless deploy already expires on the platform's schedule, so a `ttl` on one is refused rather than ignored. A deployment carrying a `ttl` cannot be linked to a custom domain — deploy without one when the site needs a domain.
149
+
140
150
  ## Registry
141
151
 
142
152
  Published to the [MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers?search=com.shipstatic/mcp) as `com.shipstatic/mcp`. Registry-aware clients see both the hosted endpoint and the local install and pick the right transport for their environment.
package/dist/server.js CHANGED
@@ -20,7 +20,7 @@ To deploy: call ${UPLOAD_TOOL_NAME} with the build output directory path. ${B.li
20
20
 
21
21
  Without SHIP_TOKEN, deployments are public and expire in ${PUBLIC_EXPIRY}. ${B.claim}
22
22
 
23
- With SHIP_TOKEN configured, deployments go to the user's account and never expire. Listing, managing, and domain operations also require SHIP_TOKEN.
23
+ With SHIP_TOKEN configured, deployments go to the user's account and never expire — pass \`ttl\` (seconds) to ${UPLOAD_TOOL_NAME} for one that expires on its own. Listing, managing, and domain operations also require SHIP_TOKEN.
24
24
 
25
25
  ${B.conceptsHeader}
26
26
  ${B.deploymentConcept}
@@ -50,8 +50,15 @@ export function createServer(ship, options) {
50
50
  labels: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.labels),
51
51
  password: z.string().optional().describe(PARAM_DESCRIPTIONS.password),
52
52
  idempotencyKey: z.string().optional().describe(PARAM_DESCRIPTIONS.idempotencyKey),
53
+ // A bare `z.number()`, and the absences are the point. `.min()`/`.max()`
54
+ // would restate `TTL_CONSTRAINTS`, and `.int()` would restate the
55
+ // fraction rule — all three owned by `validateTtl`, which the SDK runs
56
+ // in-process before a byte is uploaded and which answers in the
57
+ // constitution's own words. A second validator here could only ever
58
+ // disagree with the first, silently; its absence fails loudly instead.
59
+ ttl: z.number().optional().describe(PARAM_DESCRIPTIONS.ttl),
53
60
  },
54
- }, ({ path, labels, password, idempotencyKey }) => call(() => ship.deployments.upload(path, { labels, password, idempotencyKey, via })));
61
+ }, ({ path, labels, password, idempotencyKey, ttl }) => call(() => ship.deployments.upload(path, { labels, password, idempotencyKey, ttl, via })));
55
62
  // The other fourteen. Identical on every transport, so they live in the
56
63
  // shared package rather than here — see tools.ts for why upload is not
57
64
  // among them.
@@ -200,4 +200,20 @@ export declare const PARAM_DESCRIPTIONS: {
200
200
  * the same one on both. The window is derived, never typed out.
201
201
  */
202
202
  readonly idempotencyKey: `Makes this deploy replayable instead of repeatable. A deploy is not naturally idempotent: if a call times out you cannot tell "it never landed" from "it landed and the response was lost", and retrying creates a second deployment. Send the same key on the retry and the original deployment is replayed instead (within ${number} hours). Key the ATTEMPT \u2014 a run id, a commit sha, a uuid minted before the first try \u2014 never one minted fresh on each retry, which would defeat the point.`;
203
+ /**
204
+ * Shared one door ahead of its offer, for the same reason `idempotencyKey`
205
+ * is: only stdio declares `ttl` today (`cloudflare/mcp/CLAUDE.md`'s
206
+ * divergence table records the hosted deferral and its trigger), and the two
207
+ * refusals this teaches belong to the platform, so they read identically on
208
+ * every door.
209
+ *
210
+ * **Two things are deliberately absent, and both are the same rule.** The
211
+ * RANGE, because `@shipstatic/ship` validates it in-process before a byte is
212
+ * uploaded and relays the constitution's own sentence — a second copy here
213
+ * could only ever disagree with it. And the name of any credential: stdio
214
+ * owns `SHIP_TOKEN`, the hosted door owns "connect an account", and a SHARED
215
+ * string that named either would put one door's fact in the other's mouth.
216
+ * `tests/vocabulary.test.ts` fences the second half for every member.
217
+ */
218
+ readonly ttl: "Seconds until this deployment expires and the platform reclaims it; omit for one that never does. Only for authenticated deploys — an anonymous deployment already expires on the platform's schedule, and a requested ttl on one is refused. A deployment carrying a ttl cannot be linked to a custom domain: deploy without one if the site needs a domain.";
203
219
  };
@@ -184,4 +184,20 @@ export const PARAM_DESCRIPTIONS = {
184
184
  * the same one on both. The window is derived, never typed out.
185
185
  */
186
186
  idempotencyKey: `Makes this deploy replayable instead of repeatable. A deploy is not naturally idempotent: if a call times out you cannot tell "it never landed" from "it landed and the response was lost", and retrying creates a second deployment. Send the same key on the retry and the original deployment is replayed instead (within ${IDEMPOTENCY_KEY_CONSTRAINTS.WINDOW_SECONDS / 3600} hours). Key the ATTEMPT — a run id, a commit sha, a uuid minted before the first try — never one minted fresh on each retry, which would defeat the point.`,
187
+ /**
188
+ * Shared one door ahead of its offer, for the same reason `idempotencyKey`
189
+ * is: only stdio declares `ttl` today (`cloudflare/mcp/CLAUDE.md`'s
190
+ * divergence table records the hosted deferral and its trigger), and the two
191
+ * refusals this teaches belong to the platform, so they read identically on
192
+ * every door.
193
+ *
194
+ * **Two things are deliberately absent, and both are the same rule.** The
195
+ * RANGE, because `@shipstatic/ship` validates it in-process before a byte is
196
+ * uploaded and relays the constitution's own sentence — a second copy here
197
+ * could only ever disagree with it. And the name of any credential: stdio
198
+ * owns `SHIP_TOKEN`, the hosted door owns "connect an account", and a SHARED
199
+ * string that named either would put one door's fact in the other's mouth.
200
+ * `tests/vocabulary.test.ts` fences the second half for every member.
201
+ */
202
+ ttl: "Seconds until this deployment expires and the platform reclaims it; omit for one that never does. Only for authenticated deploys — an anonymous deployment already expires on the platform's schedule, and a requested ttl on one is refused. A deployment carrying a ttl cannot be linked to a custom domain: deploy without one if the site needs a domain.",
187
203
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/mcp",
3
- "version": "1.1.1",
3
+ "version": "1.2.0-beta.1",
4
4
  "mcpName": "com.shipstatic/mcp",
5
5
  "description": "ShipStatic MCP — deploy static websites from AI agents. Full toolset incl. custom domains. Free hosted endpoint at mcp.shipstatic.com — no install.",
6
6
  "type": "module",