Skip to content

MCP server ​

Spatial pipeline Version 2 ​

The existing pipeline tool accepts Version 2: Merge/Interpolate/UndoGreen with auxiliary path Inputs, followed optionally by terminal Split/Partition with Output.Pattern. Input/output overrides remain available; output_path in the response is the resolved template for fan-out. The sandbox checks the settings file, main input, every auxiliary input and every expanded output (including existing symlinks) before any output writer runs. No implicit spatial/transient cross-product is performed; transient sequence pipelines remain Version 1. Exodus series fan-in is reachable through the existing sequence tool in netCDF-enabled builds.

Pipeline reports ​

The pipeline tool's existing steps/warnings report matches the additive native C/Fortran/Julia/R report JSON APIs; MCP signatures and sandbox behavior are unchanged by the flat-ABI report addition. See pipeline reports.

MED multi-mesh files ​

The med_multi tool lists meshes with input_path, extracts one with input_path, mesh_name and output_path (optional output_format), or combines nonempty input_paths into a MED output_path with optional mesh_names and med_version (default 4.1.0). Its JSON report contains mesh_names for list/combine or mesh_name for extraction, plus the resolved input/output path. Every input and output is sandbox-resolved before any I/O. Named nodal/element profiles expand with NaN fill; enhanced field metadata may use the Python/h5py reader. See MED.

The pipeline tool accepts Output.Codec: "lzf" for PCD compressed binary, matching convert's existing compression: "lzf". LZF is rejected for non-PCD formats and explicit ASCII encoding. Native XDMF2/reference and Netgen extras support is also available through the existing path-based tools; tool signatures and report fields are unchanged.

XDMF series reads and conversions preserve fixed shared point/cell/side regions, and sequence fan-in to XDMF writes the first input mesh's regions alongside the static topology. Later step regions do not change that fixed membership. See shared named regions.

Expose every meshio++ operation to AI agents over the Model Context Protocol: reading and writing 80+ mesh formats, conversion, and the full mesh- and data-operation suite become tools any MCP client (Claude Code, Claude Desktop, the MCP inspector, …) can call.

bash
pip install "meshioplusplus[mcp]"          # the mcp SDK needs Python >= 3.10; SDK 1.x and 2.x both work
claude mcp add meshioplusplus -- meshioplusplus-mcp

Then ask the agent things like "convert bracket.msh to VTU, report its quality, and slice it at z = 0.02" — it drives convert, quality and slice itself.

Every tool is file-path based: input path(s) in, output path(s) out, a strict-JSON report back. That mirrors the CLI, keeps arbitrarily large meshes out of the protocol, and lets the agent work in its own filesystem workspace. The only state kept between calls is a read cache. Nothing here is part of the C++ core, which stays dependency-free.

convert reads Gmsh 2.2/4.0/4.1 inputs, including periodic links, through the native core. The 4.0 binary path accepts 4- or 8-byte producer counts. Gmsh output remains 4.1 (gmsh) or 2.2 (gmsh22), both preserving periodic links; other formats need not carry Gmsh-specific metadata. See Gmsh.

Serial VTK XML input (vtu, vtp, vts, vtr, vti) accepts multiple pieces without welding and appended raw/base64 arrays through the native core, including UInt32/UInt64 headers and either byte order. points_only and arrays still narrow data loading. VTP/VTS/VTR/VTI write inline output; mode="raw_appended" remains VTU-only. Legacy vtk structured points, structured grids and rectilinear grids also convert natively, in ASCII and big-endian binary. See formats.

Installation ​

ExtraBringsFor
meshioplusplus[mcp]the official MCP Python SDK (MIT)the meshioplusplus-mcp server
meshioplusplus[dashboard]the SDK plus Starlette and uvicorn (both BSD)meshioplusplus-mcp --http — the browser dataset manager's companion process and MCP over HTTP

This extra is not in meshioplusplus[all] — [all] means "the optional dependencies the formats need", the same reasoning as [interop]/[viewer]. Note the mcp SDK itself requires Python ≥ 3.10 while meshio++ supports 3.8: the package (and the server's pure tool layer) works everywhere, only running the server needs the newer Python.

Without the extra, everything degrades by name rather than by traceback:

$ meshioplusplus-mcp
meshio++: mcp server: mcp is not installed; install it with
`pip install meshioplusplus[mcp]` (requires Python >= 3.10)

meshioplusplus.mcp.has_mcp() answers the same question without raising.

Running the server ​

The server speaks stdio (the standard local-agent transport):

bash
meshioplusplus-mcp                          # unrestricted paths
meshioplusplus-mcp --root /path/to/work     # sandboxed (recommended)
python -m meshioplusplus.mcp                # equivalent

Claude Desktop (claude_desktop_config.json):

json
{
  "mcpServers": {
    "meshioplusplus": {
      "command": "meshioplusplus-mcp",
      "args": ["--root", "/path/to/workspace"]
    }
  }
}

Interactive browsing: npx @modelcontextprotocol/inspector meshioplusplus-mcp.

Over HTTP (--http) ​

The same process can serve over HTTP instead (v10.23.0, the [dashboard] extra): MCP over streamable HTTP at /mcp for agents, plus a small JSON API the dataset dashboard calls — GET /api/health, POST /api/tools/<name> (every tool below, dispatched through the same sandbox and sanitizer) and GET /api/files (a sandboxed download).

bash
pip install "meshioplusplus[dashboard]"
meshioplusplus-mcp --http --root /path/to/cases        # 127.0.0.1:8765, a fresh token is printed
claude mcp add --transport http meshioplusplus http://127.0.0.1:8765/mcp \
  --header "Authorization: Bearer <token>"

Flags: --host (default 127.0.0.1; binding elsewhere prints a warning), --port (default 8765), --token to fix the bearer token instead of generating one, --no-token to require none (only on a machine you alone use), --allow-origin ORIGIN (repeatable; loopback origins on any port and the hosted docs site are always admitted), --runs-dir (where training runs land) and --webhook URL (POSTed once per job when it finishes, fails or is stopped — see the dashboard; server-side by design, since a client-supplied URL the server then fetches is server-side request forgery). Every /api/* and /mcp* request needs Authorization: Bearer <token>; a missing or wrong token is a 401 {"error", "error_type": "PermissionError"} payload. The SDK's own DNS-rebinding protection stays on for /mcp; on an SDK older than streamable HTTP the endpoint is the SSE transport instead (GET /api/health reports which).

Path sandbox ​

By default paths are unrestricted — the server runs locally under your own account and MCP clients gate filesystem access themselves. Pass --root DIR (or set MESHIOPLUSPLUS_MCP_ROOT) to confine every input and output path: each path is realpath-resolved (symlinks included) and must stay inside the root; relative paths resolve against it. Violations come back as clean {"error": ..., "error_type": "ValueError"} payloads the agent can act on — no tool ever surfaces a raw traceback.

Read cache ​

An agent usually asks several questions of one file in a row — stats, quality, data_info, regions, bandwidth — and each used to parse it again. The server keeps the meshes it has parsed, least recently used first, within a byte budget set by MESHIOPLUSPLUS_MCP_CACHE_MB (default 512; 0 turns the cache off). An entry is keyed on the file's identity — resolved path, device, inode, size, and modification and change times in nanoseconds — plus the format and read options (time_step, piece, ghosts), so a file rewritten by anyone, the agent's own tools included, is read again. Every tool gets a private copy, never the cached mesh, and writing a file through the server drops its entry.

Formats whose reader follows other files are never cached, because the entry file's stat cannot see a change to the rest: XDMF with its HDF5 data, .pvtu/.pvtp pieces, .pvd, .vtm, EnSight, GiD with its results file, glTF with its buffers, TetGen and Triangle pairs, decks with include files (Abaqus, LS-DYNA, Nastran), directory formats (OpenFOAM, pmsh, Zarr, ADIOS2 .bp) and the other multi-file readers listed in _CACHE_UNSAFE_FORMATS.

Tools ​

MDPA-to-MDPA convert preserves nested sub-model-part geometry/constraint memberships as raw file ids, through the Python format side channel. Constraints remain opaque; the lists are not mesh-cell indices and operations do not remap them. Other output formats need not preserve MDPA-specific content. See MDPA.

123 tools; the six marked gated need a further extra and return a named error without it. Transforming tools take input_path/output_path (+ optional input_format/output_format, otherwise inferred from the extension) and return the written path plus a mesh summary and the operation's report. A mesh carrying a B-rep side channel (read from Kratos CAD JSON, STEP or IGES, or made by fit_cad) adds a brep entry to the summary: its faces, trimmed and rational faces, loops, edges and vertices, by count.

Inspection (read-only) ​

ToolReturns
formatsreadable/writable format lists + extension map (also the meshioplusplus://formats resource)
sniffformat identified from leading bytes (a directory: the files it holds — Elmer, OpenFOAM, a DOLFINx VTX .bp) + extension
infofast file summary via read_metadata — counts, cell blocks, data names, regions, time steps
statsbbox, centroid, areas/volumes, per-type counts, inverted cells
qualityper-metric summaries + histograms; pass output_path to write the mesh with quality:<metric> cell data
check_qualitythe quality gate: require thresholds ("scaled_jacobian >= 0.2; aspect_ratio <= 5 @ 1%") tested per cell plus max_inverted/max_degenerate; passed and one entry per check (violations, fraction, worst value and cell) — see quality gate
data_infoevery data array's dtype/shape/ranges/NaN counts
data_integratecell-measure-weighted total/mean of one or more cell_data arrays, whole-mesh and per named Cell region
regionsnamed point/cell/side groups with kind/dim/tag and an entries preview
bandwidthnode-numbering bandwidth
data_previewa bounded window (offset/limit) of one data array's values
diffverdict + equal boolean + per-section detail for two files
hausdorffthe Hausdorff distance between the surfaces of two files, the one-sided maxima with mean/RMS and the worst samples; face_samples tightens the vertex-only lower bound, max_distance adds passed — see Hausdorff distance
periodicperiodic node pairs: which master-region node each slave-region node maps onto under translate / rotate_axis+rotate_degrees (about origin) / matrix, within atol; num_pairs, pairs, unmatched, num_fixed, max_residual, and pairs_path also writes slave,master CSV — see periodic node pairs
region_adjacencywrites conforming facets shared by selected Cell regions (or cell blocks when fewer than two Cell regions exist), with parent cell/facet ids, per-facet measure, total facet count and measure — see region adjacency
find_interfacewrites conforming or proximity-matched facets between two Cell regions; accepts an optional second mesh and reports per-part (cell, facet) Side entries — see region adjacency
contact_pairsprojects Point-region nodes to closest Cell-region facets and returns master cell/facet ids, local coordinates, signed gaps, normals and unmatched points
split_interfaceduplicates node fans along a Side region or explicit (cell, facet) entries; optionally inserts cohesive cells, and refuses polyhedra
surface_watertight_checka surface's boundary, non-manifold, inconsistent-orientation and degenerate counts (the same check voxelize/compute_sdf/distance_to_surface/sample_distance skip via watertight_check="off" when called as tools)

Conversion ​

ToolNotes
convertany-to-any format conversion; points_only/arrays/time_step narrow the read and piece keeps one partition/block of a partitioned file (VTKHDF, .pvtu/.pvtp, one part of a .pvd step, or one rank of a parallel MFEM mesh) instead of the merged mesh and ghosts: keep|drop keeps or removes the ghost cells (halo) of a .pvtu/.pvtp/.pvd (stats takes both too); mode: ascii|binary|raw_appended (raw_appended: a .vtu's arrays as raw binary in one <AppendedData> section) (binary on a .frd is CalculiX's binary result layout: float64 points, float32 results) and compression: zlib|lz4|zstd|lzma|gzip|lzf|none (lzf is PCD's binary_compressed; gzip/none also for vtkhdf; the VTK XML block codecs also reach .pvd, .pvtu and .pvtp, whose pieces they compress) subsume the CLI's ascii/binary/compress/decompress verbs; grid_functions ({name: path}) reads MFEM .gf fields onto an MFEM .mesh input, patran_results ({name: path}) reads Patran 2.5 result files (.nod/.dis nodal, .els element, text or binary) onto a Patran neutral input, and write_grid_functions writes an MFEM output's data as <stem>.<name>.gf files beside it (a .mesh target is then MFEM, not Medit); an MFEM NURBS input keeps its patches, so an MFEM output is written back as the NURBS mesh, and write_nurbs (false writes the sampled cells, true refuses anything but the NURBS mesh) overrides that (v16.40.0); elmer_halo adds ElmerGrid's -halo layer to the partitioning.<N> an Elmer output writes from partition:part
pipelinerun a whole settings pipeline (settings_path; read → operation chain → write, PascalCase ops/keys); input_path/output_path override the document's paths, and the sandbox root covers the paths inside the settings file, not just the file itself; a CAD JSON, STEP, IGES or MFEM NURBS Input carries its model to MeshCadSurface, IgaSample and IgaRefine steps (v17.17.0)
sequencerun a multi-file / transient sequence (input_pattern or input_paths, output_path; optional mode/times/time_from). A {step}/{index} token in output_path writes one file per step (fan-out); a plain path writes one multi-step file (fan-in, only for xdmf, gid, usd, vtkhdf, pvd, femap and frd — anything else fails by name rather than keeping step 0). Ordering is natural-numeric, so out_9 precedes out_10. A pattern's directory component is containment-checked against the sandbox root before it is expanded, and every matched file is re-checked individually
resample_sequenceresample a sequence onto new times (input_pattern or input_paths, output_path, and times — a list or "START:STOP:STEP" — or times_from_pattern; optional method linear/nearest/previous, clamp, blend_points, time_from); reports the target times. Same sandboxing as sequence; at most two source meshes live — see Resampling onto new times
blend_stepsblend two steps of one topology (path_a, path_b, output_path, weight, optional blend_points): every float data array becomes (1 − weight)·a + weight·b, integer data from the nearer step

Mesh operations ​

region_adjacency writes the conforming facets shared by selected Cell regions, or treats multiple cell blocks as groups when fewer than two Cell regions exist; the facet mesh includes parent cell/facet ids and per-facet measure (see region adjacency).

iga_refine, iga_sample and iga_evaluate act on the NURBS model a reader keeps beside its mesh (an MFEM NURBS mesh, Kratos CAD JSON, STEP or IGES): iga_refine(input_path, output_path, subdivisions, elevate, sample_subdivisions, …) refines every patch exactly and writes the refined model (an MFEM NURBS mesh or CAD JSON keeps its patches, other formats get the sampled cells), iga_sample samples it into Lagrange cells, and iga_evaluate(input_path, params, patch, derivatives) returns points, derivatives, normals, NURBS fields and a trimmed face's inside mask (at most 10000 points per call); see isogeometric layer. A summary of the model rides in every report (nurbs: patches, dimension, degrees, control points, field names; brep: faces, loops, edges).

extract_surface, extract_skin, reorder (reports bandwidth before/after), clean, crop (bbox, half-space, or a where_array/where_compare/where_value cell_data predicate), slice, isosurface, voxelize (a regular grid around a mesh, fill all/surface/interior narrowing it to the shell or the interior — see mesh and regular grids), octree (an octree or quadtree around a mesh skin, fine only where the skin is — balance none/face/full, fill as for voxelize, transition hanging/polyhedra/templates, snap, surface_level, curvature_threshold, feature_angle, size_array, plane; reports num_leaves, num_hanging, num_snapped and num_refused — see octree), octree_mesh (a volume mesh of a skin's inside on a 2:1-balanced octree, inside decided by the fast winding number so a slightly dirty skin still meshes — output castellated (the inside leaves as hexahedra with the inside/boundary_cells cell regions, the surrogate_boundary side region and sdf:distance/sdf:closest_point point data, for embedded and Shifted Boundary Method solvers), tet (conforming tetrahedra, the default) or hex; the size field takes min_size/max_size, curvature_angle, feature_angle/feature_size, gap_cells and size_array, skin_grading graded/uniform, and fill all/inside/cover for the castellated leaves; reports the frame, leaf and cell counts, the quality with its histograms, feature recovery, timings and warnings — see octree mesh), mesh_cad_surface (a conforming triangle mesh of a CAD model's faces — Kratos CAD JSON, or STEP and IGES where OpenCASCADE is linked — at target_size, with no CAD kernel: every B-rep edge is sampled once and shared by its faces, so a closed solid comes out watertight; max_chordal_deviation caps the boundary's sagitta, min_points_per_edge, tolerance, record_face_ids (cad:face and a face:<k> region per face), edges (a line block with cad:edge), match_unreferenced_trims and strict; min_angle refines every face by Delaunay refinement to a smallest angle on the surface and smooth_iterations smooths (smooth_method laplacian/odt); order 2 or 3 writes curved triangle6/triangle10 elements whose nodes lie on the surface and edges, an element whose Jacobian is not positive everywhere written straight-sided and counted in num_curved_straightened; curvature_deviation and cells_across_gaps size the mesh by the model's curvature and gaps through one size field whose growth grading bounds, and pole_rings grades the poles; face_sizes and edge_sizes (JSON objects {"index": size}), background_path with background_array, metric (mean/varying) and virtual_edges give sizes, the surface's own metric and virtual edges, the faces reporting limited_by_edges and the report virtual_edges; reparam_condition_number, virtual_faces, model_tolerances and lop_corners (v17.20.0) mesh a face whose parameters stretch past the threshold in a flattening of its coarse mesh, a sliver face with its tangent neighbour, accept the gaps the model declares, and lop acute corners (Pav and Walkington 2005) in place of concentric shells, reported as reparametrised, merged_faces, tolerance_uses and lopped_corners (with the angle bound the paper proves on a planar face and whether the run meets it); metric_background_path (a mesh file whose 9-component point_data metric_array is a field's Hessian) with metric_error sizes the triangles by an anisotropic metric (roadmap 6.10.2); a face that cannot be meshed is reported with its status and reason, never meshed wrong; reports the counts, the open, non-manifold and misoriented edges, the extreme angles and edge lengths, max_edge_gap, one record per face (with its below_bound, bound_deg and the corners under 60 degrees and poles), num_refinement_points, num_faces_below_bound, num_smoothed, the angle_histogram and the warnings — see mesh generation from CAD), compute_sdf (a grid over a surface, filled — structure voxel/octree), distance_to_surface (the signed distance from a mesh's points or cell centres to a second surface_path mesh, attached as point_data/cell_data; band narrows it to a shell and reports num_banded — see signed distance), sample_distance (the same signed distance for a bare list of [x, y, z] points against a surface, with no output mesh — returns distances directly), transform, convert_cells, tessellate (isoparametric subdivision of the five higher-order cell types onto a refinement lattice, with a provenance map Tessellation.gather/.scatter/.aggregate use to move data between the tessellated mesh and its source; reports num_source_points/num_source_cells, num_curved_source_cells/num_pass_through_source_cells and watertight (num_boundary_facets/num_interior_facets/num_facets_with_bad_count, the last zero for a conforming tessellation) beside the output's own num_points/num_cells — see curved tessellation), subdivide (polyhedral refinement: one polyhedral child per 3D cell face, connected to a new interior point — no per-type template table, unlike refine; see subdivide), agglomerate (polyhedral coarsening: merge groups of cells into single larger polyhedral cells via greedy seed-and-grow over the shared-face dual; merge_coplanar_faces/coplanar_angle fuse coplanar shared faces into one polygon and min_sphericity gates growth on shape, reporting num_faces_merged/num_rejected — see agglomerate), refine (uniform, or a subset via cells/region/where with a conforming closure; record_hierarchy attaches the persistent refine:cell_id/refine:parent_id parent/child hierarchy a multigrid caller resolves across the sequence of meshes it keeps — see refine), undo_green (restores a refine transitional/green cell to its coarse parent, read verbatim from coarse_path — a two-mesh tool like interpolate; reports num_groups_undone/num_cells_removed — see green-element undo), decimate, decimate_volume (the volume-mesh sibling of decimate: quadric-error tet-edge collapse; preserve_boundary defaults False, the opposite of decimate's own default, since boundary vertices participate by real quadric error here — see volume decimation), remesh (replaces a surface's triangulation with a new, well-shaped one at a chosen vertex count by ACVD clustering — the output has NO correspondence to the input, point_data/cell_data/named regions are dropped; see remesh), remesh_volume (the volumetric sibling of remesh: retetrahedralizes a volume mesh or closed surface at a chosen resolution by isosurface stuffing over a BCC lattice — same no-correspondence output contract, plus warp_fraction trading boundary tet quality for a small, measured chance of non-manifold boundary edges reported as num_non_manifold_edges; see remesh_volume), optimize_volume (ODT-remeshes a tetrahedral mesh: raises its worst element quality by relocating vertices AND flipping connectivity (2-3/3-2, predicate-free) — the point set is invariant so point data and Point regions carry, cell data and Cell/Side regions are dropped; tet-only and C++-core-only; see optimize_volume), smooth (method taubin/laplacian/odt — odt, optimal-Delaunay-triangulation smoothing, is tet-only and C++-core-only with no pure-Python fallback), merge (N inputs), split (one file per piece, name_template), partition (one file per part, or one .pvtu/.pvtp parallel index over every part when name_template ends in that extension and has no {part}, which keeps ghost_layers halos as vtkGhostType and returns the index as index), interpolate (source → target field transfer), conservative_interpolate (mass-preserving overlap-measure weighted transfer — unlike interpolate, an unset arrays covers every source point_data AND cell_data array; see conservative interpolation), gradient (the gradient, divergence or curl of a point_data field — see field derivatives; reports num_skipped and num_fallback), hessian (the Hessian, second derivative, of a scalar point_data field — a composition of two gradient calls, gradient's companion one order further; see second derivatives; reports num_skipped and num_fallback), estimate_error (the Zienkiewicz-Zhu recovery-based error indicator of a point_data field, plus optional absolute/fraction/dorfler marking into error:marked for refine's own where selector — see error estimation; reports global_error, num_skipped and num_marked). export_gltf (the surface of a mesh as glTF 2.0, .glb or .gltf plus a .bin, for three.js, Blender and dashboards: normals split at creases (split_angle), point fields as raw _NAME attributes, color_by baked into an unlit COLOR_0, one node per cell region, Y-up with the axis change, scale and recentring on the root node — convert writes the same format with the defaults; see glTF). curvature (per-vertex mean and Gaussian curvature by the angle defect and the cotangent Laplace-Beltrami operator, over no input array at all — top-level like quality/stats, not a data verb — dual_area mixed-voronoi (default, C++-core-only) or barycentric (twinnable in numpy); reports total_angle_defect, the Gauss-Bonnet invariant (2*pi*chi, 4*pi for a closed genus-0 surface), plus num_boundary/num_isolated/num_degenerate and the input's surface quality — see curvature). normals (point and cell normals of a surface, written as normals; split_angle set to a number of degrees in [0, 180] duplicates points at creases so every point carries exactly one normal, appending the copies after the original points while cells keep their numbering — never reorients, so it reports the input's surface quality (inconsistent_pairs nonzero means some normals average faces that disagree about which side is out) plus num_added_points/num_split_points/num_isolated/num_undefined/num_degenerate — see normals). feature_edges (the sharp, open, non-manifold and inconsistently wound edges of a surface — or of a volume mesh's skin — written as line cells over the input's points with feature:kind/feature:angle cell data; reports num_edges and the per-category counts — see feature edges). edit_regions (a list of region edits — union/intersection/difference/rename/retag/delete, applied in order, points/cells/data untouched; reports the resulting region list — see editing regions). repair (a surface's orientation, holes and pinched vertices -- the three defects clean does not touch: triangles are rewound by the topological half-edge rule (exact across any crease, where a normal-angle test is not), boundary loops of at most max_hole_edges edges are fan-filled consistently with the surrounding surface, bowtie vertices are duplicated and closed components oriented outward; reports both the input's and the output's defect counts, so what was fixed and what remains are both visible -- see surface repair). shrinkwrap (projects every selected point of the input onto the surface of a second target_path mesh -- one projection, no iteration, optionally offset along the hit FEATURE's pseudonormal (the bisector at a crease) rather than the selected triangle's normal; every point of the source moves whatever cells it carries and only the target must be a surface, weights names a point_data array that selects or blends, and a point beyond max_distance is left alone and counted -- see shrinkwrap). sobolev_deform (smooths a raw per-point displacement field through the mesh's own P1 operators and moves the points by the result -- a screened-Poisson low-pass filter whose cutoff wavelength is length_scale, which turns a jagged displacement into one a mesh can follow without tangling; every top-dimensional block must be a linear simplex, nothing is pinned by default (so a constant displacement is preserved exactly) and non-convergence is reported through converged with the last iterate returned -- see Sobolev deformation). grid_sample (a mesh's point data onto a regular lattice, written as a .vti grid — the step a convolutional or superresolution model needs before it can see a mesh at all; reports the channel names and the coverage, the fraction of grid points inside the mesh, since a grid around a concave domain is mostly fill and a model would learn it), grid_scatter (the inverse, by trilinear interpolation — consecutive name_0/name_1/name_2 channels rebuild one multi-component array), grid_resample (onto a finer or coarser lattice over the same box — the baseline a superresolution model has to beat) and grid_power_spectrum (azimuthally averaged; whether a super-resolved field carries the right small-scale content, which a pointwise error cannot see — see mesh and regular grids). proximity_graph (a radius or k-nearest-neighbour graph over a mesh's points or cell centroids, written as line cells with a degree array — the neighbourhoods a particle state needs, since it has positions and no connectivity; box_size turns on the periodic minimum-image convention and reports the degree spread and the isolated count — see proximity graphs). subsample (a mesh reduced to a point cloud of exactly count points under a token budget — method farthest for exact farthest-point sampling, grid for the O(N + count²) lattice-then-farthest variant a million-point cloud needs, random for the baseline; bounds restricts the candidates to a box and record_ids attaches budget:original_point_id; the cells and cell_data are dropped, the point data and Point regions kept — see point-cloud budgets). render_mesh (look at a mesh with the software rasterizer, which needs no display, GPU or extra: without output_path it returns cols x rows terminal cells as text — text_format plain, ansi or html, encoding halfblock/quadrant/sextant/braille/ascii — and with one the extension picks .png (returned as MCP image content), .txt, .ansi, .html or .cast; the camera (view, azimuth/elevation, perspective, zoom), shading, edges, color_by with cmap/vmin/vmax/colorbar, supersample, background and axes are the Python options of the same names, as are the field options expr, reduce, clip, symmetric, scale, categorical, color_regions, category_edges, isolines, vectors, warp, warp_scale, warp_outline, diagnostic and quality_metric, the cut-aways cutaway (a list of at most two planes, each "+x:0.5" or six numbers) and cutaway_tint, streamlines (a vector point array to follow, with stream_seeds, stream_length and stream_color), and a text result ends with the colour range, ticks and keys; unlike screenshot it is not gated). Parameters mirror the Python API / CLI one-to-one; operations that produce reports (clean, decimate, decimate_volume, remesh, remesh_volume, optimize_volume, smooth, gradient, hessian, estimate_error, curvature, normals, feature_edges, segment (method angle or primitive, tolerance), fit_primitives (its report lists each patch's type, parameters and residuals), refine_patches (its report gives the explained area share before and after and why refinement stopped), octree_mesh, mesh_cad_surface, edit_regions, repair, shrinkwrap, sobolev_deform, tessellate) include them in the response.

rectangle, box, disk, uv_sphere, icosa_sphere, cylinder, reference_triangle, ngon, annulus, lshape, plate_with_hole, honeycomb and reference_cells build a mesh from parameters and read no input, like grid: they take output_path/output_format and the shape's own arguments (dims, size, origin, radius, height, n, nu, nv, subdivisions, nxy, nz, sides, rings, inner_radius, outer_radius, ntheta, nr, nt, angle, edge, center, cell_type, split, order, max_cells, types, pairs) and return the written path with a mesh summary whose regions list the named boundaries (xmin … zmax, rim, side/top/bottom, inner/outer, hole) — see parametric primitives.

Data operations ​

ToolNotes
data_managekeep/drop/rename arrays: keep/drop are [location, name] pairs, rename is [location, old, new] triples
data_convertaverage between locations (direction: point_to_cell | cell_to_point)
sets_dataregion-backed sets ↔ scalar integer data (direction: sets_to_data | data_to_sets, location: point | cell); key required for data→sets; sets→data accepts data_name, join_char and explicit order
data_calcexpression evaluator; accepts the CLI's "NAME = EXPR" spelling
data_conditionclamp / normalize / standardize
tensor_invariantsvon Mises / principal / hydrostatic / deviatoric of a symmetric (6-component) or general 3x3 (9-component) tensor array; outputs selects any of mises/principal/hydrostatic/deviatoric (default: all four)

Dataset manifests ​

ToolNotes
dataset_addadd a case to a dataset manifest (created if absent): input_pattern or input_paths (the sequence tool's shape, same sandboxed glob handling), optional entry_id/split/tags/group/notes/metadata; the source is validated now and stored relative to the manifest's directory, and a picture of the case: thumbnail or render_thumbnail (first step, thumbnails/<id>.png)
dataset_lista manifest's entries, filtered by split/tags (must carry all)/group (path or descendant); resolve: true also expands each entry's file/step/time plan — every resolved path is containment-checked, since a hand-edited manifest is client input too
dataset_updatecurate: split on entry_ids/all_entries, assign_splits by fractions (deterministic via seed; by_group keeps groups together), add_tags/remove_tags, or one entry's group/notes/metadata, or a picture of the case: thumbnail (an existing PNG under the manifest's directory; "none" clears it) or render_thumbnail (draws each selected entry's step thumbnail_step to thumbnails/<id>.png beside the manifest)
dataset_findevery *.json at most max_depth levels below root_dir that parses as a manifest, with its name, entry count, splits, modification time and SHA-256 content hash (the identity the dashboard binds a browser-side card to)
dataset_healthscan a manifest's entries (optionally one split / given entry_ids, step 0 or all_steps), one mesh alive at a time: per entry the step count, NaN/Inf counts over the data arrays (quality:* arrays excluded — their NaN means "N/A for this cell type"), inverted/degenerate cells and the worst scaled Jacobian from compute_quality, and the arrays present; per manifest the split balance, totals, fields missing across entries and the bad entries — the server-side producer of the dashboard's health summary

Training ​

Jobs on the machine the server runs on (see the dashboard); every run lives in its own directory under --runs-dir (default <root>/runs).

ToolNotes
train_defaultswhat a launch needs: the data arrays the manifest's first entry carries, its splits, whether the frameworks are installed, and a complete default training spec
train_startgated — start a training run (fields → target_fields over a split) as a subprocess; returns the job id. model_name picks the family and each reads only its own hyperparameters: meshgraphnet (processor_size/hidden_dim/aggregation + the graph options), srresnet (scaling_factor/conv_layer_size/resid_blocks + the grid options), fno (latent_channels/num_fno_layers/num_fno_modes/spectral_padding + the grid options, 2-D when squeeze names a world axis), afno (patch_size/embed_dim/depth/num_blocks + the grid options, squeeze required) and deeponet (no fields — the per-entry Metadata keys in parameters, plus trunk/trunk_count/trunk_method/trunk_seed and the branch/trunk sizes). An unknown model_name is refused naming the five before any spec is built. Needs nvidia-physicsnemo, plus torch_geometric for meshgraphnet only, checked before spawning so a missing framework is a named error rather than a dead process
train_status / train_metrics / train_logthe three incremental polls: state + progress + ETA, the per-epoch rows since an epoch, and the log from a byte offset
train_listevery run (newest first) with its hyperparameters and final/best losses
train_stopSIGTERM (the trainer finishes its epoch and writes final.mdlus), SIGKILL after grace_seconds
train_checkpoints / train_mark_bestthe run's .mdlus files with epoch/validation loss/size, and which one is best
train_predictgated — predict over a split with a job's (or an explicit) checkpoint, writing <column>_pred/<column>_error back into output_dir/<entry_id>.vtu; returns per-entry RMSE
guard_fitfit a geometry guardrail over a manifest split and write it as JSON — the description a trained model is scored against
guard_checkdescribe one mesh's shape, and score it against a guardrail (or a model card carrying one) if given: verdict, score against the threshold, and the descriptors that put it there
predict_filegated — predict on ONE mesh file with a trained checkpoint: no manifest, no split, no entry. Everything comes from the checkpoint's own card, including which model family wrote it; time_step picks a step of a multi-step input, target_path supplies a paired mesh, and parameters supplies a deeponet checkpoint's per-case inputs (an object of the Metadata keys it was trained on — required for that family, refused for the others). A file carrying no truth predicts anyway, with rmse/max_error null

fit_cad is not gated since v17.9.0: a closed skin → a STEP solid with analytic faces at output_path (fit CAD); a skin without segment:patch / fit:patch_* is segmented (segment_method, primitive by default, at segment_tolerance; curvature_tolerance and min_patch_cells for angle) and fitted (fit_tolerance, by default the segmentation's own tolerance) first, backend (auto, occt, native) picks the engine — native needs no CAD kernel and writes the fitted primitives as analytic STEP faces, occt needs a core built -DMESHIOPLUSPLUS_WITH_OCCT=ON and names the flag without it — merge_faces (on) merges faces on one surface, mesh_output_path also writes the re-meshed solid, cad_json_output_path also writes the solid as Kratos CAD JSON, freeform (on by default), freeform_degree and freeform_max_control steer the B-spline faces for patches no primitive explains, freeform_split (on) splits a freeform patch that folds over every flattening into pieces, fillets (on) tries a patch between two fitted faces as a rolling-ball fillet or blend, then a chamfer or conic blend, and a corner among three or more blends as a vertex blend, and threads (on) a patch as a helical thread face, of constant or varying pitch and on a taper, and the report carries valid, volume_ratio, max_deviation, the faces by type (num_bspline, num_fillet and num_helical among them, and of those num_variable_fillet, num_blend and num_rounded_thread), num_vertex_blend and num_chamfer (v17.2.0) and, of the fillets and threads, num_conic_blend, num_tapered_thread and num_variable_pitch_thread, num_split_patches, num_demoted and a brep summary. Every answer carries backend and occt_available. refine_segmentation (off by default, so a default answer is unchanged) first refines the patches as the refine_patches tool does, with max_refine_iterations (10), refine_epsilon (1e-3), normal_weight (1), allow_merge and allow_split (on), ransac_seed and refine_tolerance (1e-3 of the diagonal unless relative_tolerance is false); the report then adds refined, refinement_kept (whether the solid was built from the refined patches; OpenCASCADE also builds it from the patches as given and keeps the better), refine_iterations, num_refine_splits, num_refine_merges, initial_explained_area_fraction and explained_area_fraction. freeform_general (off) tries a patch the plane and cylinder freeform fit leaves unexplained as a general B-spline face, with freeform_tolerance (0: a quarter of the tolerance), freeform_smoothing (1e-12), max_freeform_poles (40), max_freeform_iterations (30) and max_freeform_splits (2), and adds num_freeform_general, num_freeform_refinements and avg_freeform_poles; OpenCASCADE only, sew_tolerance (0: the tolerance) sets the sewing tolerance, extend_surfaces extends freeform surfaces past their patches (adds num_extended_edges), snap_junctions places the corners where analytic faces meet by least squares (adds num_snapped_junctions) and face_deviation measures each face against the skin as fit_cad:face_hausdorff / fit_cad:face_rms field data (adds hausdorff_max and hausdorff_rms, null when not measured). The report always holds closed. freeform_mode (legacy by default, so a default answer is unchanged; adaptive, network, auto; v17.12.0) picks how the patches no primitive or feature fit explains are treated (freeform modes), with freeform_parameterization (auto; projection, lscm, harmonic), extraordinary_vertex (g1x; g0, g1, g1xx), local_refine (knots; thb, experimental), freeform_g1_tolerance_deg (1), freeform_max_control_points (400), layout_target_patch_size (0.1), layout_max_valence_deviation (2), resegment_rounds (2), layout_snap_to_features and validate_by_surface_sampling (on); outside legacy the report adds freeform_mode, brep_checked, facet_area_fraction, num_freeform_adaptive, num_network_patches, num_network_edges, num_extraordinary_vertices, num_resegmented, num_escalations, max_g1_angle_deg, max_freeform_distance, rms_freeform_distance and faces, one record per patch tried as a surface (patch, face, kind, param_method, num_ctrl, iterations, refinements, escalation_steps, max_dist, rms_dist, reverse_max_dist, max_g1_angle_deg and demotion_reason), the same table the re-meshed solid carries as fit_cad:face_table field data.

refine_patches refines a segmentation until one plane, sphere, cylinder, cone or torus explains each patch (refine patches): a surface without segment:patch is segmented first (segment_method, angle by default, feature_angle, segment_tolerance for primitive), then patch boundaries move, a primitive is split out of a freeform patch by seeded RANSAC (allow_split, ransac_seed) and neighbours one primitive explains merge (allow_merge), within tolerance (1e-3 of the diagonal unless relative is false), never across a feature edge above feature_angle; normal_weight, epsilon, max_iterations, min_patch_cells, types and prefix are the Python options of the same names. The output carries segment:patch, the patch regions and the fit:* data rewritten, so fit_cad reads it unchanged, and the report gives num_patches, num_iterations, num_splits, num_merges, num_absorbed, num_primitive, num_freeform, num_facets, tolerance, explained_area_fraction, initial_explained_area_fraction, hausdorff_max, hausdorff_rms, initial_energy, energy and stop (unchanged, small_gain, iteration_cap or energy_rose). It needs the compiled core.

Gated ​

ToolExtraNotes
data_export[arrow]data arrays → Parquet table
export_dataset[arrow] ([zarr]/h5py for those layouts)a set of meshes → one mesh_id-keyed dataset (hive Parquet / zarr / hdf5; see ML data handling)
export_cae—a set of meshes → one .npz per case in the CAE sample layout (pure numpy, no extra)
screenshot[viewer]off-screen PNG render, returned as MCP image content; without the extra the software rasterizer draws it (render_mesh takes its options)
train_start, train_predict, predict_filenvidia-physicsnemo, plus torch_geometric for the meshgraphnet family (no pip extra, deliberately)training and inference; the other train_* tools only read files and need neither. train_defaults reports which of torch_geometric/physicsnemo/deeponet (the experimental DeepONet) the server has

view, has_viewer and tui are deliberately not tools: they hand control to a person (a window, a browser tab, or a terminal taken over for orbiting and zooming), and an agent has nothing to drive. screenshot and render_mesh are their headless equivalents; the interactive terminal viewer is for the person at the keyboard.

Reports are strict JSON ​

Every response survives json.dumps(..., allow_nan=False): numpy scalars and arrays are converted, NaN/±Inf become null (with a non_finite_replaced count so the loss is visible), and any array longer than 1000 elements is replaced by a {"truncated": true, "size", "shape", "dtype", "preview"} wrapper — reports stay agent-sized no matter how large the mesh.

Architecture: the pure payload layer ​

src/python/meshioplusplus/mcp/ is split exactly the way _interop.py is, and for the same reason. Everything a tool does lives in the pure layer _tools.py — imports only meshioplusplus + numpy + stdlib, runs on every supported Python, tested in the default CI matrix with the mcp SDK absent. The registration layer _server.py contributes only typed signatures (the JSON schemas) and docstrings (the tool descriptions), and is the one module importing the SDK. It supports the mcp SDK 1.x (mcp.server.fastmcp.FastMCP) and 2.x (mcp.server.mcpserver.MCPServer, which replaced it), importing whichever exists.

_tools.TOOL_REGISTRY is the single source of truth: the server registers from it, and the parity guard in tests/python/test_mcp.py asserts every public operation in meshioplusplus.__all__ is claimed by some tool — a new operation fails CI until it gets a tool (or a conscious exemption). That test is what keeps this page's tool table honest as the library grows.

Released under the MIT License.