Everything the desktop app and the MCP server can do is a method on MeshService.
from engine.service import MeshService
svc = MeshService() # autosave=True by default
svc.load("miniature.stl")
print(svc.current.analysis["verdict"]) # 'Repair required'
svc.repair() # measured, verified, undoable
svc.fix_slivers(min_angle_deg=1.0)
svc.retopo(20000, method="quadriflow")
svc.export_stl("miniature_print_ready.stl", scale_unit="mm", align_origin=True)
svc.export_report("miniature_report.json")
svc.close() # clears the crash-recovery snapshotsMeshService(log=None, autosave=True, progress=None)-
log(message, level)— called for every step;levelisinfo/ok/warn/error. A logger that raises can never abort an operation. -
autosave— write a background snapshot of every accepted state for crash recovery. -
progress(**event)— called when a long operation starts and finishes:{"state": "start", "operation": "retopo", "label": "Smart retopology to 50,000 faces", "faces": 1994490, "eta": 92.7, "eta_text": "about 1–3 minutes"} {"state": "done", "operation": "retopo", "label": "...", "elapsed": 88.4}Use it to drive a progress bar or a status line.
engine.service.estimate_seconds(operation, faces)anddescribe_duration(seconds)are available on their own if you want the estimate without running anything.
| Method | Returns |
|---|---|
load(path) |
Full result: analysis, stats, shells, preview, state id |
analyze() |
Fresh diagnostics for the current mesh |
repair(strict_watertight=True, force=False) |
Result + report with fixes, changes, passes |
fix_slivers(min_angle_deg=1.0, force=False) |
Result + info (before, after, collapsed, flipped, skipped) |
simplify(keep_fraction=0.5, force=False) |
Result + info including deviation |
retopo(target_faces, method="quadriflow", preserve_sharp=True, adaptive=True) |
Result + info |
remove_shells(indices) |
Result for the remaining geometry |
rotate(matrix) |
stats, centre, bounds — no geometry payload |
undo() / redo() / revert() |
Result for the state you land on |
state_list() |
[{id, operation, verdict, score, faces}, …] |
export_stl(path, scale_unit="mm", align_origin=True) |
{"result": {...}} |
report() / export_report(path) |
The full JSON report |
svc.mesh— the currenttrimesh.Trimeshsvc.current— the current state (id,mesh,analysis,operation,shells)svc.shells— list oftrimesh.Trimeshwhen the model has several piecessvc.original— a pristine copy of the loaded filesvc.history— journal of accepted operations
from engine.service import ServiceError # no model loaded, impossible request
from engine.validation import ValidationError # bad path, bad number, bad matrixBoth are exceptions here. The desktop and MCP adapters convert them to
{"success": false, "error": "..."}.
Mutating calls run through _commit, which refuses a result that would increase the number of
critical issues or discard more than 95 % of the geometry:
res = svc.repair()
if not res["success"] and res.get("rejected"):
print(res["reason"]) # 'critical problems would increase from 0 to 2'
res = svc.repair(force=True) # apply anyway, deliberatelysimplify and retopo are exempt from the geometry-loss rule — dropping faces is the point — but
they still verify the result and log a warning if new problems appear.
fix_slivers reports skipped: slivers it refused to remove because the collapse or flip would
have torn the surface. A few stubborn slivers are harmless; a hole is not.
for s in MeshService.recoverable_sessions():
print(s["session"], s["source_file"], s["last"])
svc.recover(session_id) # restores the last good state
MeshService.discard_session(session_id)If you want a single algorithm without the state machine:
from engine.mesh_analysis import analyze_mesh, compare_analyses
from engine.mesh_repair import repair_mesh
from engine.mesh_cleanup import fix_slivers
from engine.mesh_retopo import retopologize, deviation
from engine.mesh_reducer import reduce_mesh
from engine.stl_exporter import export_to_stl
from engine.model_loader import load_modelEach is pure: mesh in, mesh + report out. No global state, no UI.