@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,9 @@
1
+ /**
2
+ * Workspace tools (list models)
3
+ */
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { MeshyClient } from "../services/meshy-client.js";
6
+ /**
7
+ * Register workspace tools with the MCP server
8
+ */
9
+ export declare function registerWorkspaceTools(server: McpServer, client: MeshyClient): void;
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Workspace tools (list models)
3
+ */
4
+ import { handleMeshyError } from "../services/error-handler.js";
5
+ import { ListModelsInputSchema } from "../schemas/tasks.js";
6
+ import { ResponseFormat, TaskStatus, CHARACTER_LIMIT } from "../constants.js";
7
+ /**
8
+ * Register workspace tools with the MCP server
9
+ */
10
+ export function registerWorkspaceTools(server, client) {
11
+ // List models tool
12
+ server.registerTool("meshy_list_models", {
13
+ title: "List Workspace Models",
14
+ description: `Browse 3D models in your workspace with filtering and pagination.
15
+
16
+ This tool lists all completed models in your workspace. Only shows models with status SUCCEEDED.
17
+
18
+ Args:
19
+ - workspace_id (string, optional): Workspace ID (uses default workspace if omitted)
20
+ - filter (enum): Filter by visibility - "all", "published", or "private" (default: "all")
21
+ - limit (number): Results per page, 1-100 (default: 20)
22
+ - offset (number): Skip N results for pagination (default: 0)
23
+ - response_format (enum): Output format - "markdown" or "json" (default: "markdown")
24
+
25
+ Returns:
26
+ {
27
+ "page_count": 20,
28
+ "offset": 0,
29
+ "models": [
30
+ {
31
+ "id": "abc-123",
32
+ "name": "Dragon model",
33
+ "thumbnail_url": "https://...",
34
+ "created_at": "2026-03-02T10:00:00Z",
35
+ "vertex_count": 45231,
36
+ "face_count": 89456,
37
+ "is_published": false
38
+ }
39
+ ],
40
+ "has_more": true,
41
+ "next_offset": 20
42
+ }
43
+
44
+ Use Cases:
45
+ - Browse your generated models
46
+ - Find models by creation date
47
+ - Filter published vs private models
48
+
49
+ Examples:
50
+ - List recent models: { limit: 10 }
51
+ - List published models: { filter: "published" }
52
+ - Get second page: { limit: 20, offset: 20 }
53
+
54
+ Error Handling:
55
+ - Automatically truncates if response exceeds size limit`,
56
+ inputSchema: ListModelsInputSchema,
57
+ annotations: {
58
+ readOnlyHint: true,
59
+ destructiveHint: false,
60
+ idempotentHint: true,
61
+ openWorldHint: true
62
+ }
63
+ }, async (params) => {
64
+ try {
65
+ const pageSize = params.limit;
66
+ const pageNum = Math.floor(params.offset / pageSize) + 1;
67
+ const queryParams = {
68
+ status: TaskStatus.SUCCEEDED,
69
+ page_size: pageSize,
70
+ page_num: pageNum
71
+ };
72
+ // API returns Task[] directly
73
+ const response = await client.get("/openapi/v2/text-to-3d", queryParams);
74
+ const tasks = Array.isArray(response) ? response : [];
75
+ let filteredTasks = tasks;
76
+ if (params.filter === "published") {
77
+ filteredTasks = filteredTasks.filter(task => task.model_urls && task.is_published);
78
+ }
79
+ else if (params.filter === "private") {
80
+ filteredTasks = filteredTasks.filter(task => task.model_urls && !task.is_published);
81
+ }
82
+ const models = filteredTasks
83
+ .filter(task => task.model_urls)
84
+ .map(task => ({
85
+ id: task.id,
86
+ name: task.name || task.prompt || "Untitled Model",
87
+ thumbnail_url: task.thumbnail_url || "",
88
+ created_at: task.created_at,
89
+ vertex_count: task.vertex_count,
90
+ face_count: task.face_count,
91
+ is_published: task.is_published || false
92
+ }));
93
+ const output = {
94
+ page_count: models.length,
95
+ offset: params.offset,
96
+ models,
97
+ has_more: tasks.length >= pageSize,
98
+ next_offset: tasks.length >= pageSize
99
+ ? params.offset + tasks.length
100
+ : undefined
101
+ };
102
+ // Helper to render models as markdown
103
+ const renderModelsMarkdown = (modelList, totalCount, truncated = false) => {
104
+ const lines = [`# Workspace Models`, ""];
105
+ lines.push(`**Showing**: ${totalCount} models on this page (offset: ${output.offset})`);
106
+ if (truncated)
107
+ lines.push(`*(truncated from ${models.length} results)*`);
108
+ lines.push("");
109
+ for (const model of modelList) {
110
+ lines.push(`## ${model.name}`);
111
+ lines.push(`- **ID**: ${model.id}`);
112
+ lines.push(`- **Created**: ${new Date(model.created_at).toLocaleString()}`);
113
+ if (model.vertex_count && model.face_count) {
114
+ lines.push(`- **Geometry**: ${model.vertex_count.toLocaleString()} vertices, ${model.face_count.toLocaleString()} faces`);
115
+ }
116
+ lines.push(`- **Published**: ${model.is_published ? 'Yes' : 'No'}`);
117
+ lines.push(`- **Thumbnail**: ${model.thumbnail_url}`);
118
+ lines.push("");
119
+ }
120
+ if (output.has_more) {
121
+ lines.push(`**More results available**. Use offset=${output.next_offset} to see next page.`);
122
+ }
123
+ return lines.join("\n");
124
+ };
125
+ let textContent;
126
+ if (params.response_format === ResponseFormat.MARKDOWN) {
127
+ textContent = renderModelsMarkdown(output.models, output.page_count);
128
+ }
129
+ else {
130
+ textContent = JSON.stringify(output, null, 2);
131
+ }
132
+ // Check character limit — re-render in same format with fewer models
133
+ if (textContent.length > CHARACTER_LIMIT) {
134
+ const truncatedModels = output.models.slice(0, Math.max(1, Math.floor(output.models.length / 2)));
135
+ output.models = truncatedModels;
136
+ output.page_count = truncatedModels.length;
137
+ if (params.response_format === ResponseFormat.MARKDOWN) {
138
+ textContent = renderModelsMarkdown(truncatedModels, truncatedModels.length, true) +
139
+ `\n\n[Response truncated. Use smaller limit to see more results.]`;
140
+ }
141
+ else {
142
+ textContent = JSON.stringify(output, null, 2) +
143
+ `\n\n[Response truncated. Use smaller limit to see more results.]`;
144
+ }
145
+ }
146
+ return {
147
+ content: [{ type: "text", text: textContent }],
148
+ structuredContent: output
149
+ };
150
+ }
151
+ catch (error) {
152
+ return {
153
+ isError: true,
154
+ content: [{
155
+ type: "text",
156
+ text: handleMeshyError(error)
157
+ }]
158
+ };
159
+ }
160
+ });
161
+ }
@@ -0,0 +1,261 @@
1
+ /**
2
+ * TypeScript type definitions for Meshy API
3
+ */
4
+ import { TaskStatus, TaskPhase, Topology, AIModel, ModelType, SymmetryMode, PoseMode } from "./constants.js";
5
+ export interface TextureUrlsObject {
6
+ base_color?: string;
7
+ metallic?: string;
8
+ roughness?: string;
9
+ normal?: string;
10
+ ao?: string;
11
+ }
12
+ export interface TaskError {
13
+ code?: string;
14
+ message: string;
15
+ }
16
+ export interface CreateTaskApiResponse {
17
+ result: string;
18
+ }
19
+ export interface TextTo3DApiRequest {
20
+ mode: string;
21
+ prompt: string;
22
+ ai_model: string;
23
+ moderation: boolean;
24
+ model_type?: string;
25
+ target_polycount?: number;
26
+ topology?: string;
27
+ symmetry_mode?: string;
28
+ should_remesh?: boolean;
29
+ pose_mode?: string;
30
+ target_formats?: string[];
31
+ auto_size?: boolean;
32
+ origin_at?: string;
33
+ }
34
+ export interface ImageTo3DApiRequest {
35
+ image_url: string;
36
+ enable_pbr: boolean;
37
+ moderation: boolean;
38
+ ai_model?: string;
39
+ model_type?: string;
40
+ pose_mode?: string;
41
+ topology?: string;
42
+ target_polycount?: number;
43
+ should_remesh?: boolean;
44
+ symmetry_mode?: string;
45
+ should_texture?: boolean;
46
+ texture_prompt?: string;
47
+ texture_image_url?: string;
48
+ image_enhancement?: boolean;
49
+ remove_lighting?: boolean;
50
+ save_pre_remeshed_model?: boolean;
51
+ target_formats?: string[];
52
+ auto_size?: boolean;
53
+ origin_at?: string;
54
+ }
55
+ export interface TextTo3DRefineApiRequest {
56
+ mode: string;
57
+ preview_task_id: string;
58
+ enable_pbr: boolean;
59
+ ai_model: string;
60
+ texture_prompt?: string;
61
+ texture_image_url?: string;
62
+ remove_lighting?: boolean;
63
+ target_formats?: string[];
64
+ auto_size?: boolean;
65
+ origin_at?: string;
66
+ }
67
+ export interface MultiImageTo3DApiRequest {
68
+ image_urls: string[];
69
+ enable_pbr: boolean;
70
+ moderation: boolean;
71
+ ai_model?: string;
72
+ model_type?: string;
73
+ pose_mode?: string;
74
+ topology?: string;
75
+ target_polycount?: number;
76
+ should_remesh?: boolean;
77
+ symmetry_mode?: string;
78
+ should_texture?: boolean;
79
+ texture_prompt?: string;
80
+ texture_image_url?: string;
81
+ image_enhancement?: boolean;
82
+ remove_lighting?: boolean;
83
+ save_pre_remeshed_model?: boolean;
84
+ target_formats?: string[];
85
+ auto_size?: boolean;
86
+ origin_at?: string;
87
+ }
88
+ export interface RemeshApiRequest {
89
+ target_formats: string[];
90
+ resize_height: number;
91
+ convert_format_only: boolean;
92
+ input_task_id?: string;
93
+ model_url?: string;
94
+ topology?: string;
95
+ target_polycount?: number;
96
+ auto_size?: boolean;
97
+ origin_at?: string;
98
+ }
99
+ export interface RetextureApiRequest {
100
+ enable_original_uv: boolean;
101
+ enable_pbr: boolean;
102
+ input_task_id?: string;
103
+ model_url?: string;
104
+ text_style_prompt?: string;
105
+ image_style_url?: string;
106
+ ai_model?: string;
107
+ remove_lighting?: boolean;
108
+ target_formats?: string[];
109
+ }
110
+ export interface RigApiRequest {
111
+ height_meters: number;
112
+ input_task_id?: string;
113
+ model_url?: string;
114
+ texture_image_url?: string;
115
+ }
116
+ export interface AnimateApiRequest {
117
+ rig_task_id: string;
118
+ action_id: number;
119
+ post_process?: {
120
+ operation_type: string;
121
+ fps?: number;
122
+ };
123
+ }
124
+ export interface MultiColorPrintApiRequest {
125
+ input_task_id: string;
126
+ max_colors?: number;
127
+ max_depth?: number;
128
+ }
129
+ export interface TextToImageApiRequest {
130
+ ai_model: string;
131
+ prompt: string;
132
+ generate_multi_view: boolean;
133
+ aspect_ratio: string;
134
+ pose_mode?: string;
135
+ }
136
+ export interface ImageToImageApiRequest {
137
+ ai_model: string;
138
+ prompt: string;
139
+ reference_image_urls: string[];
140
+ generate_multi_view: boolean;
141
+ }
142
+ export interface Task {
143
+ id: string;
144
+ name?: string;
145
+ type?: string;
146
+ status: TaskStatus;
147
+ phase: TaskPhase;
148
+ progress: number;
149
+ created_at: string | number;
150
+ started_at?: string | number;
151
+ updated_at?: string;
152
+ finished_at?: string | number;
153
+ model_urls?: {
154
+ glb?: string;
155
+ fbx?: string;
156
+ usdz?: string;
157
+ obj?: string;
158
+ mtl?: string;
159
+ blend?: string;
160
+ stl?: string;
161
+ "3mf"?: string;
162
+ };
163
+ thumbnail_url?: string;
164
+ texture_urls?: TextureUrlsObject[] | TextureUrlsObject;
165
+ video_url?: string;
166
+ vertex_count?: number;
167
+ face_count?: number;
168
+ aabb?: {
169
+ min: [number, number, number];
170
+ max: [number, number, number];
171
+ };
172
+ prompt?: string;
173
+ preceding_tasks?: number;
174
+ task_error?: TaskError;
175
+ is_published?: boolean;
176
+ result?: {
177
+ rigged_character_glb_url?: string;
178
+ rigged_character_fbx_url?: string;
179
+ basic_animations?: {
180
+ walking_glb_url?: string;
181
+ walking_fbx_url?: string;
182
+ walking_armature_glb_url?: string;
183
+ running_glb_url?: string;
184
+ running_fbx_url?: string;
185
+ running_armature_glb_url?: string;
186
+ };
187
+ animation_glb_url?: string;
188
+ animation_fbx_url?: string;
189
+ processed_usdz_url?: string;
190
+ processed_armature_fbx_url?: string;
191
+ processed_animation_fps_fbx_url?: string;
192
+ };
193
+ }
194
+ export interface TextTo3DArgs {
195
+ prompt: string;
196
+ ai_model: AIModel;
197
+ model_type?: ModelType;
198
+ topology?: Topology;
199
+ target_polycount?: number;
200
+ symmetry_mode?: SymmetryMode;
201
+ should_remesh?: boolean;
202
+ pose_mode?: PoseMode;
203
+ }
204
+ export interface ImageTo3DArgs {
205
+ image_url: string;
206
+ ai_model?: AIModel;
207
+ model_type?: ModelType;
208
+ pose_mode?: PoseMode;
209
+ enable_pbr: boolean;
210
+ topology?: Topology;
211
+ target_polycount?: number;
212
+ should_remesh?: boolean;
213
+ symmetry_mode?: SymmetryMode;
214
+ should_texture?: boolean;
215
+ texture_prompt?: string;
216
+ texture_image_url?: string;
217
+ image_enhancement?: boolean;
218
+ remove_lighting?: boolean;
219
+ save_pre_remeshed_model?: boolean;
220
+ }
221
+ export interface CreateTaskRequest {
222
+ mode: string;
223
+ args: TextTo3DArgs | ImageTo3DArgs;
224
+ }
225
+ export interface CreateTaskResponse {
226
+ id: string;
227
+ status: TaskStatus;
228
+ created_at: string;
229
+ }
230
+ export interface GetTaskResponse extends Task {
231
+ }
232
+ export interface ListTasksResponse {
233
+ tasks: Task[];
234
+ total: number;
235
+ limit: number;
236
+ offset: number;
237
+ }
238
+ export interface PaginationParams {
239
+ limit: number;
240
+ offset: number;
241
+ }
242
+ export interface BalanceResponse {
243
+ balance: number;
244
+ }
245
+ export interface PaginatedResponse<T> {
246
+ total: number;
247
+ count: number;
248
+ offset: number;
249
+ items: T[];
250
+ has_more: boolean;
251
+ next_offset?: number;
252
+ }
253
+ export interface ModelSummary {
254
+ id: string;
255
+ name: string;
256
+ thumbnail_url: string;
257
+ created_at: string | number;
258
+ vertex_count?: number;
259
+ face_count?: number;
260
+ is_published: boolean;
261
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TypeScript type definitions for Meshy API
3
+ */
4
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared task endpoint mapping
3
+ */
4
+ import { TaskType } from "../constants.js";
5
+ /**
6
+ * All endpoint base paths in priority order for auto-inference
7
+ */
8
+ export declare const ALL_TASK_ENDPOINTS: string[];
9
+ /**
10
+ * Task types that have list endpoints (excludes rigging and animation which have no list API)
11
+ */
12
+ export declare const LIST_CAPABLE_TASK_TYPES: TaskType[];
13
+ /**
14
+ * Map task type to its API endpoint base path
15
+ */
16
+ export declare function getTaskEndpoint(taskType: TaskType): string;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared task endpoint mapping
3
+ */
4
+ import { TaskType } from "../constants.js";
5
+ const TASK_ENDPOINTS = {
6
+ [TaskType.TEXT_TO_3D]: "/openapi/v2/text-to-3d",
7
+ [TaskType.IMAGE_TO_3D]: "/openapi/v1/image-to-3d",
8
+ [TaskType.MULTI_IMAGE_TO_3D]: "/openapi/v1/multi-image-to-3d",
9
+ [TaskType.REMESH]: "/openapi/v1/remesh",
10
+ [TaskType.RETEXTURE]: "/openapi/v1/retexture",
11
+ [TaskType.RIGGING]: "/openapi/v1/rigging",
12
+ [TaskType.ANIMATION]: "/openapi/v1/animations",
13
+ [TaskType.TEXT_TO_IMAGE]: "/openapi/v1/text-to-image",
14
+ [TaskType.IMAGE_TO_IMAGE]: "/openapi/v1/image-to-image",
15
+ [TaskType.MULTI_COLOR_PRINT]: "/openapi/v1/print/multi-color"
16
+ };
17
+ /**
18
+ * All endpoint base paths in priority order for auto-inference
19
+ */
20
+ export const ALL_TASK_ENDPOINTS = Object.values(TASK_ENDPOINTS);
21
+ /**
22
+ * Task types that have list endpoints (excludes rigging and animation which have no list API)
23
+ */
24
+ export const LIST_CAPABLE_TASK_TYPES = [
25
+ TaskType.TEXT_TO_3D,
26
+ TaskType.IMAGE_TO_3D,
27
+ TaskType.MULTI_IMAGE_TO_3D,
28
+ TaskType.REMESH,
29
+ TaskType.RETEXTURE,
30
+ TaskType.TEXT_TO_IMAGE,
31
+ TaskType.IMAGE_TO_IMAGE
32
+ ];
33
+ /**
34
+ * Map task type to its API endpoint base path
35
+ */
36
+ export function getTaskEndpoint(taskType) {
37
+ return TASK_ENDPOINTS[taskType];
38
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared API request body builder.
3
+ * Constructs a request object from required fields and optional parameters,
4
+ * omitting undefined values automatically.
5
+ */
6
+ /**
7
+ * Build an API request body by merging required fields with optional parameters.
8
+ * Undefined values in optionalParams are automatically excluded.
9
+ *
10
+ * @param requiredFields - Fields that are always included in the request
11
+ * @param optionalParams - Fields that are included only if defined
12
+ * @param excludeKeys - Keys to exclude from optionalParams (e.g., "response_format")
13
+ * @returns A flat record suitable for passing to client.post()
14
+ */
15
+ export declare function buildApiRequest(requiredFields: Record<string, unknown>, optionalParams: Record<string, unknown>, excludeKeys?: string[]): Record<string, unknown>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared API request body builder.
3
+ * Constructs a request object from required fields and optional parameters,
4
+ * omitting undefined values automatically.
5
+ */
6
+ /**
7
+ * Build an API request body by merging required fields with optional parameters.
8
+ * Undefined values in optionalParams are automatically excluded.
9
+ *
10
+ * @param requiredFields - Fields that are always included in the request
11
+ * @param optionalParams - Fields that are included only if defined
12
+ * @param excludeKeys - Keys to exclude from optionalParams (e.g., "response_format")
13
+ * @returns A flat record suitable for passing to client.post()
14
+ */
15
+ export function buildApiRequest(requiredFields, optionalParams, excludeKeys = ["response_format"]) {
16
+ const request = { ...requiredFields };
17
+ const excludeSet = new Set(excludeKeys);
18
+ for (const [key, value] of Object.entries(optionalParams)) {
19
+ if (value !== undefined && !excludeSet.has(key)) {
20
+ request[key] = value;
21
+ }
22
+ }
23
+ return request;
24
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared response formatting for task creation tools.
3
+ */
4
+ import { ResponseFormat } from "../constants.js";
5
+ export interface TaskCreatedOutput {
6
+ [x: string]: unknown;
7
+ task_id: string;
8
+ status: string;
9
+ message: string;
10
+ estimated_time: string;
11
+ }
12
+ /**
13
+ * Format a task creation response with consistent structure across all tools.
14
+ *
15
+ * @param output - Structured output data
16
+ * @param responseFormat - Desired response format (markdown or json)
17
+ * @param title - Markdown title (e.g., "3D Generation Task Created")
18
+ * @param body - Additional markdown body text (e.g., "Your 3D model is being generated...")
19
+ * @param nextStepHint - Optional hint for the task_type parameter in next steps
20
+ */
21
+ export declare function formatTaskCreatedResponse(output: TaskCreatedOutput, responseFormat: ResponseFormat, title: string, body: string, nextStepHint?: string): {
22
+ content: {
23
+ type: "text";
24
+ text: string;
25
+ }[];
26
+ structuredContent: TaskCreatedOutput;
27
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared response formatting for task creation tools.
3
+ */
4
+ import { ResponseFormat } from "../constants.js";
5
+ /**
6
+ * Format a task creation response with consistent structure across all tools.
7
+ *
8
+ * @param output - Structured output data
9
+ * @param responseFormat - Desired response format (markdown or json)
10
+ * @param title - Markdown title (e.g., "3D Generation Task Created")
11
+ * @param body - Additional markdown body text (e.g., "Your 3D model is being generated...")
12
+ * @param nextStepHint - Optional hint for the task_type parameter in next steps
13
+ */
14
+ export function formatTaskCreatedResponse(output, responseFormat, title, body, nextStepHint) {
15
+ let textContent;
16
+ if (responseFormat === ResponseFormat.MARKDOWN) {
17
+ const taskTypeParam = nextStepHint ? ` and task_type "${nextStepHint}"` : "";
18
+ textContent = `# ${title}
19
+
20
+ **Task ID**: ${output.task_id}
21
+ **Status**: ${output.status}
22
+ **Estimated Time**: ${output.estimated_time}
23
+
24
+ ${body}
25
+
26
+ **Next Steps**:
27
+ 1. **Recommended**: Use \`meshy_get_task_status\` with task_id "${output.task_id}"${taskTypeParam} to automatically wait for completion.
28
+ 2. **Alternative**: Use \`meshy_get_task_status\` with wait=false to check once.`;
29
+ }
30
+ else {
31
+ textContent = JSON.stringify(output, null, 2);
32
+ }
33
+ return {
34
+ content: [{ type: "text", text: textContent }],
35
+ structuredContent: output
36
+ };
37
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Cross-platform slicer detection utilities.
3
+ * Detects installed 3D printing slicer software and returns launch commands.
4
+ * Does NOT execute launches — returns commands for the agent to run.
5
+ */
6
+ import { SlicerType } from "../constants.js";
7
+ export interface DetectedSlicer {
8
+ type: SlicerType;
9
+ displayName: string;
10
+ supportsMulticolor: boolean;
11
+ path: string;
12
+ launchCommand: string;
13
+ urlScheme?: string;
14
+ }
15
+ /**
16
+ * Detect all installed slicer software on the current system.
17
+ * Returns an array sorted with multicolor-capable slicers first.
18
+ */
19
+ export declare function detectInstalledSlicers(): DetectedSlicer[];
20
+ /**
21
+ * Detect a specific slicer by type.
22
+ * Returns null if not installed.
23
+ */
24
+ export declare function detectSlicer(type: SlicerType): DetectedSlicer | null;
25
+ /**
26
+ * Get the best slicer for multicolor printing from a list of installed slicers.
27
+ * Returns null if no multicolor-capable slicer is installed.
28
+ */
29
+ export declare function getBestMulticolorSlicer(installed: DetectedSlicer[]): DetectedSlicer | null;