@shipstatic/mcp 1.0.0-beta.1 → 1.0.0-beta.10
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 +1 -1
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +42 -0
- package/dist/call.d.ts +50 -1
- package/dist/call.js +75 -14
- package/dist/index.d.ts +64 -2
- package/dist/index.js +64 -22
- package/dist/server.d.ts +34 -1
- package/dist/server.js +37 -204
- package/dist/tools.d.ts +53 -0
- package/dist/tools.js +219 -0
- package/dist/vocabulary.d.ts +183 -0
- package/dist/vocabulary.js +167 -0
- package/package.json +16 -4
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary both transports speak.
|
|
3
|
+
*
|
|
4
|
+
* `@shipstatic/mcp` (stdio) and the hosted Streamable-HTTP server are one
|
|
5
|
+
* product with two doors in. Everything an agent observes that is NOT forced
|
|
6
|
+
* apart by the transport lives here and is IMPORTED by both — because a fact
|
|
7
|
+
* with two owners is a fact that drifts. This pair kept ten such strings
|
|
8
|
+
* byte-identical by hand for a year, and the hand slipped: a tool description
|
|
9
|
+
* diverged unnoticed, a one-word correction had to be applied at three sites,
|
|
10
|
+
* and a test mock invented constraint numbers production never used. A
|
|
11
|
+
* coordination table written in prose is a specification for drift, not a
|
|
12
|
+
* defence against it.
|
|
13
|
+
*
|
|
14
|
+
* What belongs here: anything true of a ShipStatic deploy regardless of how
|
|
15
|
+
* the bytes arrived. What does not, and why:
|
|
16
|
+
*
|
|
17
|
+
* - **The file-input schema.** A filesystem path here, inline content there:
|
|
18
|
+
* Workers has no filesystem. Structurally forced apart.
|
|
19
|
+
* - **Tool descriptions**, as whole strings. They are deliberately rewritten
|
|
20
|
+
* hosted-side for an Apps-SDK caller that must be told not to base64-encode
|
|
21
|
+
* text — a failure mode the filesystem path does not have. What genuinely
|
|
22
|
+
* overlaps is exported as `DESCRIPTION_BLOCKS` and composed per transport.
|
|
23
|
+
* - **Anything Apps-SDK** (widget, `_meta`, `outputSchema`): hosted-only by
|
|
24
|
+
* nature.
|
|
25
|
+
*
|
|
26
|
+
* Each of those is recorded in `cloudflare/mcp/CLAUDE.md`'s divergence table.
|
|
27
|
+
* Everything else should be here, and adding a shared fact anywhere else is
|
|
28
|
+
* how the next year's drift starts.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Two packages, and the split is a rule rather than an accident: **read a
|
|
32
|
+
* constant from whatever will act on it.**
|
|
33
|
+
*
|
|
34
|
+
* The label, password and idempotency-key constraints come from
|
|
35
|
+
* `@shipstatic/ship` because the SDK is what validates a value against them
|
|
36
|
+
* before it reaches the wire — describing a bound the client in the same
|
|
37
|
+
* process will not honour is the drift that matters, and reading both from one
|
|
38
|
+
* module makes it impossible. `@shipstatic/types` declares them, but ship
|
|
39
|
+
* bundles its own copy, so importing them from types here would let a describe
|
|
40
|
+
* advertise a limit the validator beside it rejects.
|
|
41
|
+
*
|
|
42
|
+
* The public-deploy lifetime is the other kind of fact. Ship never reads it —
|
|
43
|
+
* the API stamps it — so there is no validator to agree with, and taking it
|
|
44
|
+
* from the package that merely forwards it would mean a ship release every
|
|
45
|
+
* time the platform's own vocabulary grows.
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* The server name every transport reports in `serverInfo`.
|
|
49
|
+
*
|
|
50
|
+
* Shared because it is not only prose: the Apps-SDK widget's bridge handshake
|
|
51
|
+
* sends `appInfo.name`, which the HOST correlates against `serverInfo.name` to
|
|
52
|
+
* tie the rendered view to the connector. Two literals kept equal by comment
|
|
53
|
+
* is exactly the shape this package exists to delete.
|
|
54
|
+
*/
|
|
55
|
+
export declare const SERVER_NAME = "shipstatic";
|
|
56
|
+
/**
|
|
57
|
+
* The one tool authored per transport — its INPUT differs (a filesystem path
|
|
58
|
+
* over stdio, inline bytes over HTTP), its NAME must not. Exported so the
|
|
59
|
+
* hosted parity fence can build the expected catalogue as
|
|
60
|
+
* `[UPLOAD_TOOL_NAME, ...ACCOUNT_TOOL_NAMES]` rather than counting to fifteen.
|
|
61
|
+
*/
|
|
62
|
+
export declare const UPLOAD_TOOL_NAME = "deployments_upload";
|
|
63
|
+
/**
|
|
64
|
+
* How long an anonymous deployment lives, in the words an agent reads.
|
|
65
|
+
*
|
|
66
|
+
* **Derived, since `@shipstatic/types@2.5.0-beta.19`.** It was a restatement
|
|
67
|
+
* until then, and deliberately the only one — the duration had appeared in
|
|
68
|
+
* eight places across the two servers and the widget, so a TTL change had to
|
|
69
|
+
* find all eight. Both halves of the fix landed together: types declares the
|
|
70
|
+
* number and `cloudflare/api` imports it back, because exporting without the
|
|
71
|
+
* import-back would have given the fact two owners instead of ending the
|
|
72
|
+
* duplication.
|
|
73
|
+
*
|
|
74
|
+
* A phrase rather than a number because every consumer is prose: the value has
|
|
75
|
+
* to carry its own unit, and dividing by 86400 at eight sites would restate the
|
|
76
|
+
* unit eight times instead of the number.
|
|
77
|
+
*
|
|
78
|
+
* The unit stays literal, and that is the one assumption here: this reads
|
|
79
|
+
* correctly while the TTL is a whole number of days, which it has always been.
|
|
80
|
+
* A TTL of hours would need the prose reviewed anyway — the widget's own
|
|
81
|
+
* `formatExpires` speaks in days and hours too — so the honest failure is a
|
|
82
|
+
* sentence someone must rewrite, not a number that silently rounds.
|
|
83
|
+
*/
|
|
84
|
+
export declare const PUBLIC_EXPIRY: string;
|
|
85
|
+
/**
|
|
86
|
+
* MCP tool annotations by kind of operation. An agent reads these to decide
|
|
87
|
+
* whether it may call speculatively (`readOnlyHint`), whether a retry is free
|
|
88
|
+
* (`idempotentHint`), and whether it must confirm with the user first
|
|
89
|
+
* (`destructiveHint`).
|
|
90
|
+
*
|
|
91
|
+
* **`CREATE` carries no `idempotentHint`, deliberately.** A deploy creates a
|
|
92
|
+
* new deployment on every call. `idempotencyKey` makes a retry replay the
|
|
93
|
+
* original instead — but that property is conditional on an argument the
|
|
94
|
+
* caller may not pass, while the annotation is static per tool. Advertising it
|
|
95
|
+
* would promise every agent that any retry is free, which is exactly false for
|
|
96
|
+
* the keyless caller, and an annotation an agent trusts wrongly is worse than
|
|
97
|
+
* one it never reads.
|
|
98
|
+
*/
|
|
99
|
+
export declare const ANNOTATIONS: {
|
|
100
|
+
readonly READ: {
|
|
101
|
+
readonly openWorldHint: true;
|
|
102
|
+
readonly readOnlyHint: true;
|
|
103
|
+
readonly destructiveHint: false;
|
|
104
|
+
readonly idempotentHint: true;
|
|
105
|
+
};
|
|
106
|
+
readonly CREATE: {
|
|
107
|
+
readonly openWorldHint: true;
|
|
108
|
+
readonly readOnlyHint: false;
|
|
109
|
+
readonly destructiveHint: false;
|
|
110
|
+
};
|
|
111
|
+
readonly WRITE: {
|
|
112
|
+
readonly openWorldHint: true;
|
|
113
|
+
readonly readOnlyHint: false;
|
|
114
|
+
readonly destructiveHint: false;
|
|
115
|
+
readonly idempotentHint: true;
|
|
116
|
+
};
|
|
117
|
+
readonly DESTRUCTIVE: {
|
|
118
|
+
readonly openWorldHint: true;
|
|
119
|
+
readonly readOnlyHint: false;
|
|
120
|
+
readonly destructiveHint: true;
|
|
121
|
+
readonly idempotentHint: true;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* INSTRUCTIONS sentences both transports say.
|
|
126
|
+
*
|
|
127
|
+
* `initialize`'s instructions are the other half of what an agent reads
|
|
128
|
+
* before acting (the catalogue is the first). Each transport composes its own
|
|
129
|
+
* from these blocks plus the two things that are genuinely its own: how files
|
|
130
|
+
* arrive, and how a caller authenticates.
|
|
131
|
+
*
|
|
132
|
+
* Three of these are duplicated prose TODAY, kept equal by review. The last
|
|
133
|
+
* two are stdio-only only because the hosted transport has no domain tools
|
|
134
|
+
* yet — when it gains them with OAuth they become shared too, which is
|
|
135
|
+
* precisely when someone would otherwise copy them across.
|
|
136
|
+
*/
|
|
137
|
+
export declare const INSTRUCTION_BLOCKS: {
|
|
138
|
+
readonly opening: "ShipStatic deploys static websites instantly. Free, no account required.";
|
|
139
|
+
readonly liveAndPassword: "The site is live immediately. To make the site private, pass `password` — visitors must unlock before viewing, including on any custom domains pointing at it.";
|
|
140
|
+
readonly claim: "The response includes a claim URL — always show the deployment URL and the claim URL to the user so they can keep the site permanently.";
|
|
141
|
+
readonly conceptsHeader: "Concepts:";
|
|
142
|
+
readonly deploymentConcept: "- Deployment: an immutable set of files with an instant URL (e.g. happy-cat-abc1234.shipstatic.com). No setup needed.";
|
|
143
|
+
readonly domainConcept: "- Domain: a custom domain (e.g. www.example.com) pointing to a deployment. Optional. Subdomains only — not apex domains.";
|
|
144
|
+
readonly domainWorkflow: "To add a custom domain: domains_validate → domains_set → domains_records (show DNS records to user) → user configures DNS → domains_verify.";
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* The fragments of the upload tool's description that both transports say.
|
|
148
|
+
*
|
|
149
|
+
* The surrounding descriptions diverge on purpose — hosted opens for an
|
|
150
|
+
* Apps-SDK caller and spends a paragraph on plain-text-vs-base64, stdio takes
|
|
151
|
+
* a filesystem path and has no such hazard — so what is shared is smaller than
|
|
152
|
+
* a sentence in one case and exactly a sentence in the other. Both were pinned
|
|
153
|
+
* by a `toContain` on each side, which meant three copies of each fragment
|
|
154
|
+
* (two sources and a test literal) held equal by nobody.
|
|
155
|
+
*
|
|
156
|
+
* Same reasoning as `INSTRUCTION_BLOCKS`, one altitude down: blocks are shared,
|
|
157
|
+
* composition is per transport.
|
|
158
|
+
*/
|
|
159
|
+
export declare const DESCRIPTION_BLOCKS: {
|
|
160
|
+
/** The no-account promise, mid-sentence in both openings. */
|
|
161
|
+
readonly free: "free, no account or API key required";
|
|
162
|
+
/** The password read-back rule — a password the user never sees locks them out. */
|
|
163
|
+
readonly password: "To make the site private, pass `password`; always show the password to the user if you set one.";
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Deploy-parameter descriptions shared by both transports.
|
|
167
|
+
*
|
|
168
|
+
* The numbers interpolate from `@shipstatic/types` rather than being written
|
|
169
|
+
* out, so a platform constraint change reaches every agent-facing string
|
|
170
|
+
* without anyone editing prose — the same reason the API and the SDK import
|
|
171
|
+
* them instead of restating them.
|
|
172
|
+
*/
|
|
173
|
+
export declare const PARAM_DESCRIPTIONS: {
|
|
174
|
+
readonly labels: "Labels for organizing deployments (e.g. [\"production\", \"v1.2\"]). Lowercase, 3-25 chars, allows . _ - separators. Up to 10.";
|
|
175
|
+
readonly password: "Optional password to gate the deployment behind an unlock prompt (6–128 characters; whitespace significant). Visitors must enter this password before viewing the site, including on any custom domains pointing at it.";
|
|
176
|
+
/**
|
|
177
|
+
* Shared even though only stdio offers the option today: the hosted door
|
|
178
|
+
* gains it with OAuth (it can scope a replay per user once callers have an
|
|
179
|
+
* identity), and the law this teaches — key the ATTEMPT, never the try — is
|
|
180
|
+
* the same one on both. The window is derived, never typed out.
|
|
181
|
+
*/
|
|
182
|
+
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.`;
|
|
183
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary both transports speak.
|
|
3
|
+
*
|
|
4
|
+
* `@shipstatic/mcp` (stdio) and the hosted Streamable-HTTP server are one
|
|
5
|
+
* product with two doors in. Everything an agent observes that is NOT forced
|
|
6
|
+
* apart by the transport lives here and is IMPORTED by both — because a fact
|
|
7
|
+
* with two owners is a fact that drifts. This pair kept ten such strings
|
|
8
|
+
* byte-identical by hand for a year, and the hand slipped: a tool description
|
|
9
|
+
* diverged unnoticed, a one-word correction had to be applied at three sites,
|
|
10
|
+
* and a test mock invented constraint numbers production never used. A
|
|
11
|
+
* coordination table written in prose is a specification for drift, not a
|
|
12
|
+
* defence against it.
|
|
13
|
+
*
|
|
14
|
+
* What belongs here: anything true of a ShipStatic deploy regardless of how
|
|
15
|
+
* the bytes arrived. What does not, and why:
|
|
16
|
+
*
|
|
17
|
+
* - **The file-input schema.** A filesystem path here, inline content there:
|
|
18
|
+
* Workers has no filesystem. Structurally forced apart.
|
|
19
|
+
* - **Tool descriptions**, as whole strings. They are deliberately rewritten
|
|
20
|
+
* hosted-side for an Apps-SDK caller that must be told not to base64-encode
|
|
21
|
+
* text — a failure mode the filesystem path does not have. What genuinely
|
|
22
|
+
* overlaps is exported as `DESCRIPTION_BLOCKS` and composed per transport.
|
|
23
|
+
* - **Anything Apps-SDK** (widget, `_meta`, `outputSchema`): hosted-only by
|
|
24
|
+
* nature.
|
|
25
|
+
*
|
|
26
|
+
* Each of those is recorded in `cloudflare/mcp/CLAUDE.md`'s divergence table.
|
|
27
|
+
* Everything else should be here, and adding a shared fact anywhere else is
|
|
28
|
+
* how the next year's drift starts.
|
|
29
|
+
*/
|
|
30
|
+
import { IDEMPOTENCY_KEY_CONSTRAINTS, LABEL_CONSTRAINTS, PASSWORD_CONSTRAINTS, } from '@shipstatic/ship';
|
|
31
|
+
import { PUBLIC_DEPLOYMENT_TTL_SECONDS } from '@shipstatic/types';
|
|
32
|
+
/**
|
|
33
|
+
* Two packages, and the split is a rule rather than an accident: **read a
|
|
34
|
+
* constant from whatever will act on it.**
|
|
35
|
+
*
|
|
36
|
+
* The label, password and idempotency-key constraints come from
|
|
37
|
+
* `@shipstatic/ship` because the SDK is what validates a value against them
|
|
38
|
+
* before it reaches the wire — describing a bound the client in the same
|
|
39
|
+
* process will not honour is the drift that matters, and reading both from one
|
|
40
|
+
* module makes it impossible. `@shipstatic/types` declares them, but ship
|
|
41
|
+
* bundles its own copy, so importing them from types here would let a describe
|
|
42
|
+
* advertise a limit the validator beside it rejects.
|
|
43
|
+
*
|
|
44
|
+
* The public-deploy lifetime is the other kind of fact. Ship never reads it —
|
|
45
|
+
* the API stamps it — so there is no validator to agree with, and taking it
|
|
46
|
+
* from the package that merely forwards it would mean a ship release every
|
|
47
|
+
* time the platform's own vocabulary grows.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* The server name every transport reports in `serverInfo`.
|
|
51
|
+
*
|
|
52
|
+
* Shared because it is not only prose: the Apps-SDK widget's bridge handshake
|
|
53
|
+
* sends `appInfo.name`, which the HOST correlates against `serverInfo.name` to
|
|
54
|
+
* tie the rendered view to the connector. Two literals kept equal by comment
|
|
55
|
+
* is exactly the shape this package exists to delete.
|
|
56
|
+
*/
|
|
57
|
+
export const SERVER_NAME = 'shipstatic';
|
|
58
|
+
/**
|
|
59
|
+
* The one tool authored per transport — its INPUT differs (a filesystem path
|
|
60
|
+
* over stdio, inline bytes over HTTP), its NAME must not. Exported so the
|
|
61
|
+
* hosted parity fence can build the expected catalogue as
|
|
62
|
+
* `[UPLOAD_TOOL_NAME, ...ACCOUNT_TOOL_NAMES]` rather than counting to fifteen.
|
|
63
|
+
*/
|
|
64
|
+
export const UPLOAD_TOOL_NAME = 'deployments_upload';
|
|
65
|
+
/**
|
|
66
|
+
* How long an anonymous deployment lives, in the words an agent reads.
|
|
67
|
+
*
|
|
68
|
+
* **Derived, since `@shipstatic/types@2.5.0-beta.19`.** It was a restatement
|
|
69
|
+
* until then, and deliberately the only one — the duration had appeared in
|
|
70
|
+
* eight places across the two servers and the widget, so a TTL change had to
|
|
71
|
+
* find all eight. Both halves of the fix landed together: types declares the
|
|
72
|
+
* number and `cloudflare/api` imports it back, because exporting without the
|
|
73
|
+
* import-back would have given the fact two owners instead of ending the
|
|
74
|
+
* duplication.
|
|
75
|
+
*
|
|
76
|
+
* A phrase rather than a number because every consumer is prose: the value has
|
|
77
|
+
* to carry its own unit, and dividing by 86400 at eight sites would restate the
|
|
78
|
+
* unit eight times instead of the number.
|
|
79
|
+
*
|
|
80
|
+
* The unit stays literal, and that is the one assumption here: this reads
|
|
81
|
+
* correctly while the TTL is a whole number of days, which it has always been.
|
|
82
|
+
* A TTL of hours would need the prose reviewed anyway — the widget's own
|
|
83
|
+
* `formatExpires` speaks in days and hours too — so the honest failure is a
|
|
84
|
+
* sentence someone must rewrite, not a number that silently rounds.
|
|
85
|
+
*/
|
|
86
|
+
export const PUBLIC_EXPIRY = `${PUBLIC_DEPLOYMENT_TTL_SECONDS / 86_400} days`;
|
|
87
|
+
const OPEN_WORLD = { openWorldHint: true };
|
|
88
|
+
/**
|
|
89
|
+
* MCP tool annotations by kind of operation. An agent reads these to decide
|
|
90
|
+
* whether it may call speculatively (`readOnlyHint`), whether a retry is free
|
|
91
|
+
* (`idempotentHint`), and whether it must confirm with the user first
|
|
92
|
+
* (`destructiveHint`).
|
|
93
|
+
*
|
|
94
|
+
* **`CREATE` carries no `idempotentHint`, deliberately.** A deploy creates a
|
|
95
|
+
* new deployment on every call. `idempotencyKey` makes a retry replay the
|
|
96
|
+
* original instead — but that property is conditional on an argument the
|
|
97
|
+
* caller may not pass, while the annotation is static per tool. Advertising it
|
|
98
|
+
* would promise every agent that any retry is free, which is exactly false for
|
|
99
|
+
* the keyless caller, and an annotation an agent trusts wrongly is worse than
|
|
100
|
+
* one it never reads.
|
|
101
|
+
*/
|
|
102
|
+
export const ANNOTATIONS = {
|
|
103
|
+
READ: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, ...OPEN_WORLD },
|
|
104
|
+
CREATE: { readOnlyHint: false, destructiveHint: false, ...OPEN_WORLD },
|
|
105
|
+
WRITE: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, ...OPEN_WORLD },
|
|
106
|
+
DESTRUCTIVE: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, ...OPEN_WORLD },
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* INSTRUCTIONS sentences both transports say.
|
|
110
|
+
*
|
|
111
|
+
* `initialize`'s instructions are the other half of what an agent reads
|
|
112
|
+
* before acting (the catalogue is the first). Each transport composes its own
|
|
113
|
+
* from these blocks plus the two things that are genuinely its own: how files
|
|
114
|
+
* arrive, and how a caller authenticates.
|
|
115
|
+
*
|
|
116
|
+
* Three of these are duplicated prose TODAY, kept equal by review. The last
|
|
117
|
+
* two are stdio-only only because the hosted transport has no domain tools
|
|
118
|
+
* yet — when it gains them with OAuth they become shared too, which is
|
|
119
|
+
* precisely when someone would otherwise copy them across.
|
|
120
|
+
*/
|
|
121
|
+
export const INSTRUCTION_BLOCKS = {
|
|
122
|
+
opening: 'ShipStatic deploys static websites instantly. Free, no account required.',
|
|
123
|
+
liveAndPassword: 'The site is live immediately. To make the site private, pass `password` — visitors must unlock before viewing, including on any custom domains pointing at it.',
|
|
124
|
+
claim: 'The response includes a claim URL — always show the deployment URL and the claim URL to the user so they can keep the site permanently.',
|
|
125
|
+
conceptsHeader: 'Concepts:',
|
|
126
|
+
deploymentConcept: '- Deployment: an immutable set of files with an instant URL (e.g. happy-cat-abc1234.shipstatic.com). No setup needed.',
|
|
127
|
+
domainConcept: '- Domain: a custom domain (e.g. www.example.com) pointing to a deployment. Optional. Subdomains only — not apex domains.',
|
|
128
|
+
domainWorkflow: 'To add a custom domain: domains_validate → domains_set → domains_records (show DNS records to user) → user configures DNS → domains_verify.',
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* The fragments of the upload tool's description that both transports say.
|
|
132
|
+
*
|
|
133
|
+
* The surrounding descriptions diverge on purpose — hosted opens for an
|
|
134
|
+
* Apps-SDK caller and spends a paragraph on plain-text-vs-base64, stdio takes
|
|
135
|
+
* a filesystem path and has no such hazard — so what is shared is smaller than
|
|
136
|
+
* a sentence in one case and exactly a sentence in the other. Both were pinned
|
|
137
|
+
* by a `toContain` on each side, which meant three copies of each fragment
|
|
138
|
+
* (two sources and a test literal) held equal by nobody.
|
|
139
|
+
*
|
|
140
|
+
* Same reasoning as `INSTRUCTION_BLOCKS`, one altitude down: blocks are shared,
|
|
141
|
+
* composition is per transport.
|
|
142
|
+
*/
|
|
143
|
+
export const DESCRIPTION_BLOCKS = {
|
|
144
|
+
/** The no-account promise, mid-sentence in both openings. */
|
|
145
|
+
free: 'free, no account or API key required',
|
|
146
|
+
/** The password read-back rule — a password the user never sees locks them out. */
|
|
147
|
+
password: 'To make the site private, pass `password`; always show the password to the user if you set one.',
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Deploy-parameter descriptions shared by both transports.
|
|
151
|
+
*
|
|
152
|
+
* The numbers interpolate from `@shipstatic/types` rather than being written
|
|
153
|
+
* out, so a platform constraint change reaches every agent-facing string
|
|
154
|
+
* without anyone editing prose — the same reason the API and the SDK import
|
|
155
|
+
* them instead of restating them.
|
|
156
|
+
*/
|
|
157
|
+
export const PARAM_DESCRIPTIONS = {
|
|
158
|
+
labels: `Labels for organizing deployments (e.g. ["production", "v1.2"]). Lowercase, ${LABEL_CONSTRAINTS.MIN_LENGTH}-${LABEL_CONSTRAINTS.MAX_LENGTH} chars, allows . _ - separators. Up to ${LABEL_CONSTRAINTS.MAX_COUNT}.`,
|
|
159
|
+
password: `Optional password to gate the deployment behind an unlock prompt (${PASSWORD_CONSTRAINTS.MIN_LENGTH}–${PASSWORD_CONSTRAINTS.MAX_LENGTH} characters; whitespace significant). Visitors must enter this password before viewing the site, including on any custom domains pointing at it.`,
|
|
160
|
+
/**
|
|
161
|
+
* Shared even though only stdio offers the option today: the hosted door
|
|
162
|
+
* gains it with OAuth (it can scope a replay per user once callers have an
|
|
163
|
+
* identity), and the law this teaches — key the ATTEMPT, never the try — is
|
|
164
|
+
* the same one on both. The window is derived, never typed out.
|
|
165
|
+
*/
|
|
166
|
+
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.`,
|
|
167
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipstatic/mcp",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.10",
|
|
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",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./dist/bin.js"
|
|
10
|
+
],
|
|
8
11
|
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
9
19
|
"bin": {
|
|
10
|
-
"shipstatic-mcp": "./dist/
|
|
20
|
+
"shipstatic-mcp": "./dist/bin.js"
|
|
11
21
|
},
|
|
12
22
|
"scripts": {
|
|
13
23
|
"build": "tsc",
|
|
@@ -17,6 +27,7 @@
|
|
|
17
27
|
"typecheck": "tsc -p tsconfig.check.json --noEmit",
|
|
18
28
|
"lint": "biome check .",
|
|
19
29
|
"format": "biome format --write .",
|
|
30
|
+
"smoke": "node smoke.mjs",
|
|
20
31
|
"coverage": "vitest --run --coverage"
|
|
21
32
|
},
|
|
22
33
|
"files": [
|
|
@@ -55,14 +66,15 @@
|
|
|
55
66
|
"license": "MIT",
|
|
56
67
|
"dependencies": {
|
|
57
68
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
58
|
-
"@shipstatic/ship": "2.0.0-beta.
|
|
69
|
+
"@shipstatic/ship": "2.0.0-beta.17",
|
|
70
|
+
"@shipstatic/types": "2.5.0-beta.21",
|
|
59
71
|
"zod": "^4.4.3"
|
|
60
72
|
},
|
|
61
73
|
"devDependencies": {
|
|
62
74
|
"@biomejs/biome": "2.5.5",
|
|
63
|
-
"@shipstatic/types": "2.5.0-beta.18",
|
|
64
75
|
"@types/node": "^25.9.5",
|
|
65
76
|
"@vitest/coverage-v8": "4.1.10",
|
|
77
|
+
"esbuild": "^0.25.12",
|
|
66
78
|
"typescript": "^6.0.3",
|
|
67
79
|
"vitest": "4.1.10"
|
|
68
80
|
},
|