@shipstatic/mcp 0.7.0-beta.1 → 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 +14 -4
- package/dist/server.js +51 -11
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -102,24 +102,24 @@ 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 | |
|
|
109
109
|
|
|
110
110
|
### Domains
|
|
111
111
|
|
|
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) |
|
|
119
119
|
| `domains_share` | Get a shareable link so someone else can see the required DNS records |
|
|
120
120
|
| `domains_validate` | Check if a domain name is valid and available before connecting it |
|
|
121
121
|
| `domains_verify` | Check if DNS is configured correctly after you set up the records |
|
|
122
|
-
| `
|
|
122
|
+
| `domains_delete` | Permanently disconnect and delete a custom domain |
|
|
123
123
|
|
|
124
124
|
### Account
|
|
125
125
|
|
|
@@ -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,
|
|
@@ -87,7 +126,7 @@ export function createServer(ship) {
|
|
|
87
126
|
.describe('Labels to set. Replaces all existing labels. Pass empty array to clear.'),
|
|
88
127
|
},
|
|
89
128
|
}, ({ deployment, labels }) => call(() => ship.deployments.set(deployment, { labels })));
|
|
90
|
-
server.registerTool('
|
|
129
|
+
server.registerTool('deployments_delete', {
|
|
91
130
|
description: 'Permanently delete a deployment and its files. You MUST confirm with the user before calling this tool, referencing the deployment.',
|
|
92
131
|
annotations: DESTRUCTIVE,
|
|
93
132
|
inputSchema: {
|
|
@@ -95,7 +134,7 @@ export function createServer(ship) {
|
|
|
95
134
|
.string()
|
|
96
135
|
.describe('Deployment hostname to delete (e.g. "happy-cat-abc1234.shipstatic.com")'),
|
|
97
136
|
},
|
|
98
|
-
}, ({ deployment }) => call(() => ship.deployments.
|
|
137
|
+
}, ({ deployment }) => call(() => ship.deployments.delete(deployment)));
|
|
99
138
|
// Domains
|
|
100
139
|
server.registerTool('domains_set', {
|
|
101
140
|
description: 'Create or update a custom domain. Can reserve a name (omit deployment), link it to a deployment, switch deployments, or update labels. After creating, call domains_records and show the DNS records to the user.',
|
|
@@ -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,
|
|
@@ -170,13 +210,13 @@ export function createServer(ship) {
|
|
|
170
210
|
.describe('Domain name to verify DNS for. Must be a domain previously created with domains_set.'),
|
|
171
211
|
},
|
|
172
212
|
}, ({ domain }) => call(() => ship.domains.verify(domain)));
|
|
173
|
-
server.registerTool('
|
|
213
|
+
server.registerTool('domains_delete', {
|
|
174
214
|
description: 'Permanently delete a domain. You MUST confirm with the user before calling this tool, referencing the domain name.',
|
|
175
215
|
annotations: DESTRUCTIVE,
|
|
176
216
|
inputSchema: {
|
|
177
217
|
domain: z.string().describe('Domain name to delete (e.g. "www.example.com")'),
|
|
178
218
|
},
|
|
179
|
-
}, ({ domain }) => call(() => ship.domains.
|
|
219
|
+
}, ({ domain }) => call(() => ship.domains.delete(domain)));
|
|
180
220
|
// Debugging
|
|
181
221
|
server.registerTool('whoami', {
|
|
182
222
|
description: 'Show authenticated account details including email, plan, and usage.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipstatic/mcp",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
"author": "ShipStatic",
|
|
55
55
|
"license": "MIT",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
58
|
-
"@shipstatic/ship": "2.0.0-beta.
|
|
59
|
-
"zod": "^4.3
|
|
57
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
58
|
+
"@shipstatic/ship": "2.0.0-beta.15",
|
|
59
|
+
"zod": "^4.4.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@biomejs/biome": "2.5.5",
|
|
63
|
-
"@shipstatic/types": "2.5.0-beta.
|
|
64
|
-
"@types/node": "^25.
|
|
63
|
+
"@shipstatic/types": "2.5.0-beta.18",
|
|
64
|
+
"@types/node": "^25.9.5",
|
|
65
65
|
"@vitest/coverage-v8": "4.1.10",
|
|
66
|
-
"typescript": "^6.0.
|
|
66
|
+
"typescript": "^6.0.3",
|
|
67
67
|
"vitest": "4.1.10"
|
|
68
68
|
},
|
|
69
69
|
"packageManager": "pnpm@10.12.4"
|