@shipstatic/mcp 1.0.0-beta.0 → 1.0.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 +12 -2
- package/dist/server.js +47 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ The hosted endpoint exposes `deployments_upload` only. The local install exposes
|
|
|
102
102
|
| Tool | Description | Hosted |
|
|
103
103
|
|------|-------------|:---:|
|
|
104
104
|
| `deployments_upload` | Publish files and get a live URL instantly, optionally protected by a password | ✓ |
|
|
105
|
-
| `deployments_list` | List all deployments with their URLs, status, labels, and password protection state | |
|
|
105
|
+
| `deployments_list` | List all deployments with their URLs, status, labels, and password protection state. Pages with `limit` and `cursor` | |
|
|
106
106
|
| `deployments_get` | Get deployment details including URL, status, file count, size, labels, and password protection state | |
|
|
107
107
|
| `deployments_set` | Update the labels on a deployment for organization and filtering | |
|
|
108
108
|
| `deployments_delete` | Permanently delete a deployment and all its files | |
|
|
@@ -112,7 +112,7 @@ The hosted endpoint exposes `deployments_upload` only. The local install exposes
|
|
|
112
112
|
| Tool | Description |
|
|
113
113
|
|------|-------------|
|
|
114
114
|
| `domains_set` | Connect a custom domain to your site, switch deployments, or update labels |
|
|
115
|
-
| `domains_list` | List all domains with their linked deployment and verification status |
|
|
115
|
+
| `domains_list` | List all domains with their linked deployment and verification status. Pages with `limit` and `cursor` |
|
|
116
116
|
| `domains_get` | Get domain details including linked deployment, verification status, and labels |
|
|
117
117
|
| `domains_records` | Get the DNS records you need to configure at your DNS provider |
|
|
118
118
|
| `domains_dns` | Look up which DNS provider hosts a domain (e.g. Cloudflare, Namecheap) |
|
|
@@ -127,6 +127,16 @@ The hosted endpoint exposes `deployments_upload` only. The local install exposes
|
|
|
127
127
|
|------|-------------|
|
|
128
128
|
| `whoami` | Get your account details including email, plan, and usage |
|
|
129
129
|
|
|
130
|
+
### Paging
|
|
131
|
+
|
|
132
|
+
`deployments_list` and `domains_list` accept `limit` and `cursor`. Each response carries a `cursor` — pass it back to fetch the next page; `null` means you are on the last one.
|
|
133
|
+
|
|
134
|
+
### Retrying a deploy safely
|
|
135
|
+
|
|
136
|
+
`deployments_upload` accepts an `idempotencyKey`. If a deploy times out you cannot tell "it never landed" from "it landed and the response was lost", and retrying without a key creates a second site. Send the same key on the retry and the original deployment is returned instead.
|
|
137
|
+
|
|
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
|
+
|
|
130
140
|
## Registry
|
|
131
141
|
|
|
132
142
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { LABEL_CONSTRAINTS, PASSWORD_CONSTRAINTS } from '@shipstatic/ship';
|
|
3
|
+
import { IDEMPOTENCY_KEY_CONSTRAINTS, LABEL_CONSTRAINTS, PASSWORD_CONSTRAINTS, } from '@shipstatic/ship';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { call } from './call.js';
|
|
6
6
|
const { version } = createRequire(import.meta.url)('../package.json');
|
|
@@ -11,6 +11,15 @@ const READ = {
|
|
|
11
11
|
idempotentHint: true,
|
|
12
12
|
...OPEN_WORLD,
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* Deploys carry no `idempotentHint`, and that stays true now that
|
|
16
|
+
* `idempotencyKey` exists. The annotation is a STATIC per-tool claim; the
|
|
17
|
+
* property it would assert is per-CALL — true only when the caller supplies a
|
|
18
|
+
* key, false for the keyless caller, who is the common one. Advertising it
|
|
19
|
+
* would tell every agent that any retry is free, which is exactly wrong for
|
|
20
|
+
* the majority. An annotation an agent trusts wrongly is worse than one it
|
|
21
|
+
* never reads.
|
|
22
|
+
*/
|
|
14
23
|
const CREATE = { readOnlyHint: false, destructiveHint: false, ...OPEN_WORLD };
|
|
15
24
|
const WRITE = {
|
|
16
25
|
readOnlyHint: false,
|
|
@@ -24,6 +33,31 @@ const DESTRUCTIVE = {
|
|
|
24
33
|
idempotentHint: true,
|
|
25
34
|
...OPEN_WORLD,
|
|
26
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* The pagination surface, shared by every list tool because it is one
|
|
38
|
+
* contract, not two. A list answers `{<collection>, cursor}` and nothing
|
|
39
|
+
* else — `cursor` carries the whole has-more signal and is null on the last
|
|
40
|
+
* page, so there is no `total` to ask for and no has-more boolean.
|
|
41
|
+
*
|
|
42
|
+
* No upper bound is stated here on purpose. The API clamps an unusable
|
|
43
|
+
* `limit` server-side and owns that number; restating a cap in the tool
|
|
44
|
+
* schema would give one fact two owners and let them drift. `min(1)` is not
|
|
45
|
+
* a cap — it rejects a value that could never mean anything.
|
|
46
|
+
*/
|
|
47
|
+
const PAGINATION_INPUT = {
|
|
48
|
+
limit: z
|
|
49
|
+
.number()
|
|
50
|
+
.int()
|
|
51
|
+
.min(1)
|
|
52
|
+
.optional()
|
|
53
|
+
.describe('Maximum number of items to return in one page. Omit for the server default.'),
|
|
54
|
+
cursor: z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Opaque position from the previous response's `cursor` field; omit for the first page."),
|
|
58
|
+
};
|
|
59
|
+
/** Appended to every list tool's description — the paging contract, stated once. */
|
|
60
|
+
const PAGING_NOTE = " The response's `cursor` is null on the last page; pass it back as `cursor` to fetch the next.";
|
|
27
61
|
const INSTRUCTIONS = `ShipStatic deploys static websites instantly. Free, no account required.
|
|
28
62
|
|
|
29
63
|
To deploy: call deployments_upload with the build output directory path. 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.
|
|
@@ -55,17 +89,22 @@ export function createServer(ship) {
|
|
|
55
89
|
labels: z
|
|
56
90
|
.array(z.string())
|
|
57
91
|
.optional()
|
|
58
|
-
.describe(`Labels for organizing deployments (e.g. ["production", "v1.2"]). Lowercase, ${LABEL_CONSTRAINTS.MIN_LENGTH}-${LABEL_CONSTRAINTS.MAX_LENGTH} chars, allows . _ - separators.`),
|
|
92
|
+
.describe(`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}.`),
|
|
59
93
|
password: z
|
|
60
94
|
.string()
|
|
61
95
|
.optional()
|
|
62
96
|
.describe(`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.`),
|
|
97
|
+
idempotencyKey: z
|
|
98
|
+
.string()
|
|
99
|
+
.optional()
|
|
100
|
+
.describe(`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.`),
|
|
63
101
|
},
|
|
64
|
-
}, ({ path, labels, password }) => call(() => ship.deployments.upload(path, { labels, password, via: 'mcp' })));
|
|
102
|
+
}, ({ path, labels, password, idempotencyKey }) => call(() => ship.deployments.upload(path, { labels, password, idempotencyKey, via: 'mcp' })));
|
|
65
103
|
server.registerTool('deployments_list', {
|
|
66
|
-
description:
|
|
104
|
+
description: `List all deployments with their URLs, status, labels, and password protection state.${PAGING_NOTE}`,
|
|
67
105
|
annotations: READ,
|
|
68
|
-
|
|
106
|
+
inputSchema: PAGINATION_INPUT,
|
|
107
|
+
}, ({ limit, cursor }) => call(() => ship.deployments.list({ limit, cursor })));
|
|
69
108
|
server.registerTool('deployments_get', {
|
|
70
109
|
description: 'Get deployment details including URL, status, file count, size, labels, and password protection state.',
|
|
71
110
|
annotations: READ,
|
|
@@ -113,9 +152,10 @@ export function createServer(ship) {
|
|
|
113
152
|
},
|
|
114
153
|
}, ({ domain, deployment, labels }) => call(() => ship.domains.set(domain, { deployment, labels })));
|
|
115
154
|
server.registerTool('domains_list', {
|
|
116
|
-
description:
|
|
155
|
+
description: `List all domains with their URLs, linked deployment, and verification status.${PAGING_NOTE}`,
|
|
117
156
|
annotations: READ,
|
|
118
|
-
|
|
157
|
+
inputSchema: PAGINATION_INPUT,
|
|
158
|
+
}, ({ limit, cursor }) => call(() => ship.domains.list({ limit, cursor })));
|
|
119
159
|
server.registerTool('domains_get', {
|
|
120
160
|
description: 'Get domain details including URL, linked deployment, verification status, and labels.',
|
|
121
161
|
annotations: READ,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipstatic/mcp",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.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",
|