@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.
Files changed (58) hide show
  1. package/.env.example +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +108 -0
  4. package/dist/constants.d.ts +123 -0
  5. package/dist/constants.js +169 -0
  6. package/dist/index.d.ts +8 -0
  7. package/dist/index.js +130 -0
  8. package/dist/instructions.d.ts +6 -0
  9. package/dist/instructions.js +90 -0
  10. package/dist/schemas/balance.d.ts +11 -0
  11. package/dist/schemas/balance.js +8 -0
  12. package/dist/schemas/common.d.ts +38 -0
  13. package/dist/schemas/common.js +52 -0
  14. package/dist/schemas/generation.d.ts +219 -0
  15. package/dist/schemas/generation.js +217 -0
  16. package/dist/schemas/image.d.ts +55 -0
  17. package/dist/schemas/image.js +46 -0
  18. package/dist/schemas/output.d.ts +75 -0
  19. package/dist/schemas/output.js +41 -0
  20. package/dist/schemas/postprocessing.d.ts +135 -0
  21. package/dist/schemas/postprocessing.js +123 -0
  22. package/dist/schemas/printing.d.ts +63 -0
  23. package/dist/schemas/printing.js +54 -0
  24. package/dist/schemas/tasks.d.ts +123 -0
  25. package/dist/schemas/tasks.js +85 -0
  26. package/dist/services/error-handler.d.ts +32 -0
  27. package/dist/services/error-handler.js +141 -0
  28. package/dist/services/file-utils.d.ts +15 -0
  29. package/dist/services/file-utils.js +55 -0
  30. package/dist/services/meshy-client.d.ts +54 -0
  31. package/dist/services/meshy-client.js +172 -0
  32. package/dist/services/output-manager.d.ts +52 -0
  33. package/dist/services/output-manager.js +284 -0
  34. package/dist/tools/balance.d.ts +9 -0
  35. package/dist/tools/balance.js +61 -0
  36. package/dist/tools/generation.d.ts +9 -0
  37. package/dist/tools/generation.js +419 -0
  38. package/dist/tools/image.d.ts +9 -0
  39. package/dist/tools/image.js +154 -0
  40. package/dist/tools/postprocessing.d.ts +9 -0
  41. package/dist/tools/postprocessing.js +405 -0
  42. package/dist/tools/printing.d.ts +9 -0
  43. package/dist/tools/printing.js +338 -0
  44. package/dist/tools/tasks.d.ts +9 -0
  45. package/dist/tools/tasks.js +1074 -0
  46. package/dist/tools/workspace.d.ts +9 -0
  47. package/dist/tools/workspace.js +161 -0
  48. package/dist/types.d.ts +261 -0
  49. package/dist/types.js +4 -0
  50. package/dist/utils/endpoints.d.ts +16 -0
  51. package/dist/utils/endpoints.js +38 -0
  52. package/dist/utils/request-builder.d.ts +15 -0
  53. package/dist/utils/request-builder.js +24 -0
  54. package/dist/utils/response-formatter.d.ts +27 -0
  55. package/dist/utils/response-formatter.js +37 -0
  56. package/dist/utils/slicer-detector.d.ts +29 -0
  57. package/dist/utils/slicer-detector.js +237 -0
  58. 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;