claude-cad 0.1.0__py3-none-any.whl
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.
- claude_cad/__init__.py +5 -0
- claude_cad/mock_server.py +543 -0
- claude_cad/model_generator.py +471 -0
- claude_cad/server.py +505 -0
- claude_cad/utils.py +86 -0
- claude_cad-0.1.0.dist-info/METADATA +147 -0
- claude_cad-0.1.0.dist-info/RECORD +11 -0
- claude_cad-0.1.0.dist-info/WHEEL +5 -0
- claude_cad-0.1.0.dist-info/entry_points.txt +2 -0
- claude_cad-0.1.0.dist-info/licenses/LICENSE +201 -0
- claude_cad-0.1.0.dist-info/top_level.txt +1 -0
claude_cad/server.py
ADDED
@@ -0,0 +1,505 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Claude CAD MCP Server
|
4
|
+
|
5
|
+
This server implements the Model Context Protocol (MCP) for 3D modeling
|
6
|
+
capabilities using CadQuery.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import json
|
11
|
+
import tempfile
|
12
|
+
import uuid
|
13
|
+
import shutil
|
14
|
+
from pathlib import Path
|
15
|
+
from typing import Dict, List, Optional, Any, Union
|
16
|
+
|
17
|
+
# MCP SDK imports
|
18
|
+
# The import structure has changed - adjust based on the error
|
19
|
+
from mcp.server import Server
|
20
|
+
# Import StdioTransport instead of StdioServerTransport
|
21
|
+
from mcp.transports.stdio import StdioTransport
|
22
|
+
from mcp.types import (
|
23
|
+
CallToolRequestSchema,
|
24
|
+
ListToolsRequestSchema,
|
25
|
+
ListResourcesRequestSchema,
|
26
|
+
ReadResourceRequestSchema,
|
27
|
+
ErrorCode,
|
28
|
+
McpError,
|
29
|
+
)
|
30
|
+
|
31
|
+
# CadQuery imports
|
32
|
+
import cadquery as cq
|
33
|
+
|
34
|
+
from . import model_generator
|
35
|
+
from . import utils
|
36
|
+
|
37
|
+
class ClaudeCADServer:
|
38
|
+
"""MCP Server implementation for 3D modeling with CadQuery."""
|
39
|
+
|
40
|
+
def __init__(self):
|
41
|
+
"""Initialize the CAD MCP server."""
|
42
|
+
self.server = Server(
|
43
|
+
{
|
44
|
+
"name": "claude_cad",
|
45
|
+
"version": "0.1.0",
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"capabilities": {
|
49
|
+
"resources": {},
|
50
|
+
"tools": {},
|
51
|
+
}
|
52
|
+
}
|
53
|
+
)
|
54
|
+
|
55
|
+
# Create a temp directory for storing generated models
|
56
|
+
self.temp_dir = Path(tempfile.mkdtemp())
|
57
|
+
|
58
|
+
# Dictionary to store created models
|
59
|
+
self.models: Dict[str, Dict[str, Any]] = {}
|
60
|
+
|
61
|
+
# Set up request handlers
|
62
|
+
self.setup_handlers()
|
63
|
+
self.setup_error_handling()
|
64
|
+
|
65
|
+
def setup_error_handling(self) -> None:
|
66
|
+
"""Configure error handling for the server."""
|
67
|
+
self.server.onerror = lambda error: print(f"[MCP Error] {error}", flush=True)
|
68
|
+
|
69
|
+
def setup_handlers(self) -> None:
|
70
|
+
"""Register all request handlers for the server."""
|
71
|
+
self.setup_resource_handlers()
|
72
|
+
self.setup_tool_handlers()
|
73
|
+
|
74
|
+
def setup_resource_handlers(self) -> None:
|
75
|
+
"""Set up handlers for resource-related requests."""
|
76
|
+
|
77
|
+
# Handler for listing available models as resources
|
78
|
+
self.server.setRequestHandler(
|
79
|
+
ListResourcesRequestSchema,
|
80
|
+
self.handle_list_resources
|
81
|
+
)
|
82
|
+
|
83
|
+
# Handler for reading model content
|
84
|
+
self.server.setRequestHandler(
|
85
|
+
ReadResourceRequestSchema,
|
86
|
+
self.handle_read_resource
|
87
|
+
)
|
88
|
+
|
89
|
+
def setup_tool_handlers(self) -> None:
|
90
|
+
"""Set up handlers for tool-related requests."""
|
91
|
+
|
92
|
+
# Handler for listing available CAD tools
|
93
|
+
self.server.setRequestHandler(
|
94
|
+
ListToolsRequestSchema,
|
95
|
+
self.handle_list_tools
|
96
|
+
)
|
97
|
+
|
98
|
+
# Handler for calling CAD tools
|
99
|
+
self.server.setRequestHandler(
|
100
|
+
CallToolRequestSchema,
|
101
|
+
self.handle_call_tool
|
102
|
+
)
|
103
|
+
|
104
|
+
async def handle_list_resources(self, request: Any) -> Dict[str, List[Dict[str, str]]]:
|
105
|
+
"""Handle the resources/list request to provide available 3D models.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
A dictionary containing the list of available models as resources.
|
109
|
+
"""
|
110
|
+
return {
|
111
|
+
"resources": [
|
112
|
+
{
|
113
|
+
"uri": f"model://{model_id}",
|
114
|
+
"name": model_info.get("name", f"Model {model_id}"),
|
115
|
+
"mimeType": "model/step",
|
116
|
+
"description": model_info.get("description", "A 3D model created with CadQuery")
|
117
|
+
}
|
118
|
+
for model_id, model_info in self.models.items()
|
119
|
+
]
|
120
|
+
}
|
121
|
+
|
122
|
+
async def handle_read_resource(self, request: Any) -> Dict[str, List[Dict[str, str]]]:
|
123
|
+
"""Handle the resources/read request to provide model data.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
request: The MCP request object containing the resource URI.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
A dictionary containing the model data.
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
McpError: If the requested model is not found.
|
133
|
+
"""
|
134
|
+
url = utils.parse_resource_uri(request.params.uri)
|
135
|
+
model_id = url.path.lstrip('/')
|
136
|
+
|
137
|
+
if model_id not in self.models:
|
138
|
+
raise McpError(
|
139
|
+
ErrorCode.InvalidRequest,
|
140
|
+
f"Model {model_id} not found"
|
141
|
+
)
|
142
|
+
|
143
|
+
model_info = self.models[model_id]
|
144
|
+
|
145
|
+
# Return metadata about the model
|
146
|
+
return {
|
147
|
+
"contents": [
|
148
|
+
{
|
149
|
+
"uri": request.params.uri,
|
150
|
+
"mimeType": "application/json",
|
151
|
+
"text": json.dumps(model_info, indent=2)
|
152
|
+
}
|
153
|
+
]
|
154
|
+
}
|
155
|
+
|
156
|
+
async def handle_list_tools(self, request: Any) -> Dict[str, List[Dict[str, Any]]]:
|
157
|
+
"""Handle the tools/list request to provide available CAD tools.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
A dictionary containing the list of available CAD tools.
|
161
|
+
"""
|
162
|
+
return {
|
163
|
+
"tools": [
|
164
|
+
{
|
165
|
+
"name": "create_primitive",
|
166
|
+
"description": "Create a primitive 3D shape",
|
167
|
+
"inputSchema": {
|
168
|
+
"type": "object",
|
169
|
+
"properties": {
|
170
|
+
"shape_type": {
|
171
|
+
"type": "string",
|
172
|
+
"description": "Type of primitive shape (box, sphere, cylinder, cone)",
|
173
|
+
"enum": ["box", "sphere", "cylinder", "cone"]
|
174
|
+
},
|
175
|
+
"parameters": {
|
176
|
+
"type": "object",
|
177
|
+
"description": "Parameters for the primitive shape"
|
178
|
+
},
|
179
|
+
"name": {
|
180
|
+
"type": "string",
|
181
|
+
"description": "Name of the model"
|
182
|
+
},
|
183
|
+
"description": {
|
184
|
+
"type": "string",
|
185
|
+
"description": "Description of the model"
|
186
|
+
}
|
187
|
+
},
|
188
|
+
"required": ["shape_type", "parameters"]
|
189
|
+
}
|
190
|
+
},
|
191
|
+
{
|
192
|
+
"name": "create_model_from_text",
|
193
|
+
"description": "Create a 3D model from a text description using CadQuery",
|
194
|
+
"inputSchema": {
|
195
|
+
"type": "object",
|
196
|
+
"properties": {
|
197
|
+
"description": {
|
198
|
+
"type": "string",
|
199
|
+
"description": "Natural language description of the 3D model to create"
|
200
|
+
},
|
201
|
+
"name": {
|
202
|
+
"type": "string",
|
203
|
+
"description": "Name of the model"
|
204
|
+
},
|
205
|
+
"format": {
|
206
|
+
"type": "string",
|
207
|
+
"description": "Export format (step, stl)",
|
208
|
+
"enum": ["step", "stl"],
|
209
|
+
"default": "step"
|
210
|
+
}
|
211
|
+
},
|
212
|
+
"required": ["description"]
|
213
|
+
}
|
214
|
+
},
|
215
|
+
{
|
216
|
+
"name": "execute_cadquery_script",
|
217
|
+
"description": "Execute custom CadQuery Python code to create a 3D model",
|
218
|
+
"inputSchema": {
|
219
|
+
"type": "object",
|
220
|
+
"properties": {
|
221
|
+
"code": {
|
222
|
+
"type": "string",
|
223
|
+
"description": "Python code using CadQuery to create a model"
|
224
|
+
},
|
225
|
+
"name": {
|
226
|
+
"type": "string",
|
227
|
+
"description": "Name of the model"
|
228
|
+
},
|
229
|
+
"description": {
|
230
|
+
"type": "string",
|
231
|
+
"description": "Description of the model"
|
232
|
+
},
|
233
|
+
"format": {
|
234
|
+
"type": "string",
|
235
|
+
"description": "Export format (step, stl)",
|
236
|
+
"enum": ["step", "stl"],
|
237
|
+
"default": "step"
|
238
|
+
}
|
239
|
+
},
|
240
|
+
"required": ["code"]
|
241
|
+
}
|
242
|
+
}
|
243
|
+
]
|
244
|
+
}
|
245
|
+
|
246
|
+
async def handle_call_tool(self, request: Any) -> Dict[str, List[Dict[str, str]]]:
|
247
|
+
"""Handle the tools/call request to execute a CAD tool.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
request: The MCP request object containing the tool name and arguments.
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
A dictionary containing the result of the tool execution.
|
254
|
+
|
255
|
+
Raises:
|
256
|
+
McpError: If the requested tool is not found or if there's an error executing it.
|
257
|
+
"""
|
258
|
+
tool_name = request.params.name
|
259
|
+
args = request.params.arguments or {}
|
260
|
+
|
261
|
+
try:
|
262
|
+
if tool_name == "create_primitive":
|
263
|
+
return await self._handle_create_primitive(args)
|
264
|
+
elif tool_name == "create_model_from_text":
|
265
|
+
return await self._handle_create_model_from_text(args)
|
266
|
+
elif tool_name == "execute_cadquery_script":
|
267
|
+
return await self._handle_execute_cadquery_script(args)
|
268
|
+
else:
|
269
|
+
raise McpError(
|
270
|
+
ErrorCode.MethodNotFound,
|
271
|
+
f"Unknown tool: {tool_name}"
|
272
|
+
)
|
273
|
+
except Exception as e:
|
274
|
+
if isinstance(e, McpError):
|
275
|
+
raise
|
276
|
+
raise McpError(
|
277
|
+
ErrorCode.InternalError,
|
278
|
+
f"Error executing {tool_name}: {str(e)}"
|
279
|
+
)
|
280
|
+
|
281
|
+
async def _handle_create_primitive(self, args: Dict[str, Any]) -> Dict[str, List[Dict[str, str]]]:
|
282
|
+
"""Handle the create_primitive tool request.
|
283
|
+
|
284
|
+
Args:
|
285
|
+
args: The tool arguments containing the primitive shape details.
|
286
|
+
|
287
|
+
Returns:
|
288
|
+
A dictionary containing the result of the primitive creation.
|
289
|
+
"""
|
290
|
+
shape_type = args.get("shape_type")
|
291
|
+
parameters = args.get("parameters", {})
|
292
|
+
name = args.get("name", f"Primitive {shape_type.capitalize()}")
|
293
|
+
description = args.get("description", f"A {shape_type} created with CadQuery")
|
294
|
+
|
295
|
+
# Generate the model using CadQuery
|
296
|
+
try:
|
297
|
+
model = model_generator.create_primitive(shape_type, parameters)
|
298
|
+
|
299
|
+
# Save the model and store metadata
|
300
|
+
model_id = self._save_model(model, name, description)
|
301
|
+
|
302
|
+
# Return success message with model information
|
303
|
+
return {
|
304
|
+
"content": [
|
305
|
+
{
|
306
|
+
"type": "text",
|
307
|
+
"text": f"Created {shape_type} model with ID: {model_id}\n"
|
308
|
+
f"You can access this model as a resource with URI: model://{model_id}"
|
309
|
+
}
|
310
|
+
]
|
311
|
+
}
|
312
|
+
except Exception as e:
|
313
|
+
raise McpError(
|
314
|
+
ErrorCode.InternalError,
|
315
|
+
f"Error creating primitive: {str(e)}"
|
316
|
+
)
|
317
|
+
|
318
|
+
async def _handle_create_model_from_text(self, args: Dict[str, Any]) -> Dict[str, List[Dict[str, str]]]:
|
319
|
+
"""Handle the create_model_from_text tool request.
|
320
|
+
|
321
|
+
Args:
|
322
|
+
args: The tool arguments containing the text description of the model.
|
323
|
+
|
324
|
+
Returns:
|
325
|
+
A dictionary containing the result of the model creation.
|
326
|
+
"""
|
327
|
+
description = args.get("description")
|
328
|
+
name = args.get("name", "Text-generated Model")
|
329
|
+
format_type = args.get("format", "step")
|
330
|
+
|
331
|
+
if not description:
|
332
|
+
raise McpError(
|
333
|
+
ErrorCode.InvalidParams,
|
334
|
+
"Model description is required"
|
335
|
+
)
|
336
|
+
|
337
|
+
try:
|
338
|
+
# Generate model from text description
|
339
|
+
model, code = model_generator.create_from_text(description)
|
340
|
+
|
341
|
+
# Save the model
|
342
|
+
model_id = self._save_model(model, name, description, code, format_type)
|
343
|
+
|
344
|
+
# Return success message with model information and generated code
|
345
|
+
return {
|
346
|
+
"content": [
|
347
|
+
{
|
348
|
+
"type": "text",
|
349
|
+
"text": f"Created model from description with ID: {model_id}\n"
|
350
|
+
f"You can access this model as a resource with URI: model://{model_id}\n\n"
|
351
|
+
f"Generated CadQuery code:\n```python\n{code}\n```"
|
352
|
+
}
|
353
|
+
]
|
354
|
+
}
|
355
|
+
except Exception as e:
|
356
|
+
raise McpError(
|
357
|
+
ErrorCode.InternalError,
|
358
|
+
f"Error creating model from text: {str(e)}"
|
359
|
+
)
|
360
|
+
|
361
|
+
async def _handle_execute_cadquery_script(self, args: Dict[str, Any]) -> Dict[str, List[Dict[str, str]]]:
|
362
|
+
"""Handle the execute_cadquery_script tool request.
|
363
|
+
|
364
|
+
Args:
|
365
|
+
args: The tool arguments containing the CadQuery Python code.
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
A dictionary containing the result of the script execution.
|
369
|
+
"""
|
370
|
+
code = args.get("code")
|
371
|
+
name = args.get("name", "Custom CadQuery Model")
|
372
|
+
description = args.get("description", "A model created with custom CadQuery code")
|
373
|
+
format_type = args.get("format", "step")
|
374
|
+
|
375
|
+
if not code:
|
376
|
+
raise McpError(
|
377
|
+
ErrorCode.InvalidParams,
|
378
|
+
"CadQuery code is required"
|
379
|
+
)
|
380
|
+
|
381
|
+
try:
|
382
|
+
# Execute the CadQuery script
|
383
|
+
model = model_generator.execute_script(code)
|
384
|
+
|
385
|
+
# Save the model
|
386
|
+
model_id = self._save_model(model, name, description, code, format_type)
|
387
|
+
|
388
|
+
# Return success message with model information
|
389
|
+
return {
|
390
|
+
"content": [
|
391
|
+
{
|
392
|
+
"type": "text",
|
393
|
+
"text": f"Successfully executed CadQuery code and created model with ID: {model_id}\n"
|
394
|
+
f"You can access this model as a resource with URI: model://{model_id}"
|
395
|
+
}
|
396
|
+
]
|
397
|
+
}
|
398
|
+
except Exception as e:
|
399
|
+
raise McpError(
|
400
|
+
ErrorCode.InternalError,
|
401
|
+
f"Error executing CadQuery script: {str(e)}"
|
402
|
+
)
|
403
|
+
|
404
|
+
def _save_model(self,
|
405
|
+
model: cq.Workplane,
|
406
|
+
name: str,
|
407
|
+
description: str,
|
408
|
+
code: Optional[str] = None,
|
409
|
+
format_type: str = "step") -> str:
|
410
|
+
"""Save a CadQuery model to disk and store its metadata.
|
411
|
+
|
412
|
+
Args:
|
413
|
+
model: The CadQuery Workplane object.
|
414
|
+
name: The name of the model.
|
415
|
+
description: The description of the model.
|
416
|
+
code: The CadQuery code used to generate the model (optional).
|
417
|
+
format_type: The export format (step, stl).
|
418
|
+
|
419
|
+
Returns:
|
420
|
+
The generated model ID.
|
421
|
+
"""
|
422
|
+
model_id = str(uuid.uuid4())
|
423
|
+
model_dir = self.temp_dir / model_id
|
424
|
+
model_dir.mkdir(exist_ok=True)
|
425
|
+
|
426
|
+
# Export the model in the requested format
|
427
|
+
if format_type.lower() == "stl":
|
428
|
+
file_path = model_dir / f"{model_id}.stl"
|
429
|
+
model.exportStl(str(file_path))
|
430
|
+
mime_type = "model/stl"
|
431
|
+
else: # Default to STEP
|
432
|
+
file_path = model_dir / f"{model_id}.step"
|
433
|
+
model.exportStep(str(file_path))
|
434
|
+
mime_type = "model/step"
|
435
|
+
|
436
|
+
# Store model metadata
|
437
|
+
self.models[model_id] = {
|
438
|
+
"id": model_id,
|
439
|
+
"name": name,
|
440
|
+
"description": description,
|
441
|
+
"file_path": str(file_path),
|
442
|
+
"mime_type": mime_type,
|
443
|
+
"format": format_type,
|
444
|
+
"code": code
|
445
|
+
}
|
446
|
+
|
447
|
+
return model_id
|
448
|
+
|
449
|
+
async def run(self) -> None:
|
450
|
+
"""Start the CAD MCP server."""
|
451
|
+
# Use StdioTransport instead of StdioServerTransport
|
452
|
+
transport = StdioTransport()
|
453
|
+
await self.server.connect(transport)
|
454
|
+
print("Claude CAD MCP server running", flush=True)
|
455
|
+
|
456
|
+
async def cleanup(self) -> None:
|
457
|
+
"""Clean up resources used by the server."""
|
458
|
+
try:
|
459
|
+
# Remove temporary directory
|
460
|
+
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
461
|
+
except Exception as e:
|
462
|
+
print(f"Error during cleanup: {e}", flush=True)
|
463
|
+
|
464
|
+
# Close the server connection
|
465
|
+
await self.server.close()
|
466
|
+
|
467
|
+
|
468
|
+
def main() -> None:
|
469
|
+
"""Entry point for the Claude CAD MCP server."""
|
470
|
+
import asyncio
|
471
|
+
import signal
|
472
|
+
import sys
|
473
|
+
|
474
|
+
# Create and run the server
|
475
|
+
server = ClaudeCADServer()
|
476
|
+
|
477
|
+
# Set up signal handlers
|
478
|
+
loop = asyncio.get_event_loop()
|
479
|
+
|
480
|
+
for sig in [signal.SIGINT, signal.SIGTERM]:
|
481
|
+
loop.add_signal_handler(sig, lambda: asyncio.create_task(shutdown(server, loop)))
|
482
|
+
|
483
|
+
try:
|
484
|
+
loop.run_until_complete(server.run())
|
485
|
+
loop.run_forever()
|
486
|
+
except Exception as e:
|
487
|
+
print(f"Server error: {e}", flush=True)
|
488
|
+
sys.exit(1)
|
489
|
+
|
490
|
+
|
491
|
+
async def shutdown(server: ClaudeCADServer, loop: asyncio.AbstractEventLoop) -> None:
|
492
|
+
"""Gracefully shut down the server."""
|
493
|
+
print("Shutting down server...", flush=True)
|
494
|
+
await server.cleanup()
|
495
|
+
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
|
496
|
+
|
497
|
+
for task in tasks:
|
498
|
+
task.cancel()
|
499
|
+
|
500
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
501
|
+
loop.stop()
|
502
|
+
|
503
|
+
|
504
|
+
if __name__ == "__main__":
|
505
|
+
main()
|
claude_cad/utils.py
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
"""
|
2
|
+
Utility functions for Claude CAD.
|
3
|
+
|
4
|
+
This module contains helper functions used throughout the Claude CAD package.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import tempfile
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import Dict, Any, Union
|
11
|
+
from urllib.parse import urlparse
|
12
|
+
|
13
|
+
|
14
|
+
def parse_resource_uri(uri: str) -> Any:
|
15
|
+
"""Parse a resource URI into its components.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
uri: The resource URI to parse (e.g., 'model://123456').
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
A parsed URL object.
|
22
|
+
"""
|
23
|
+
return urlparse(uri)
|
24
|
+
|
25
|
+
|
26
|
+
def get_temp_file_path(suffix: str = '') -> Path:
|
27
|
+
"""Create a temporary file path with the given suffix.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
suffix: File extension with dot (e.g., '.step').
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
Path object representing a temporary file path.
|
34
|
+
"""
|
35
|
+
fd, path = tempfile.mkstemp(suffix=suffix)
|
36
|
+
os.close(fd)
|
37
|
+
return Path(path)
|
38
|
+
|
39
|
+
|
40
|
+
def ensure_directory_exists(directory_path: Union[str, Path]) -> Path:
|
41
|
+
"""Ensure that a directory exists, creating it if necessary.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
directory_path: Path to the directory to create.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
Path object representing the directory.
|
48
|
+
"""
|
49
|
+
path = Path(directory_path)
|
50
|
+
path.mkdir(parents=True, exist_ok=True)
|
51
|
+
return path
|
52
|
+
|
53
|
+
|
54
|
+
def supported_export_formats() -> Dict[str, Dict[str, Any]]:
|
55
|
+
"""Get a dictionary of supported export formats.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
A dictionary mapping format names to their properties.
|
59
|
+
"""
|
60
|
+
return {
|
61
|
+
"step": {
|
62
|
+
"extension": ".step",
|
63
|
+
"mime_type": "model/step",
|
64
|
+
"description": "STEP File Format (ISO 10303)",
|
65
|
+
},
|
66
|
+
"stl": {
|
67
|
+
"extension": ".stl",
|
68
|
+
"mime_type": "model/stl",
|
69
|
+
"description": "STL File Format (Standard Triangle Language)",
|
70
|
+
},
|
71
|
+
"dxf": {
|
72
|
+
"extension": ".dxf",
|
73
|
+
"mime_type": "application/dxf",
|
74
|
+
"description": "DXF File Format (Drawing Exchange Format)",
|
75
|
+
},
|
76
|
+
"svg": {
|
77
|
+
"extension": ".svg",
|
78
|
+
"mime_type": "image/svg+xml",
|
79
|
+
"description": "SVG File Format (Scalable Vector Graphics)",
|
80
|
+
},
|
81
|
+
"gltf": {
|
82
|
+
"extension": ".gltf",
|
83
|
+
"mime_type": "model/gltf+json",
|
84
|
+
"description": "glTF File Format (GL Transmission Format)",
|
85
|
+
},
|
86
|
+
}
|