Skip to main content
Glama

OpenSCAD MCP Server

by jhacksman
format_validator.py7.83 kB
import os import logging import zipfile import xml.etree.ElementTree as ET from typing import Tuple, Optional, Dict, Any logger = logging.getLogger(__name__) class FormatValidator: """Validates 3D model formats for compatibility with printers.""" @staticmethod def validate_3mf(file_path: str) -> Tuple[bool, Optional[str]]: """ Validate a 3MF file for compatibility with Prusa and Bambu printers. Args: file_path: Path to the 3MF file Returns: Tuple of (is_valid, error_message) """ if not os.path.exists(file_path): return False, f"File not found: {file_path}" try: # 3MF files are ZIP archives with XML content with zipfile.ZipFile(file_path, 'r') as zip_ref: # Check for required files required_files = ['3D/3dmodel.model', '[Content_Types].xml'] for req_file in required_files: try: zip_ref.getinfo(req_file) except KeyError: return False, f"Missing required file in 3MF: {req_file}" # Validate 3D model file with zip_ref.open('3D/3dmodel.model') as model_file: tree = ET.parse(model_file) root = tree.getroot() # Check for required elements if root.tag != '{http://schemas.microsoft.com/3dmanufacturing/core/2015/02}model': return False, "Invalid 3MF: Missing model element" # Verify resources section exists resources = root.find('.//{http://schemas.microsoft.com/3dmanufacturing/core/2015/02}resources') if resources is None: return False, "Invalid 3MF: Missing resources element" return True, None except Exception as e: logger.error(f"Error validating 3MF file: {str(e)}") return False, f"Error validating 3MF file: {str(e)}" @staticmethod def validate_amf(file_path: str) -> Tuple[bool, Optional[str]]: """ Validate an AMF file for compatibility with printers. Args: file_path: Path to the AMF file Returns: Tuple of (is_valid, error_message) """ if not os.path.exists(file_path): return False, f"File not found: {file_path}" try: # Parse the AMF file (XML format) tree = ET.parse(file_path) root = tree.getroot() # Check for required elements if root.tag != 'amf': return False, "Invalid AMF: Missing amf root element" # Check for at least one object objects = root.findall('./object') if not objects: return False, "Invalid AMF: No objects found" # Check that each object has a mesh for obj in objects: mesh = obj.find('./mesh') if mesh is None: return False, f"Invalid AMF: Object {obj.get('id', 'unknown')} is missing a mesh" # Check for vertices and volumes vertices = mesh.find('./vertices') volumes = mesh.findall('./volume') if vertices is None: return False, f"Invalid AMF: Mesh in object {obj.get('id', 'unknown')} is missing vertices" if not volumes: return False, f"Invalid AMF: Mesh in object {obj.get('id', 'unknown')} has no volumes" return True, None except Exception as e: logger.error(f"Error validating AMF file: {str(e)}") return False, f"Error validating AMF file: {str(e)}" @staticmethod def extract_metadata(file_path: str) -> Dict[str, Any]: """ Extract metadata from a 3MF or AMF file. Args: file_path: Path to the 3D model file Returns: Dictionary of metadata """ metadata = {} # Check file extension ext = os.path.splitext(file_path)[1].lower() try: if ext == '.3mf': with zipfile.ZipFile(file_path, 'r') as zip_ref: metadata_path = "Metadata/model_metadata.xml" try: with zip_ref.open(metadata_path) as f: tree = ET.parse(f) root = tree.getroot() for meta in root.findall('./meta'): name = meta.get('name') if name: metadata[name] = meta.text except KeyError: # Metadata file doesn't exist pass elif ext == '.amf': tree = ET.parse(file_path) root = tree.getroot() for meta in root.findall('./metadata'): name = meta.get('type') if name: metadata[name] = meta.text except Exception as e: logger.error(f"Error extracting metadata: {str(e)}") return metadata @staticmethod def check_printer_compatibility(file_path: str, printer_type: str = "prusa") -> Tuple[bool, Optional[str]]: """ Check if a 3D model file is compatible with a specific printer type. Args: file_path: Path to the 3D model file printer_type: Type of printer ("prusa" or "bambu") Returns: Tuple of (is_compatible, error_message) """ # Check file extension ext = os.path.splitext(file_path)[1].lower() # Validate based on file format if ext == '.3mf': is_valid, error = FormatValidator.validate_3mf(file_path) if not is_valid: return False, error # Additional printer-specific checks if printer_type.lower() == "prusa": # Prusa-specific checks for 3MF # For now, just basic validation is sufficient return True, None elif printer_type.lower() == "bambu": # Bambu-specific checks for 3MF # For now, just basic validation is sufficient return True, None else: return False, f"Unknown printer type: {printer_type}" elif ext == '.amf': is_valid, error = FormatValidator.validate_amf(file_path) if not is_valid: return False, error # Additional printer-specific checks if printer_type.lower() == "prusa": # Prusa-specific checks for AMF # For now, just basic validation is sufficient return True, None elif printer_type.lower() == "bambu": # Bambu-specific checks for AMF # For now, just basic validation is sufficient return True, None else: return False, f"Unknown printer type: {printer_type}" else: return False, f"Unsupported file format for printer compatibility check: {ext}"

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jhacksman/OpenSCAD-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server