@meshy-ai/meshy-mcp-server 0.2.0
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/.env.example +14 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/constants.d.ts +123 -0
- package/dist/constants.js +169 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +130 -0
- package/dist/instructions.d.ts +6 -0
- package/dist/instructions.js +90 -0
- package/dist/schemas/balance.d.ts +11 -0
- package/dist/schemas/balance.js +8 -0
- package/dist/schemas/common.d.ts +38 -0
- package/dist/schemas/common.js +52 -0
- package/dist/schemas/generation.d.ts +219 -0
- package/dist/schemas/generation.js +217 -0
- package/dist/schemas/image.d.ts +55 -0
- package/dist/schemas/image.js +46 -0
- package/dist/schemas/output.d.ts +75 -0
- package/dist/schemas/output.js +41 -0
- package/dist/schemas/postprocessing.d.ts +135 -0
- package/dist/schemas/postprocessing.js +123 -0
- package/dist/schemas/printing.d.ts +63 -0
- package/dist/schemas/printing.js +54 -0
- package/dist/schemas/tasks.d.ts +123 -0
- package/dist/schemas/tasks.js +85 -0
- package/dist/services/error-handler.d.ts +32 -0
- package/dist/services/error-handler.js +141 -0
- package/dist/services/file-utils.d.ts +15 -0
- package/dist/services/file-utils.js +55 -0
- package/dist/services/meshy-client.d.ts +54 -0
- package/dist/services/meshy-client.js +172 -0
- package/dist/services/output-manager.d.ts +52 -0
- package/dist/services/output-manager.js +284 -0
- package/dist/tools/balance.d.ts +9 -0
- package/dist/tools/balance.js +61 -0
- package/dist/tools/generation.d.ts +9 -0
- package/dist/tools/generation.js +419 -0
- package/dist/tools/image.d.ts +9 -0
- package/dist/tools/image.js +154 -0
- package/dist/tools/postprocessing.d.ts +9 -0
- package/dist/tools/postprocessing.js +405 -0
- package/dist/tools/printing.d.ts +9 -0
- package/dist/tools/printing.js +338 -0
- package/dist/tools/tasks.d.ts +9 -0
- package/dist/tools/tasks.js +1074 -0
- package/dist/tools/workspace.d.ts +9 -0
- package/dist/tools/workspace.js +161 -0
- package/dist/types.d.ts +261 -0
- package/dist/types.js +4 -0
- package/dist/utils/endpoints.d.ts +16 -0
- package/dist/utils/endpoints.js +38 -0
- package/dist/utils/request-builder.d.ts +15 -0
- package/dist/utils/request-builder.js +24 -0
- package/dist/utils/response-formatter.d.ts +27 -0
- package/dist/utils/response-formatter.js +37 -0
- package/dist/utils/slicer-detector.d.ts +29 -0
- package/dist/utils/slicer-detector.js +237 -0
- package/package.json +64 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3D printing tools (send to slicer, analyze printability, process multicolor)
|
|
3
|
+
*/
|
|
4
|
+
import { handleMeshyError } from "../services/error-handler.js";
|
|
5
|
+
import { SendToSlicerInputSchema, AnalyzePrintabilityInputSchema, ProcessMulticolorInputSchema } from "../schemas/printing.js";
|
|
6
|
+
import { ResponseFormat, SlicerType } from "../constants.js";
|
|
7
|
+
import { formatTaskCreatedResponse } from "../utils/response-formatter.js";
|
|
8
|
+
import { detectInstalledSlicers, detectSlicer, getBestMulticolorSlicer } from "../utils/slicer-detector.js";
|
|
9
|
+
/**
|
|
10
|
+
* Build Bambu Studio URL scheme (kept for backward compat).
|
|
11
|
+
*/
|
|
12
|
+
function buildBambuStudioUrl(modelUrl, fileName) {
|
|
13
|
+
const urlWithFragment = `${modelUrl}#/${fileName}`;
|
|
14
|
+
const isMac = process.platform === "darwin";
|
|
15
|
+
if (isMac) {
|
|
16
|
+
return `bambustudioopen://${encodeURIComponent(urlWithFragment)}`;
|
|
17
|
+
}
|
|
18
|
+
return `bambustudio://open?file=${encodeURIComponent(urlWithFragment)}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Format detected slicers into a markdown table.
|
|
22
|
+
*/
|
|
23
|
+
function formatSlicerTable(slicers) {
|
|
24
|
+
if (slicers.length === 0)
|
|
25
|
+
return "No slicers detected.";
|
|
26
|
+
const rows = slicers.map(s => `| ${s.displayName} | ${s.supportsMulticolor ? "Yes" : "No"} | \`${s.path}\` |`);
|
|
27
|
+
return `| Slicer | Multicolor | Path |
|
|
28
|
+
|--------|-----------|------|
|
|
29
|
+
${rows.join("\n")}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Register 3D printing tools with the MCP server
|
|
33
|
+
*/
|
|
34
|
+
export function registerPrintingTools(server, client) {
|
|
35
|
+
// ── Send to Slicer ──────────────────────────────────────────────
|
|
36
|
+
server.registerTool("meshy_send_to_slicer", {
|
|
37
|
+
title: "Send Model to 3D Printing Slicer",
|
|
38
|
+
description: `Detect installed 3D printing slicer software and return launch commands.
|
|
39
|
+
|
|
40
|
+
IMPORTANT: This tool should be called as the FIRST STEP in any 3D printing workflow — before generating the model. Save the result and reuse it at the end to open the model in the user's chosen slicer.
|
|
41
|
+
|
|
42
|
+
Supports 7 slicers: OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, Anycubic Slicer Next, PrusaSlicer, UltiMaker Cura.
|
|
43
|
+
Multicolor-capable: OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, Anycubic Slicer Next.
|
|
44
|
+
|
|
45
|
+
Usage pattern:
|
|
46
|
+
1. Call this tool at the START of a print workflow to detect slicers
|
|
47
|
+
2. Present detected slicers to the user
|
|
48
|
+
3. Generate and download the model
|
|
49
|
+
4. At the END, ask user which slicer to use from the detected list
|
|
50
|
+
5. Take the launch_command, replace {file} with the local file path, execute via Bash
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
- model_url (string): Download URL of the model file (can be a placeholder at detection time)
|
|
54
|
+
- slicer_type (enum): 'auto' (default, detect all) or a specific slicer
|
|
55
|
+
- file_name (string): File name for the model (default: "meshy_model.3mf")
|
|
56
|
+
- is_multicolor (boolean): If true, only recommends multicolor-capable slicers (default: false)
|
|
57
|
+
- response_format (enum): "markdown" or "json" (default: "markdown")
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
{
|
|
61
|
+
"detected_slicers": [{"name": "OrcaSlicer", "launch_command": "open -a \\"OrcaSlicer\\" \\"{file}\\"", ...}],
|
|
62
|
+
"recommended_slicer": {"name": "...", "launch_command": "..."},
|
|
63
|
+
"model_url": "https://..."
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
The launch_command contains {file} as placeholder. Replace it with the actual local file path before executing via Bash.`,
|
|
67
|
+
inputSchema: SendToSlicerInputSchema,
|
|
68
|
+
annotations: {
|
|
69
|
+
readOnlyHint: true,
|
|
70
|
+
destructiveHint: false,
|
|
71
|
+
idempotentHint: true,
|
|
72
|
+
openWorldHint: false
|
|
73
|
+
}
|
|
74
|
+
}, async (params) => {
|
|
75
|
+
try {
|
|
76
|
+
// Step 1: Detect slicers
|
|
77
|
+
let detected = [];
|
|
78
|
+
if (params.slicer_type === "auto") {
|
|
79
|
+
detected = detectInstalledSlicers();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const specific = detectSlicer(params.slicer_type);
|
|
83
|
+
if (specific)
|
|
84
|
+
detected = [specific];
|
|
85
|
+
}
|
|
86
|
+
// Step 2: Filter for multicolor if requested
|
|
87
|
+
if (params.is_multicolor) {
|
|
88
|
+
detected = detected.filter(s => s.supportsMulticolor);
|
|
89
|
+
}
|
|
90
|
+
// Step 3: Pick recommended slicer
|
|
91
|
+
const recommended = params.is_multicolor
|
|
92
|
+
? getBestMulticolorSlicer(detected)
|
|
93
|
+
: (detected[0] || null);
|
|
94
|
+
// Step 4: Build launch command with actual file name
|
|
95
|
+
const slicerList = detected.map(s => ({
|
|
96
|
+
name: s.displayName,
|
|
97
|
+
type: s.type,
|
|
98
|
+
path: s.path,
|
|
99
|
+
supports_multicolor: s.supportsMulticolor,
|
|
100
|
+
launch_command: s.launchCommand.replace("{file}", params.model_url),
|
|
101
|
+
url_scheme: s.urlScheme
|
|
102
|
+
}));
|
|
103
|
+
const recommendedOutput = recommended ? {
|
|
104
|
+
name: recommended.displayName,
|
|
105
|
+
type: recommended.type,
|
|
106
|
+
launch_command: recommended.launchCommand.replace("{file}", params.model_url),
|
|
107
|
+
supports_multicolor: recommended.supportsMulticolor
|
|
108
|
+
} : null;
|
|
109
|
+
// Bambu URL scheme as bonus
|
|
110
|
+
const bambuSlicer = detected.find(s => s.type === SlicerType.BAMBU);
|
|
111
|
+
const bambuUrlScheme = bambuSlicer
|
|
112
|
+
? buildBambuStudioUrl(params.model_url, params.file_name)
|
|
113
|
+
: undefined;
|
|
114
|
+
const output = {
|
|
115
|
+
detected_slicers: slicerList,
|
|
116
|
+
recommended_slicer: recommendedOutput,
|
|
117
|
+
model_url: params.model_url,
|
|
118
|
+
file_name: params.file_name,
|
|
119
|
+
bambu_url_scheme: bambuUrlScheme,
|
|
120
|
+
is_multicolor: params.is_multicolor
|
|
121
|
+
};
|
|
122
|
+
let textContent;
|
|
123
|
+
if (params.response_format === ResponseFormat.MARKDOWN) {
|
|
124
|
+
if (detected.length === 0) {
|
|
125
|
+
const slicerSuggestions = params.is_multicolor
|
|
126
|
+
? "OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, or Anycubic Slicer Next"
|
|
127
|
+
: "OrcaSlicer, Bambu Studio, PrusaSlicer, UltiMaker Cura, or others";
|
|
128
|
+
textContent = `# No Slicer Detected
|
|
129
|
+
|
|
130
|
+
No ${params.is_multicolor ? "multicolor-capable " : ""}slicer software was found on this system.
|
|
131
|
+
|
|
132
|
+
**Recommended slicers to install**: ${slicerSuggestions}
|
|
133
|
+
|
|
134
|
+
**Manual steps**:
|
|
135
|
+
1. Download the model from: ${params.model_url}
|
|
136
|
+
2. Install a slicer from the list above
|
|
137
|
+
3. Open the downloaded file in the slicer: File → Import → select the file`;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const launchCmd = recommendedOutput?.launch_command || "";
|
|
141
|
+
textContent = `# Slicer Detection Result
|
|
142
|
+
|
|
143
|
+
## Detected Slicers
|
|
144
|
+
|
|
145
|
+
${formatSlicerTable(detected)}
|
|
146
|
+
|
|
147
|
+
## Recommended: ${recommendedOutput?.name || "None"}
|
|
148
|
+
|
|
149
|
+
**Launch command** (execute via Bash):
|
|
150
|
+
\`\`\`
|
|
151
|
+
${launchCmd}
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
**Model URL**: ${params.model_url}
|
|
155
|
+
**File Name**: ${params.file_name}${bambuUrlScheme ? `\n\n**Bambu Studio URL Scheme**:\n\`\`\`\n${bambuUrlScheme}\n\`\`\`` : ""}
|
|
156
|
+
|
|
157
|
+
**Reminders**:
|
|
158
|
+
- Execute the launch command above to open the model in the slicer
|
|
159
|
+
- Check print settings (layer height, infill, supports) in the slicer before printing
|
|
160
|
+
- The model URL is temporary and will expire after 24 hours`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
textContent = JSON.stringify(output, null, 2);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: textContent }],
|
|
168
|
+
structuredContent: output
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return {
|
|
173
|
+
isError: true,
|
|
174
|
+
content: [{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: handleMeshyError(error)
|
|
177
|
+
}]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
// ── Analyze Printability (placeholder) ──────────────────────────
|
|
182
|
+
server.registerTool("meshy_analyze_printability", {
|
|
183
|
+
title: "Analyze Model Printability",
|
|
184
|
+
description: `[PLACEHOLDER] Analyze a 3D model's suitability for 3D printing.
|
|
185
|
+
|
|
186
|
+
Currently returns a manual checklist for print readiness. Will be replaced with automated analysis when the Meshy printability API becomes available.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
- task_id (string): Task ID of the completed model to analyze (required)
|
|
190
|
+
- task_type (string): Task type (default: "text-to-3d")
|
|
191
|
+
- response_format (enum): Output format - "markdown" or "json" (default: "markdown")
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
{
|
|
195
|
+
"status": "manual_check_required",
|
|
196
|
+
"checklist": [ ... ],
|
|
197
|
+
"recommendations": [ ... ]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
Examples:
|
|
201
|
+
- Analyze model: { task_id: "abc-123", task_type: "text-to-3d" }`,
|
|
202
|
+
inputSchema: AnalyzePrintabilityInputSchema,
|
|
203
|
+
annotations: {
|
|
204
|
+
readOnlyHint: true,
|
|
205
|
+
destructiveHint: false,
|
|
206
|
+
idempotentHint: true,
|
|
207
|
+
openWorldHint: true
|
|
208
|
+
}
|
|
209
|
+
}, async (params) => {
|
|
210
|
+
try {
|
|
211
|
+
const checklist = [
|
|
212
|
+
{ item: "Wall thickness", recommendation: "Minimum 1.2mm for FDM, 0.8mm for resin", status: "check_manually" },
|
|
213
|
+
{ item: "Overhangs", recommendation: "Keep below 45° or add supports", status: "check_manually" },
|
|
214
|
+
{ item: "Manifold mesh", recommendation: "Ensure mesh is watertight with no holes", status: "check_manually" },
|
|
215
|
+
{ item: "Minimum detail size", recommendation: "At least 0.4mm for FDM nozzle, 0.05mm for resin", status: "check_manually" },
|
|
216
|
+
{ item: "Base stability", recommendation: "Flat base or add a brim/raft in slicer", status: "check_manually" },
|
|
217
|
+
{ item: "Floating parts", recommendation: "All parts should be connected or printed separately", status: "check_manually" }
|
|
218
|
+
];
|
|
219
|
+
const recommendations = [
|
|
220
|
+
"Import the model file into your slicer to check for mesh errors",
|
|
221
|
+
"Use the slicer's built-in repair tool if issues are detected",
|
|
222
|
+
"Consider adding supports for overhanging features",
|
|
223
|
+
"Scale the model appropriately for your printer's build volume",
|
|
224
|
+
"For figurines/miniatures, consider hollowing the model to save material"
|
|
225
|
+
];
|
|
226
|
+
const output = {
|
|
227
|
+
task_id: params.task_id,
|
|
228
|
+
status: "manual_check_required",
|
|
229
|
+
message: "Automated printability analysis is not yet available. Please review the checklist below.",
|
|
230
|
+
checklist,
|
|
231
|
+
recommendations
|
|
232
|
+
};
|
|
233
|
+
let textContent;
|
|
234
|
+
if (params.response_format === ResponseFormat.MARKDOWN) {
|
|
235
|
+
textContent = `# Printability Analysis
|
|
236
|
+
|
|
237
|
+
**Task ID**: ${params.task_id}
|
|
238
|
+
**Status**: Manual check required
|
|
239
|
+
|
|
240
|
+
> Automated printability analysis is not yet available. Please review the checklist below.
|
|
241
|
+
|
|
242
|
+
## Checklist
|
|
243
|
+
|
|
244
|
+
| Check | Recommendation | Status |
|
|
245
|
+
|-------|---------------|--------|
|
|
246
|
+
${checklist.map(c => `| ${c.item} | ${c.recommendation} | Check manually |`).join("\n")}
|
|
247
|
+
|
|
248
|
+
## Recommendations
|
|
249
|
+
|
|
250
|
+
${recommendations.map((r, i) => `${i + 1}. ${r}`).join("\n")}`;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
textContent = JSON.stringify(output, null, 2);
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
content: [{ type: "text", text: textContent }],
|
|
257
|
+
structuredContent: output
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
isError: true,
|
|
263
|
+
content: [{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: handleMeshyError(error)
|
|
266
|
+
}]
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
// ── Process Multicolor (real API) ───────────────────────────────
|
|
271
|
+
server.registerTool("meshy_process_multicolor", {
|
|
272
|
+
title: "Process Multi-Color 3D Print",
|
|
273
|
+
description: `Process a textured 3D model for multi-color 3D printing. Cost: 10 credits.
|
|
274
|
+
|
|
275
|
+
Segments the model's texture into discrete color regions and creates a task that outputs a 3MF file ready for multi-color slicers (OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, Anycubic Slicer Next).
|
|
276
|
+
|
|
277
|
+
PREREQUISITES:
|
|
278
|
+
- The input model MUST have textures. Run meshy_text_to_3d_refine or meshy_retexture first.
|
|
279
|
+
- A multicolor-capable slicer should be installed on the user's system.
|
|
280
|
+
|
|
281
|
+
IMPORTANT: Before calling, ask the user to confirm:
|
|
282
|
+
- max_colors: depends on their printer's multi-color capability (e.g. Bambu AMS supports 4-16 colors). Ask how many colors they want.
|
|
283
|
+
- max_depth: affects color boundary precision and file size. Higher = finer but larger. Ask user's preference or suggest default 4.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
- input_task_id (string): Task ID of a completed TEXTURED model (required)
|
|
287
|
+
- max_colors (number): Max colors for segmentation (1-16, default: 4). MUST confirm with user based on their printer capability.
|
|
288
|
+
- max_depth (number): Segmentation depth (3-6, default: 4). Higher = finer separation, larger file. MUST confirm with user.
|
|
289
|
+
- response_format (enum): "markdown" or "json" (default: "markdown")
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
{ "task_id": "...", "status": "PENDING", ... }
|
|
293
|
+
|
|
294
|
+
Next Steps:
|
|
295
|
+
1. Use meshy_get_task_status with task_id and task_type="multi-color-print" to wait for completion
|
|
296
|
+
2. The completed task will have a 3MF download URL in model_urls
|
|
297
|
+
3. Use meshy_download_model with format="3mf" to download
|
|
298
|
+
4. Use meshy_send_to_slicer with is_multicolor=true to open the 3MF in a multicolor slicer
|
|
299
|
+
|
|
300
|
+
Examples:
|
|
301
|
+
- Default: { input_task_id: "abc-123" }
|
|
302
|
+
- Custom: { input_task_id: "abc-123", max_colors: 8, max_depth: 5 }`,
|
|
303
|
+
inputSchema: ProcessMulticolorInputSchema,
|
|
304
|
+
annotations: {
|
|
305
|
+
readOnlyHint: false,
|
|
306
|
+
destructiveHint: false,
|
|
307
|
+
idempotentHint: false,
|
|
308
|
+
openWorldHint: true
|
|
309
|
+
}
|
|
310
|
+
}, async (params) => {
|
|
311
|
+
try {
|
|
312
|
+
const request = {
|
|
313
|
+
input_task_id: params.input_task_id
|
|
314
|
+
};
|
|
315
|
+
if (params.max_colors !== undefined)
|
|
316
|
+
request.max_colors = params.max_colors;
|
|
317
|
+
if (params.max_depth !== undefined)
|
|
318
|
+
request.max_depth = params.max_depth;
|
|
319
|
+
const response = await client.post("/openapi/v1/print/multi-color", request);
|
|
320
|
+
const taskId = response.result;
|
|
321
|
+
return formatTaskCreatedResponse({
|
|
322
|
+
task_id: taskId,
|
|
323
|
+
status: "PENDING",
|
|
324
|
+
message: `Multi-color processing started for task "${params.input_task_id}" with ${params.max_colors} colors (depth: ${params.max_depth}). Cost: 10 credits.`,
|
|
325
|
+
estimated_time: "2-5 minutes"
|
|
326
|
+
}, params.response_format, "Multi-Color Processing Task Created", `Processing model from task "${params.input_task_id}" into ${params.max_colors} colors (depth: ${params.max_depth}). The output will be a 3MF file for multi-color 3D printing.`, "multi-color-print");
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
return {
|
|
330
|
+
isError: true,
|
|
331
|
+
content: [{
|
|
332
|
+
type: "text",
|
|
333
|
+
text: handleMeshyError(error)
|
|
334
|
+
}]
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task management tools (get status with wait mode, list, cancel, download)
|
|
3
|
+
*/
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { MeshyClient } from "../services/meshy-client.js";
|
|
6
|
+
/**
|
|
7
|
+
* Register task management tools with the MCP server
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerTaskTools(server: McpServer, client: MeshyClient): void;
|