openscad-mcp-server 1.0.4 → 1.0.5
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 +24 -0
- package/dist/index.mjs +49 -27
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# OpenSCAD MCP Server
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/openscad-mcp-server)
|
|
4
|
+
[](https://www.npmjs.com/package/openscad-mcp-server)
|
|
4
5
|
[](https://github.com/fboldo/openscad-mcp-server/actions/workflows/ci.yml)
|
|
5
6
|
|
|
6
7
|
An MCP (Model Context Protocol) server that renders **PNG previews** and **STL geometry** from OpenSCAD (SCAD) source code. It is designed to support **iterative, agent-driven CAD workflows**, where models can be previewed visually and exported for downstream use (e.g. fabrication, simulation, or inspection).
|
|
@@ -33,6 +34,16 @@ This MCP server is currently in beta. Performance, APIs, and capabilities may ch
|
|
|
33
34
|
- Input: `scadCode` (string), optional `filename` (string)
|
|
34
35
|
- Output: MCP embedded resource (STL)
|
|
35
36
|
|
|
37
|
+
## Skill
|
|
38
|
+
|
|
39
|
+
This repository also includes an [OpenSCAD iterative modeling skill](skills/openscad-iterative-modeling/SKILL.md) that demonstrates how to use this MCP server to support an iterative SCAD → PNG → critique → refine loop.
|
|
40
|
+
|
|
41
|
+
## Limitations
|
|
42
|
+
|
|
43
|
+
- **Performance**: Rendering complex SCAD models can be slow, especially in a WASM environment.
|
|
44
|
+
- **Feature support**: Not all OpenSCAD features may be fully supported or may have limitations in the WASM version.
|
|
45
|
+
- **Fonts**: Text rendering is not currently supported. Support is planned for a future release.
|
|
46
|
+
|
|
36
47
|
## Installation
|
|
37
48
|
|
|
38
49
|
The published package is intended to run over stdio. Configure it in your MCP client using `npx`:
|
|
@@ -48,6 +59,16 @@ The published package is intended to run over stdio. Configure it in your MCP cl
|
|
|
48
59
|
}
|
|
49
60
|
```
|
|
50
61
|
|
|
62
|
+
### Using the Skill
|
|
63
|
+
|
|
64
|
+
[Agents skills](https://github.com/agentskills/agentskills) are a simple, open format for giving agents new capabilities and expertise.
|
|
65
|
+
|
|
66
|
+
The most straightforward to use the OpenSCAD iterative modeling skill is to install it using the [skills CLI](https://github.com/vercel-labs/skills):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx skills add fboldo/openscad-mcp-server --skill openscad-iterative-modeling
|
|
70
|
+
```
|
|
71
|
+
|
|
51
72
|
## Local development
|
|
52
73
|
|
|
53
74
|
- Install deps: `bun install`
|
|
@@ -62,6 +83,9 @@ The published package is intended to run over stdio. Configure it in your MCP cl
|
|
|
62
83
|
- [jhacksman/OpenSCAD-MCP-Server](https://github.com/jhacksman/OpenSCAD-MCP-Server)
|
|
63
84
|
This project provides a different approach relying on generating images from user prompts, followed by 3D reconstruction and even 3D printer discovery. It's a very interesting project, and I recommend checking it out if you are interested in OpenSCAD and MCP servers.
|
|
64
85
|
|
|
86
|
+
- [petrijr/openscad-mcp](https://github.com/petrijr/openscad-mcp)
|
|
87
|
+
Similar to this project, but it uses a Python-based server and relies on the OpenSCAD CLI for rendering.
|
|
88
|
+
|
|
65
89
|
## Relevant Links
|
|
66
90
|
|
|
67
91
|
- [OpenSCAD](https://openscad.org/)
|
package/dist/index.mjs
CHANGED
|
@@ -58,24 +58,48 @@ const asTool = (fn) => {
|
|
|
58
58
|
|
|
59
59
|
//#endregion
|
|
60
60
|
//#region src/infra/openscad.ts
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Executes a callback function with an OpenSCAD instance, capturing any errors and outputs produced during the execution.
|
|
63
|
+
*
|
|
64
|
+
* @param callback - An async function that receives an OpenSCAD instance to perform operations
|
|
65
|
+
* @returns A tuple containing the callback result (or error), an array of errors captured from OpenSCAD's stderr, and a set of outputs captured from OpenSCAD's stdout
|
|
66
|
+
*/
|
|
67
|
+
const executeOpenSCAD = async (callback) => {
|
|
68
|
+
const outputs = [];
|
|
69
|
+
const response = await callback(await createOpenSCAD({
|
|
70
|
+
noInitialRun: true,
|
|
71
|
+
printErr: (text) => {
|
|
72
|
+
outputs.push(text);
|
|
73
|
+
},
|
|
74
|
+
print: (text) => {
|
|
75
|
+
outputs.push(text);
|
|
67
76
|
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
print: (text) => writeStderr(`[OpenSCAD]: ${text}`),
|
|
71
|
-
printErr: (text) => writeStderr(`[OpenSCAD Error]: ${text}`)
|
|
77
|
+
})).catch((e) => {
|
|
78
|
+
return e;
|
|
72
79
|
});
|
|
80
|
+
if (response.constructor.name.includes("Error")) {
|
|
81
|
+
const errors = outputs.filter((o) => o.toLowerCase().includes("error:"));
|
|
82
|
+
const errorMessage = errors.length > 0 ? [response.message, ...errors].filter(Boolean).join("\n") : response.message;
|
|
83
|
+
throw new Error(errorMessage);
|
|
84
|
+
}
|
|
85
|
+
return response;
|
|
73
86
|
};
|
|
74
87
|
|
|
75
88
|
//#endregion
|
|
76
89
|
//#region src/app/tools/export-scad-stl/tool.ts
|
|
77
90
|
const exportScadStlTool = async ({ filename = "model.stl", scadCode }) => {
|
|
78
|
-
const
|
|
91
|
+
const result = await executeOpenSCAD(async (instance) => {
|
|
92
|
+
const openscad = instance.getInstance();
|
|
93
|
+
openscad.FS.writeFile("input.scad", scadCode);
|
|
94
|
+
openscad.callMain([
|
|
95
|
+
"input.scad",
|
|
96
|
+
"-o",
|
|
97
|
+
"output.stl",
|
|
98
|
+
"--backend=manifold"
|
|
99
|
+
]);
|
|
100
|
+
return openscad.FS.readFile("output.stl");
|
|
101
|
+
});
|
|
102
|
+
if (!result) throw new Error("Failed to generate STL from OpenSCAD");
|
|
79
103
|
const assignedFilename = filename.endsWith(".stl") ? filename : `${filename}.stl`;
|
|
80
104
|
const defaultFilename = `model-${(/* @__PURE__ */ new Date()).getTime()}.stl`;
|
|
81
105
|
return {
|
|
@@ -83,7 +107,7 @@ const exportScadStlTool = async ({ filename = "model.stl", scadCode }) => {
|
|
|
83
107
|
resource: {
|
|
84
108
|
uri: `file://${assignedFilename ?? defaultFilename}`,
|
|
85
109
|
mimeType: "model/stl",
|
|
86
|
-
blob: Buffer.from(
|
|
110
|
+
blob: Buffer.from(result, "utf8").toString("base64")
|
|
87
111
|
}
|
|
88
112
|
};
|
|
89
113
|
};
|
|
@@ -172,9 +196,21 @@ const resolveCameraPosition = (cameraPosition, cameraPreset) => {
|
|
|
172
196
|
return CAMERA_PRESETS[cameraPreset];
|
|
173
197
|
};
|
|
174
198
|
const renderScadPngTool = async ({ scadCode, width, height, cameraPreset, cameraPosition }) => {
|
|
199
|
+
const result = await executeOpenSCAD(async (instance) => {
|
|
200
|
+
const openscad = instance.getInstance();
|
|
201
|
+
openscad.FS.writeFile("input.scad", scadCode);
|
|
202
|
+
openscad.callMain([
|
|
203
|
+
"input.scad",
|
|
204
|
+
"-o",
|
|
205
|
+
"output.stl",
|
|
206
|
+
"--backend=manifold"
|
|
207
|
+
]);
|
|
208
|
+
return openscad.FS.readFile("output.stl");
|
|
209
|
+
});
|
|
210
|
+
if (!result) throw new Error("Failed to generate STL from OpenSCAD");
|
|
175
211
|
return {
|
|
176
212
|
type: "image",
|
|
177
|
-
data: await createPngBase64FromStl(
|
|
213
|
+
data: await createPngBase64FromStl(result, width, height, resolveCameraPosition(cameraPosition, cameraPreset)),
|
|
178
214
|
mimeType: "image/png",
|
|
179
215
|
_meta: {
|
|
180
216
|
width,
|
|
@@ -208,20 +244,6 @@ const RenderScadPngToolInputSchema = z.object({
|
|
|
208
244
|
cameraPreset: RenderScadPngCameraPreset.optional().describe("A named camera preset used when `cameraPosition` is not provided"),
|
|
209
245
|
cameraPosition: RenderScadPngCamera.optional().describe("Camera position as { x,y,z }. Example: { x: 0, y: -25, z: 20 }")
|
|
210
246
|
});
|
|
211
|
-
const RenderScadPngToolOutputSchema = z.object({
|
|
212
|
-
image: ImageContentSchema,
|
|
213
|
-
metadata: z.object({
|
|
214
|
-
width: z.number(),
|
|
215
|
-
height: z.number(),
|
|
216
|
-
cameraPreset: RenderScadPngCameraPreset.optional(),
|
|
217
|
-
cameraPosition: RenderScadPngCamera.optional(),
|
|
218
|
-
timingsMs: z.object({
|
|
219
|
-
openscadToStl: z.number(),
|
|
220
|
-
stlToPng: z.number(),
|
|
221
|
-
total: z.number()
|
|
222
|
-
})
|
|
223
|
-
})
|
|
224
|
-
});
|
|
225
247
|
|
|
226
248
|
//#endregion
|
|
227
249
|
//#region src/app/tools/render-scad-png/register.ts
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"module": "./dist/index.mjs",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.5",
|
|
7
7
|
"mcpName": "io.github.fboldo/openscad-mcp-server",
|
|
8
8
|
"description": "An MCP server that renders PNG previews and STL geometry from OpenSCAD (SCAD) source code.",
|
|
9
9
|
"repository": {
|
package/server.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"url": "https://github.com/fboldo/openscad-mcp-server",
|
|
9
9
|
"source": "github"
|
|
10
10
|
},
|
|
11
|
-
"version": "1.0.
|
|
11
|
+
"version": "1.0.5",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
16
16
|
"identifier": "openscad-mcp-server",
|
|
17
|
-
"version": "1.0.
|
|
17
|
+
"version": "1.0.5",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
}
|