@polypad/mcp 0.1.2 → 0.1.6
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 +9 -5
- package/dist/THIRD_PARTY_NOTICES.txt +260 -0
- package/dist/index.js +104 -52
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -6,15 +6,15 @@ The server runs on your own computer. Nothing is uploaded and there is no accoun
|
|
|
6
6
|
|
|
7
7
|
## Set up
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Claude Desktop, one click (no Node.js or terminal needed):** download [polypad.mcpb](https://polypad.ink/downloads/polypad.mcpb) (also behind **Connect AI** on polypad.ink) and open it; Claude Desktop shows an Install button. It runs this same server with Claude Desktop's built-in Node.
|
|
10
10
|
|
|
11
|
-
**Claude Code
|
|
11
|
+
**Claude Code and Cursor:** you need Node.js 22 or newer. `npx` downloads the server on first use.
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
14
|
claude mcp add polypad -- npx -y @polypad/mcp
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
**
|
|
17
|
+
**Cursor** (`.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` for all projects):
|
|
18
18
|
|
|
19
19
|
```json
|
|
20
20
|
{
|
|
@@ -27,7 +27,7 @@ claude mcp add polypad -- npx -y @polypad/mcp
|
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
The same block works in Claude Desktop's `claude_desktop_config.json` (Settings, Developer, Edit Config, then restart) if you prefer it to the extension.
|
|
31
31
|
|
|
32
32
|
## Connect
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ You can add Polypad to Claude Code and Claude Desktop (or Cursor) and use them a
|
|
|
44
44
|
|
|
45
45
|
## Troubleshooting
|
|
46
46
|
|
|
47
|
-
- **"Server not running"** in the dialog: your AI client has not started the server yet. Ask it something about Polypad, and check `npx -y @polypad/mcp --help` works (Node 22 or newer).
|
|
47
|
+
- **"Server not running"** in the dialog: your AI client has not started the server yet. Ask it something about Polypad, and check `npx -y @polypad/mcp --help` works (Node 22 or newer; the Claude Desktop extension needs neither).
|
|
48
48
|
- **"Wrong code"**: enter the code from the latest start. It changes whenever the connection's owner restarts.
|
|
49
49
|
- **"Port 41720 is already in use"** (the tools say so): a program that is not a Polypad MCP server (or an old 0.1.0 one) holds the port. Close it, or use `--port 41721` here and in the dialog's Advanced section. The server checks again every 2 seconds.
|
|
50
50
|
|
|
@@ -72,4 +72,8 @@ Lengths are in mm and angles in degrees. Sizes can be numbers or formulas over y
|
|
|
72
72
|
- The assistant sees and edits only the project you have open. Disconnect in the dialog cuts it off at any time.
|
|
73
73
|
- Polypad sends nothing to a server. What leaves your machine is what your AI client sends to its model provider: your prompts and the tool results.
|
|
74
74
|
|
|
75
|
+
## Licenses
|
|
76
|
+
|
|
77
|
+
Polypad MCP is MIT licensed. `dist/index.js` bundles a few open-source packages (the MCP SDK, ws, zod and their dependencies); their names, versions and full license texts are in `THIRD_PARTY_NOTICES.txt`, shipped next to it in this package.
|
|
78
|
+
|
|
75
79
|
More: [polypad.ink](https://polypad.ink). License: MIT.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
THIRD-PARTY NOTICES for @polypad/mcp
|
|
2
|
+
|
|
3
|
+
dist/index.js bundles the open-source packages listed below. Each one is under its own license; the license
|
|
4
|
+
texts are reproduced in full as the packages ship them.
|
|
5
|
+
|
|
6
|
+
@modelcontextprotocol/sdk@1.32.1 (MIT)
|
|
7
|
+
ajv@8.20.0 (MIT)
|
|
8
|
+
ajv-formats@3.0.1 (MIT)
|
|
9
|
+
fast-deep-equal@3.1.3 (MIT)
|
|
10
|
+
fast-uri@3.1.8 (BSD-3-Clause)
|
|
11
|
+
json-schema-traverse@1.0.0 (MIT)
|
|
12
|
+
ws@8.22.0 (MIT)
|
|
13
|
+
zod@4.6.5 (MIT)
|
|
14
|
+
zod-to-json-schema@3.25.2 (ISC)
|
|
15
|
+
|
|
16
|
+
==============================================================================
|
|
17
|
+
@modelcontextprotocol/sdk@1.32.1
|
|
18
|
+
License: MIT
|
|
19
|
+
Source: https://modelcontextprotocol.io
|
|
20
|
+
|
|
21
|
+
MIT License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2024 Anthropic, PBC
|
|
24
|
+
|
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
26
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
27
|
+
in the Software without restriction, including without limitation the rights
|
|
28
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
29
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
30
|
+
furnished to do so, subject to the following conditions:
|
|
31
|
+
|
|
32
|
+
The above copyright notice and this permission notice shall be included in all
|
|
33
|
+
copies or substantial portions of the Software.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
41
|
+
SOFTWARE.
|
|
42
|
+
|
|
43
|
+
==============================================================================
|
|
44
|
+
ajv@8.20.0
|
|
45
|
+
License: MIT
|
|
46
|
+
Source: https://ajv.js.org
|
|
47
|
+
|
|
48
|
+
The MIT License (MIT)
|
|
49
|
+
|
|
50
|
+
Copyright (c) 2015-2021 Evgeny Poberezkin
|
|
51
|
+
|
|
52
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
53
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
54
|
+
in the Software without restriction, including without limitation the rights
|
|
55
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
56
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
57
|
+
furnished to do so, subject to the following conditions:
|
|
58
|
+
|
|
59
|
+
The above copyright notice and this permission notice shall be included in all
|
|
60
|
+
copies or substantial portions of the Software.
|
|
61
|
+
|
|
62
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
63
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
64
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
65
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
66
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
67
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
68
|
+
SOFTWARE.
|
|
69
|
+
|
|
70
|
+
==============================================================================
|
|
71
|
+
ajv-formats@3.0.1
|
|
72
|
+
License: MIT
|
|
73
|
+
Source: https://github.com/ajv-validator/ajv-formats#readme
|
|
74
|
+
|
|
75
|
+
MIT License
|
|
76
|
+
|
|
77
|
+
Copyright (c) 2020 Evgeny Poberezkin
|
|
78
|
+
|
|
79
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
80
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
81
|
+
in the Software without restriction, including without limitation the rights
|
|
82
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
83
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
84
|
+
furnished to do so, subject to the following conditions:
|
|
85
|
+
|
|
86
|
+
The above copyright notice and this permission notice shall be included in all
|
|
87
|
+
copies or substantial portions of the Software.
|
|
88
|
+
|
|
89
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
90
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
91
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
92
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
93
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
94
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
95
|
+
SOFTWARE.
|
|
96
|
+
|
|
97
|
+
==============================================================================
|
|
98
|
+
fast-deep-equal@3.1.3
|
|
99
|
+
License: MIT
|
|
100
|
+
Source: https://github.com/epoberezkin/fast-deep-equal#readme
|
|
101
|
+
|
|
102
|
+
MIT License
|
|
103
|
+
|
|
104
|
+
Copyright (c) 2017 Evgeny Poberezkin
|
|
105
|
+
|
|
106
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
107
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
108
|
+
in the Software without restriction, including without limitation the rights
|
|
109
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
110
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
111
|
+
furnished to do so, subject to the following conditions:
|
|
112
|
+
|
|
113
|
+
The above copyright notice and this permission notice shall be included in all
|
|
114
|
+
copies or substantial portions of the Software.
|
|
115
|
+
|
|
116
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
117
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
118
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
119
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
120
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
121
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
122
|
+
SOFTWARE.
|
|
123
|
+
|
|
124
|
+
==============================================================================
|
|
125
|
+
fast-uri@3.1.8
|
|
126
|
+
License: BSD-3-Clause
|
|
127
|
+
Source: https://github.com/fastify/fast-uri
|
|
128
|
+
|
|
129
|
+
Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
|
|
130
|
+
Copyright (c) 2021-present The Fastify team <https://github.com/fastify/fastify#team>
|
|
131
|
+
All rights reserved.
|
|
132
|
+
|
|
133
|
+
Redistribution and use in source and binary forms, with or without
|
|
134
|
+
modification, are permitted provided that the following conditions are met:
|
|
135
|
+
* Redistributions of source code must retain the above copyright
|
|
136
|
+
notice, this list of conditions and the following disclaimer.
|
|
137
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
138
|
+
notice, this list of conditions and the following disclaimer in the
|
|
139
|
+
documentation and/or other materials provided with the distribution.
|
|
140
|
+
* The names of any contributors may not be used to endorse or promote
|
|
141
|
+
products derived from this software without specific prior written
|
|
142
|
+
permission.
|
|
143
|
+
|
|
144
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
145
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
146
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
147
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
148
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
149
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
150
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
151
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
152
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
153
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
154
|
+
|
|
155
|
+
* * *
|
|
156
|
+
|
|
157
|
+
The complete list of contributors can be found at:
|
|
158
|
+
- https://github.com/garycourt/uri-js/graphs/contributors
|
|
159
|
+
|
|
160
|
+
==============================================================================
|
|
161
|
+
json-schema-traverse@1.0.0
|
|
162
|
+
License: MIT
|
|
163
|
+
Source: https://github.com/epoberezkin/json-schema-traverse#readme
|
|
164
|
+
|
|
165
|
+
MIT License
|
|
166
|
+
|
|
167
|
+
Copyright (c) 2017 Evgeny Poberezkin
|
|
168
|
+
|
|
169
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
170
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
171
|
+
in the Software without restriction, including without limitation the rights
|
|
172
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
173
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
174
|
+
furnished to do so, subject to the following conditions:
|
|
175
|
+
|
|
176
|
+
The above copyright notice and this permission notice shall be included in all
|
|
177
|
+
copies or substantial portions of the Software.
|
|
178
|
+
|
|
179
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
180
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
181
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
182
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
183
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
184
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
185
|
+
SOFTWARE.
|
|
186
|
+
|
|
187
|
+
==============================================================================
|
|
188
|
+
ws@8.22.0
|
|
189
|
+
License: MIT
|
|
190
|
+
Source: https://github.com/websockets/ws
|
|
191
|
+
|
|
192
|
+
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
|
193
|
+
Copyright (c) 2013 Arnout Kazemier and contributors
|
|
194
|
+
Copyright (c) 2016 Luigi Pinca and contributors
|
|
195
|
+
|
|
196
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
197
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
198
|
+
the Software without restriction, including without limitation the rights to
|
|
199
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
200
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
201
|
+
subject to the following conditions:
|
|
202
|
+
|
|
203
|
+
The above copyright notice and this permission notice shall be included in all
|
|
204
|
+
copies or substantial portions of the Software.
|
|
205
|
+
|
|
206
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
207
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
208
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
209
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
210
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
211
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
212
|
+
|
|
213
|
+
==============================================================================
|
|
214
|
+
zod@4.6.5
|
|
215
|
+
License: MIT
|
|
216
|
+
Source: https://zod.dev
|
|
217
|
+
|
|
218
|
+
MIT License
|
|
219
|
+
|
|
220
|
+
Copyright (c) 2025 Colin McDonnell
|
|
221
|
+
|
|
222
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
223
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
224
|
+
in the Software without restriction, including without limitation the rights
|
|
225
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
226
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
227
|
+
furnished to do so, subject to the following conditions:
|
|
228
|
+
|
|
229
|
+
The above copyright notice and this permission notice shall be included in all
|
|
230
|
+
copies or substantial portions of the Software.
|
|
231
|
+
|
|
232
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
233
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
234
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
235
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
236
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
237
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
238
|
+
SOFTWARE.
|
|
239
|
+
|
|
240
|
+
==============================================================================
|
|
241
|
+
zod-to-json-schema@3.25.2
|
|
242
|
+
License: ISC
|
|
243
|
+
Source: https://github.com/StefanTerdell/zod-to-json-schema
|
|
244
|
+
|
|
245
|
+
ISC License
|
|
246
|
+
|
|
247
|
+
Copyright (c) 2020, Stefan Terdell
|
|
248
|
+
|
|
249
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
250
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
251
|
+
copyright notice and this permission notice appear in all copies.
|
|
252
|
+
|
|
253
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
254
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
255
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
256
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
257
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
258
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
259
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
260
|
+
|
package/dist/index.js
CHANGED
|
@@ -34241,7 +34241,7 @@ var rawSketchShape = { ...sketchShape, constraints: external_exports.array(RawSk
|
|
|
34241
34241
|
var SketchSchema = external_exports.object(sketchShape).superRefine(validateSketch);
|
|
34242
34242
|
|
|
34243
34243
|
// ../../packages/document/src/project.ts
|
|
34244
|
-
var SCHEMA_VERSION =
|
|
34244
|
+
var SCHEMA_VERSION = 18;
|
|
34245
34245
|
var MAX_SNAPSHOT_NAME = 80;
|
|
34246
34246
|
var MAX_PATTERN_COUNT = 100;
|
|
34247
34247
|
var MAX_PATTERN_COPIES = 400;
|
|
@@ -34269,7 +34269,10 @@ function makeSchemas(v, constraint) {
|
|
|
34269
34269
|
external_exports.object({ kind: external_exports.literal("box"), width: pos, depth: pos, height: pos }),
|
|
34270
34270
|
external_exports.object({ kind: external_exports.literal("cylinder"), radius: pos, height: pos }),
|
|
34271
34271
|
external_exports.object({ kind: external_exports.literal("sphere"), radius: pos }),
|
|
34272
|
-
external_exports.object({ kind: external_exports.literal("cone"), radius: pos, topRadius: nonneg, height: pos })
|
|
34272
|
+
external_exports.object({ kind: external_exports.literal("cone"), radius: pos, topRadius: nonneg, height: pos }),
|
|
34273
|
+
external_exports.object({ kind: external_exports.literal("torus"), radius: pos, tubeRadius: pos }),
|
|
34274
|
+
external_exports.object({ kind: external_exports.literal("tube"), radius: pos, innerRadius: pos, height: pos }),
|
|
34275
|
+
external_exports.object({ kind: external_exports.literal("wedge"), width: pos, depth: pos, height: pos, topWidth: nonneg })
|
|
34273
34276
|
]);
|
|
34274
34277
|
const PrimitiveFeatureSchema = external_exports.object({
|
|
34275
34278
|
...featureBase,
|
|
@@ -34333,37 +34336,43 @@ function makeSchemas(v, constraint) {
|
|
|
34333
34336
|
});
|
|
34334
34337
|
const direction = external_exports.object({ axis: external_exports.enum(["x", "y", "z"]), flip: external_exports.boolean().default(false) });
|
|
34335
34338
|
const count = v(external_exports.number().int().min(2).max(MAX_PATTERN_COUNT));
|
|
34339
|
+
const LayoutSchema = external_exports.discriminatedUnion("kind", [
|
|
34340
|
+
external_exports.object({
|
|
34341
|
+
kind: external_exports.literal("linear"),
|
|
34342
|
+
direction,
|
|
34343
|
+
count,
|
|
34344
|
+
spacing: pos,
|
|
34345
|
+
second: external_exports.object({ direction, count, spacing: pos }).optional()
|
|
34346
|
+
}),
|
|
34347
|
+
external_exports.object({
|
|
34348
|
+
kind: external_exports.literal("circular"),
|
|
34349
|
+
axis: external_exports.enum(["x", "y", "z"]),
|
|
34350
|
+
centre: vec32,
|
|
34351
|
+
count,
|
|
34352
|
+
// 360 spreads the copies evenly around; less puts the last copy at this angle.
|
|
34353
|
+
angle: v(external_exports.number().gt(0).max(360)).default(360)
|
|
34354
|
+
})
|
|
34355
|
+
]);
|
|
34356
|
+
const gridWithinLimit = (f) => {
|
|
34357
|
+
const layout = f.layout;
|
|
34358
|
+
if (layout.kind !== "linear" || !layout.second) return true;
|
|
34359
|
+
const [a, b] = [layout.count, layout.second.count];
|
|
34360
|
+
return typeof a !== "number" || typeof b !== "number" || a * b <= MAX_PATTERN_COPIES;
|
|
34361
|
+
};
|
|
34362
|
+
const gridIssue = { path: ["layout", "second", "count"], message: `A grid can have at most ${MAX_PATTERN_COPIES} copies (count x second count)` };
|
|
34336
34363
|
const PatternFeatureSchema = external_exports.object({
|
|
34337
34364
|
...featureBase,
|
|
34338
34365
|
type: external_exports.literal("pattern"),
|
|
34339
34366
|
body: external_exports.string(),
|
|
34340
34367
|
result: external_exports.enum(["join", "separate"]).default("join"),
|
|
34341
|
-
layout:
|
|
34342
|
-
|
|
34343
|
-
|
|
34344
|
-
|
|
34345
|
-
|
|
34346
|
-
|
|
34347
|
-
|
|
34348
|
-
|
|
34349
|
-
external_exports.object({
|
|
34350
|
-
kind: external_exports.literal("circular"),
|
|
34351
|
-
axis: external_exports.enum(["x", "y", "z"]),
|
|
34352
|
-
centre: vec32,
|
|
34353
|
-
count,
|
|
34354
|
-
// 360 spreads the copies evenly around; less puts the last copy at this angle.
|
|
34355
|
-
angle: v(external_exports.number().gt(0).max(360)).default(360)
|
|
34356
|
-
})
|
|
34357
|
-
])
|
|
34358
|
-
}).refine(
|
|
34359
|
-
(f) => {
|
|
34360
|
-
const layout = f.layout;
|
|
34361
|
-
if (layout.kind !== "linear" || !layout.second) return true;
|
|
34362
|
-
const [a, b] = [layout.count, layout.second.count];
|
|
34363
|
-
return typeof a !== "number" || typeof b !== "number" || a * b <= MAX_PATTERN_COPIES;
|
|
34364
|
-
},
|
|
34365
|
-
{ path: ["layout", "second", "count"], message: `A grid can have at most ${MAX_PATTERN_COPIES} copies (count x second count)` }
|
|
34366
|
-
);
|
|
34368
|
+
layout: LayoutSchema
|
|
34369
|
+
}).refine(gridWithinLimit, gridIssue);
|
|
34370
|
+
const FeaturePatternSchema = external_exports.object({
|
|
34371
|
+
...featureBase,
|
|
34372
|
+
type: external_exports.literal("featurePattern"),
|
|
34373
|
+
feature: external_exports.string(),
|
|
34374
|
+
layout: LayoutSchema
|
|
34375
|
+
}).refine(gridWithinLimit, gridIssue);
|
|
34367
34376
|
const MirrorFeatureSchema = external_exports.object({
|
|
34368
34377
|
...featureBase,
|
|
34369
34378
|
type: external_exports.literal("mirror"),
|
|
@@ -34429,6 +34438,7 @@ function makeSchemas(v, constraint) {
|
|
|
34429
34438
|
HoleFeatureSchema,
|
|
34430
34439
|
TransformFeatureSchema,
|
|
34431
34440
|
PatternFeatureSchema,
|
|
34441
|
+
FeaturePatternSchema,
|
|
34432
34442
|
MirrorFeatureSchema,
|
|
34433
34443
|
CopyFeatureSchema,
|
|
34434
34444
|
ImportFeatureSchema,
|
|
@@ -34449,6 +34459,15 @@ var modelShape = {
|
|
|
34449
34459
|
features: external_exports.record(external_exports.string(), FeatureSchema),
|
|
34450
34460
|
order: external_exports.array(external_exports.string())
|
|
34451
34461
|
};
|
|
34462
|
+
function holePatternProblem(model, feature) {
|
|
34463
|
+
const source = model.features[feature.feature];
|
|
34464
|
+
if (!source) return "The hole this pattern repeats was deleted";
|
|
34465
|
+
if (source.type !== "hole") return `${source.name} is not a hole; a hole pattern repeats a hole`;
|
|
34466
|
+
const at = model.order.indexOf(feature.id);
|
|
34467
|
+
const from = model.order.indexOf(source.id);
|
|
34468
|
+
if (from < 0 || at >= 0 && from > at) return `${source.name} comes after this pattern; a hole pattern repeats an earlier hole`;
|
|
34469
|
+
return null;
|
|
34470
|
+
}
|
|
34452
34471
|
function checkModel(model, ctx) {
|
|
34453
34472
|
const ids = Object.keys(model.features);
|
|
34454
34473
|
if (new Set(model.order).size !== model.order.length) {
|
|
@@ -34461,6 +34480,11 @@ function checkModel(model, ctx) {
|
|
|
34461
34480
|
if (model.features[id].id !== id) {
|
|
34462
34481
|
ctx.addIssue({ code: "custom", path: ["features", id], message: "Feature id mismatch" });
|
|
34463
34482
|
}
|
|
34483
|
+
const feature = model.features[id];
|
|
34484
|
+
if (feature.type === "featurePattern") {
|
|
34485
|
+
const problem = holePatternProblem(model, feature);
|
|
34486
|
+
if (problem) ctx.addIssue({ code: "custom", path: ["features", id, "feature"], message: problem });
|
|
34487
|
+
}
|
|
34464
34488
|
}
|
|
34465
34489
|
}
|
|
34466
34490
|
var SnapshotSchema = external_exports.object({
|
|
@@ -34654,6 +34678,26 @@ var patternDirection = external_exports.object({
|
|
|
34654
34678
|
axis: external_exports.enum(["x", "y", "z"]).describe("World axis the copies step along"),
|
|
34655
34679
|
flip: external_exports.boolean().default(false).describe("True steps along the negative axis")
|
|
34656
34680
|
});
|
|
34681
|
+
var patternLayout = external_exports.discriminatedUnion("kind", [
|
|
34682
|
+
external_exports.object({
|
|
34683
|
+
kind: external_exports.literal("linear"),
|
|
34684
|
+
direction: patternDirection,
|
|
34685
|
+
count: external_exports.union([external_exports.number().int().min(2).max(100), external_exports.string().min(1)]).describe("Number of copies in a row, including the original (2 to 100)"),
|
|
34686
|
+
spacing: size("Distance between copies"),
|
|
34687
|
+
second: external_exports.object({
|
|
34688
|
+
direction: patternDirection,
|
|
34689
|
+
count: external_exports.union([external_exports.number().int().min(2).max(100), external_exports.string().min(1)]).describe("Rows in the second direction, including the original"),
|
|
34690
|
+
spacing: size("Distance between rows")
|
|
34691
|
+
}).optional().describe("A second direction makes a grid (at most 400 copies in all)")
|
|
34692
|
+
}),
|
|
34693
|
+
external_exports.object({
|
|
34694
|
+
kind: external_exports.literal("circular"),
|
|
34695
|
+
axis: external_exports.enum(["x", "y", "z"]).describe("World axis (through `centre`) the copies turn about"),
|
|
34696
|
+
centre: vec3("A point on the axis"),
|
|
34697
|
+
count: external_exports.union([external_exports.number().int().min(2).max(100), external_exports.string().min(1)]).describe("Number of copies including the original"),
|
|
34698
|
+
angle: external_exports.union([external_exports.number().gt(0).max(360), external_exports.string().min(1)]).default(360).describe("360 spreads the copies evenly around the full circle; less puts the last copy at this angle (degrees)")
|
|
34699
|
+
})
|
|
34700
|
+
]).describe("How the copies are laid out. Counts include the original");
|
|
34657
34701
|
var inputs = {
|
|
34658
34702
|
new_project: external_exports.object({
|
|
34659
34703
|
name: external_exports.string().trim().min(1).max(120).default("Untitled").describe("Name of the new project"),
|
|
@@ -34673,8 +34717,28 @@ var inputs = {
|
|
|
34673
34717
|
radius: size("Radius of the base"),
|
|
34674
34718
|
topRadius: external_exports.union([external_exports.number().nonnegative(), external_exports.string().min(1)]).describe(`Radius of the top in mm, 0 for a point${EXPR}`),
|
|
34675
34719
|
height: size("Height along Z")
|
|
34720
|
+
}),
|
|
34721
|
+
external_exports.object({
|
|
34722
|
+
kind: external_exports.literal("tube"),
|
|
34723
|
+
radius: size("Outer radius"),
|
|
34724
|
+
innerRadius: size("Inner radius, smaller than the outer radius"),
|
|
34725
|
+
height: size("Height along Z")
|
|
34726
|
+
}),
|
|
34727
|
+
external_exports.object({
|
|
34728
|
+
kind: external_exports.literal("torus"),
|
|
34729
|
+
radius: size("Distance from the centre to the middle of the ring's tube"),
|
|
34730
|
+
tubeRadius: size("Radius of the tube, smaller than the ring radius")
|
|
34731
|
+
}),
|
|
34732
|
+
external_exports.object({
|
|
34733
|
+
kind: external_exports.literal("wedge"),
|
|
34734
|
+
width: size("Size of the base along X"),
|
|
34735
|
+
depth: size("Size along Y"),
|
|
34736
|
+
height: size("Height along Z, at the tall side"),
|
|
34737
|
+
topWidth: external_exports.union([external_exports.number().nonnegative(), external_exports.string().min(1)]).describe(`Width of the flat top in mm, 0 for a sharp ramp edge, the width for a box${EXPR}`)
|
|
34676
34738
|
})
|
|
34677
|
-
]).describe(
|
|
34739
|
+
]).describe(
|
|
34740
|
+
"The solid to add. A primitive stands on the XY plane: its base is centred on `position` and it grows upwards along Z. A torus stands on the XY plane too (it spans z 0 to twice its tube radius). A wedge is tallest at its -X side and slopes down to +X; its top is `topWidth` wide."
|
|
34741
|
+
),
|
|
34678
34742
|
position: vec3("Where the centre of the base goes").default([0, 0, 0]),
|
|
34679
34743
|
rotation: vec3("Rotation about X, then Y, then Z, around the base centre", "degrees").default([0, 0, 0])
|
|
34680
34744
|
}),
|
|
@@ -34739,26 +34803,11 @@ var inputs = {
|
|
|
34739
34803
|
pattern: external_exports.object({
|
|
34740
34804
|
body: bodyId,
|
|
34741
34805
|
result: external_exports.enum(["join", "separate"]).default("join").describe("join fuses the copies into the body; separate makes each copy its own body"),
|
|
34742
|
-
layout:
|
|
34743
|
-
|
|
34744
|
-
|
|
34745
|
-
|
|
34746
|
-
|
|
34747
|
-
spacing: size("Distance between copies"),
|
|
34748
|
-
second: external_exports.object({
|
|
34749
|
-
direction: patternDirection,
|
|
34750
|
-
count: external_exports.union([external_exports.number().int().min(2).max(100), external_exports.string().min(1)]).describe("Rows in the second direction, including the original"),
|
|
34751
|
-
spacing: size("Distance between rows")
|
|
34752
|
-
}).optional().describe("A second direction makes a grid (at most 400 copies in all)")
|
|
34753
|
-
}),
|
|
34754
|
-
external_exports.object({
|
|
34755
|
-
kind: external_exports.literal("circular"),
|
|
34756
|
-
axis: external_exports.enum(["x", "y", "z"]).describe("World axis (through `centre`) the copies turn about"),
|
|
34757
|
-
centre: vec3("A point on the axis"),
|
|
34758
|
-
count: external_exports.union([external_exports.number().int().min(2).max(100), external_exports.string().min(1)]).describe("Number of copies including the original"),
|
|
34759
|
-
angle: external_exports.union([external_exports.number().gt(0).max(360), external_exports.string().min(1)]).default(360).describe("360 spreads the copies evenly around the full circle; less puts the last copy at this angle (degrees)")
|
|
34760
|
-
})
|
|
34761
|
-
]).describe("How the copies are laid out")
|
|
34806
|
+
layout: patternLayout
|
|
34807
|
+
}),
|
|
34808
|
+
pattern_hole: external_exports.object({
|
|
34809
|
+
hole: featureId.describe("Id of the hole to repeat (returned by hole, or listed by get_project). Its copies are cut into the same body"),
|
|
34810
|
+
layout: patternLayout
|
|
34762
34811
|
}),
|
|
34763
34812
|
mirror: external_exports.object({
|
|
34764
34813
|
body: bodyId,
|
|
@@ -34810,7 +34859,7 @@ var inputs = {
|
|
|
34810
34859
|
var descriptions = {
|
|
34811
34860
|
new_project: "Start a new, empty Polypad project (optionally from a template), replacing the open one. All lengths in Polypad are millimetres and angles are degrees; Z is up.",
|
|
34812
34861
|
set_parameter: "Create or change a named parameter (a number or an expression). Any length field of any feature can use the name, so one change resizes everything bound to it. Reports features that no longer build. Set the sizes up front, then use the names in fields.",
|
|
34813
|
-
add_primitive: "Add a solid box, cylinder, sphere or
|
|
34862
|
+
add_primitive: "Add a solid box, cylinder, tube, sphere, cone, torus or wedge as a new body. Returns the new feature's id, which is also the body id for later commands. Sizes in mm.",
|
|
34814
34863
|
add_sketch: "Add a 2D sketch on a base plane or on a flat face and draw shapes in it (rectangle, circle, line, polyline, arc, slot, polygon). The sketch is solved and its remaining degrees of freedom reported. Use extrude or revolve to turn closed shapes into a solid.",
|
|
34815
34864
|
extrude: "Extrude the closed shapes of a sketch into a solid: as a new body, or cut from / joined to an existing body. Returns the feature id (the new body's id when mode is new).",
|
|
34816
34865
|
revolve: "Revolve the closed shapes of a sketch about an axis in the sketch plane to make a solid of revolution: new body, or cut / join with an existing body. Use it for round parts: draw half the outline beside the axis.",
|
|
@@ -34819,7 +34868,8 @@ var descriptions = {
|
|
|
34819
34868
|
fillet: "Round the given edges of a body with a radius. Call list_edges first (narrow it with its filter) and pass the edge indices and their rebuildId. If it fails, try a smaller radius or fewer edges.",
|
|
34820
34869
|
chamfer: "Bevel the given edges of a body. Call list_edges first (narrow it with its filter) and pass the edge indices and their rebuildId. If it fails, try a smaller distance or fewer edges.",
|
|
34821
34870
|
shell: "Hollow out a body, leaving the given faces open, with walls of a given thickness. Call list_faces first and pass the face indices and their rebuildId. Use this for enclosures and cups instead of subtracting a smaller box; list_faces with filter normal finds the face to open.",
|
|
34822
|
-
pattern: "Repeat a body in a row or grid, or around an axis, either fusing the copies into the body or making each copy a body of its own. Model one copy (or one cutter) and pattern it instead of adding copies by hand.",
|
|
34871
|
+
pattern: "Repeat a body in a row or grid, or around an axis, either fusing the copies into the body or making each copy a body of its own. Model one copy (or one cutter) and pattern it instead of adding copies by hand. To repeat a screw hole use pattern_hole instead.",
|
|
34872
|
+
pattern_hole: "Repeat a hole you already made: in a row or grid, or around an axis (a bolt circle, for example 6 holes around a flange). The copies are cut into the hole's body in one go with the hole's size, fit, head and depth, and follow later edits to the hole. The layout works in world axes: for a bolt circle on a top face use kind circular, axis z, centre a point on the axis (the middle of the circle) and the count; the hole itself is one of the copies, so place it on the bolt circle. A copy that lands off the face is skipped and reported.",
|
|
34823
34873
|
mirror: "Mirror a whole body across a plane (yz, xz or xy, optionally moved along its normal), joining the copy into the body or making it a separate body. Model one side of a symmetric part, then mirror it.",
|
|
34824
34874
|
move: "Move, rotate and/or scale a whole body (translate in mm, rotate in degrees about X, Y, Z around a pivot).",
|
|
34825
34875
|
update_feature: "Change fields of an existing feature (for example an extrude's distance or a box's width); everything after it rebuilds. The change is refused, and the project left as it was, if the result is not a valid model. Prefer this over deleting and rebuilding a feature.",
|
|
@@ -35460,8 +35510,10 @@ Workflow:
|
|
|
35460
35510
|
Pick the tool by task:
|
|
35461
35511
|
- Hollow part (enclosure, box, cup): shell the solid with the open face(s) removed (list_faces with filter normal "+z" finds the top); don't subtract a smaller box.
|
|
35462
35512
|
- Screw holes, standoffs, inserts: hole (size, fit close/normal/loose/tap/insert, counterbore or countersink, blind depth), not cut circles.
|
|
35513
|
+
- Several of the same hole (a bolt circle on a flange, a row or grid of mounting holes): make one hole with hole, then pattern_hole with its id. For a bolt circle use layout kind circular, axis = the face normal axis (z for a top face), centre = a point on the circle's axis, count = number of holes; place the first hole on the circle itself (list_faces gives the face's frame and bounds). Copies that land off the face are skipped and reported.
|
|
35463
35514
|
- Repeated parts (rows, grids, around a circle, lattices): model one body, or one cutter body, then pattern it (then combine subtract for a cutter). Symmetric parts: model one side, then mirror.
|
|
35464
35515
|
- Round parts (knobs, turrets, finials, domes): add_sketch on XZ (sketch x = world X, sketch y = world Z), draw half the outline at x >= 0, then revolve with axis "y" (the world Z axis).
|
|
35516
|
+
- Pipes, spacers and rings: the tube primitive (a torus for an O-ring), not two subtracted cylinders.
|
|
35465
35517
|
- Sizes that belong together or may change: set_parameter first, then use the names in fields.
|
|
35466
35518
|
- Changing or fixing a feature: update_feature (or undo the last step); don't delete and rebuild.
|
|
35467
35519
|
- Picking edges: use the list_edges filter (face, direction, kind) and the list_faces filter (normal, kind) instead of reading every edge. For the rim of the top: list_faces normal "+z", then list_edges face <index>.
|
|
@@ -35496,7 +35548,7 @@ function createPolypadMcp(options = {}) {
|
|
|
35496
35548
|
}
|
|
35497
35549
|
|
|
35498
35550
|
// src/index.ts
|
|
35499
|
-
var VERSION = true ? "0.1.
|
|
35551
|
+
var VERSION = true ? "0.1.6" : "0.0.0-dev";
|
|
35500
35552
|
var err = (line2) => process.stderr.write(`${line2}
|
|
35501
35553
|
`);
|
|
35502
35554
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polypad/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server that lets Claude, Cursor or another AI assistant build and inspect 3D models in your open Polypad tab (polypad.ink). Runs locally, nothing is uploaded.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist/index.js",
|
|
21
|
+
"dist/THIRD_PARTY_NOTICES.txt",
|
|
21
22
|
"README.md",
|
|
22
23
|
"LICENSE"
|
|
23
24
|
],
|
|
@@ -32,9 +33,11 @@
|
|
|
32
33
|
"prepublishOnly": "tsup",
|
|
33
34
|
"typecheck": "tsc --noEmit",
|
|
34
35
|
"test": "vitest run",
|
|
35
|
-
"lint": "eslint ."
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"build:mcpb": "node mcpb/build.mjs"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
40
|
+
"@anthropic-ai/mcpb": "2.1.2",
|
|
38
41
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
39
42
|
"@polypad/core": "workspace:*",
|
|
40
43
|
"@polypad/document": "workspace:*",
|