Back to Browse

AnkusDrive MCP Server

Developer ToolsUse Caution4.4MCP RegistryLocal
Free

Server data from the Official MCP Registry

Drive FreeCAD from an agent: CAD modeling, drawings, FEM, CFD, EM, and multiphysics simulation.

About

Drive FreeCAD from an agent: CAD modeling, drawings, FEM, CFD, EM, and multiphysics simulation.

Security Report

4.4
Use Caution4.4High Risk

AnkusDrive appears to be a legitimate FreeCAD CLI and MCP server whose visible code (README, pyproject, cli.py, and the first part of mcp_server.py) shows no exfiltration, backdoors, or hardcoded secrets. The main concern is that the MCP surface includes a run_script capability that executes arbitrary Python in a FreeCAD worker, which is inherent to its design but should be understood by users. The mcp_server.py file and the tool implementations were truncated, so the audit is incomplete. Supply chain analysis found 10 known vulnerabilities in dependencies (0 critical, 5 high severity). Package verification found 1 issue.

6 files analyzed · 15 issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

File System Read

Reads files on your machine. Normal for tools that analyze or process local data.

File System Write

Writes or modifies files on your machine. Check that this is expected for the tool.

process_spawn

Check that this permission is expected for this type of plugin.

HTTP Network Access

Connects to external APIs or services over the internet.

env_vars

Check that this permission is expected for this type of plugin.

What You'll Need

Set these up before or after installing:

Absolute path to FreeCAD 1.1's freecadcmd. Leave unset to auto-discover on PATH and standard install locations.Optional

Environment variable: ANKUSDRIVE_FREECADCMD

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-gchen19-ankusdrive": {
      "env": {
        "ANKUSDRIVE_FREECADCMD": "your-ankusdrive-freecadcmd-here"
      },
      "args": [
        "ankusdrive"
      ],
      "command": "uvx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

AnkusDrive

A CLI + MCP server that drives FreeCAD through its Python API so LLMs (and humans at a terminal) can design mechanical parts and run FEM simulations without clicking through the GUI.

Why

FreeCAD exposes almost everything it does through a Python API — create documents, build sketches, extrude solids, mesh them, run CalculiX/Elmer FEM solves, read back stress/displacement fields. But that API lives inside FreeCAD's embedded Python (freecadcmd), which is awkward to call from anywhere else. AnkusDrive wraps it behind two surfaces:

  • CLI — one-shot commands (ankusdrive run script.py, ankusdrive box --w 10 --d 20 --h 5 -o part.FCStd) for scripts, CI, and quick iteration.
  • MCP server — 280+ structured tools (new_document, add_primitive, boolean_op, pad, add_gear, fem_new_analysis, fem_run, fem_results) so an LLM agent can model, inspect, and simulate iteratively. Beyond core CAD/FEM this now spans a broad simulation surface (thermal, CFD/CHT, EM, acoustics, FSI, injection molding, granular/DEM, optics, multibody) and a design-control layer (item/part numbers, recipes, variant families, lifecycle/revision, ECO change orders, versioned interfaces).
  • Multi-agent orchestration — a host-side reference layer that lets a team of agents partition one product into components, build them in parallel, and merge the pieces back together with the joints actually fitting (see Multi-agent design).

Target environment

  • FreeCAD 1.1.x. The freecadcmd binary is auto-discovered per-OS (macOS .app bundle, Linux /usr/bin etc., Windows C:\Program Files\FreeCAD 1.1\bin\freecadcmd.exe — version-globbed); override via $ANKUSDRIVE_FREECADCMD or rely on PATH. Run ankusdrive doctor to see exactly what resolved.
  • Bundled Python, ccx (CalculiX), and gmsh already ship inside every FreeCAD install — the macOS .app, the Linux package, and the Windows bin\ — so core CAD + structural FEM work on all three with no extra install.
  • Host-side rendering needs Pillow and numpy; both are installed by AnkusDrive as regular pip deps.
  • One optional exception: drawing PDF/SVG export (export_drawing) renders inside FreeCAD's bundled Python, so it needs reportlab + svglib installed there — see Drawing export (PDF/SVG). DXF export and everything else leave FreeCAD's Python untouched.

Setup

AnkusDrive is a pip-installable package; FreeCAD itself is the only thing you install separately. The host-side dependencies (mcp, Pillow, numpy) come along with the install. freecadcmd is launched as a subprocess and uses its own bundled Python — AnkusDrive doesn't touch it.

# 1. Install FreeCAD 1.1.x from https://www.freecad.org/
#    (macOS: drag to /Applications; Linux: distro package or AppImage;
#     Windows: run the installer — default C:\Program Files\FreeCAD 1.1)

# 2. Install AnkusDrive. Pick one:
pipx install ankusdrive                                       # from PyPI — isolated app, `ankusdrive` on PATH
pip install ankusdrive                                        # or into an env you manage yourself
# unreleased main, or for development from a clone:
pipx install git+https://github.com/gchen19/AnkusDrive.git
git clone https://github.com/gchen19/AnkusDrive.git && cd AnkusDrive
python3 -m venv .venv && .venv/bin/pip install -e .         # `.venv/bin/ankusdrive`

# 3. Smoke-test that the worker can reach FreeCAD, and see the full setup report
ankusdrive ping        # → ping=pong freecad=1.1.1
ankusdrive doctor      # per-item FreeCAD + solver checklist with the exact fix each

On Windows, don't follow the block above by hand — there is one scripted path that does all of it including the MCP registration: Windows quickstart (PowerShell).

AnkusDrive is published on PyPI at pypi.org/project/ankusdrive; the distribution roadmap beyond it (marketplace listings, hosted transport) is tracked in epic #303; the original phase plan is kept as a design record at docs/archive/PUBLISHING_PLAN.md.

macOS quickstart — solvers in a container

FreeCAD, CalculiX, SU2, PrusaSlicer and every pip-wheel family run natively on a Mac; the block above is all you need for those. What has no practical macOS build is the Linux solver stack — OpenFOAM, Elmer, YADE, openEMS, Bempp, preCICE, openInjMoldSim. Those run in a container, and AnkusDrive stays on the host and reaches into it. The image is multi-arch, so on Apple Silicon it runs native, not emulated.

# 1. A container engine: Docker Desktop, OrbStack, colima or podman.

# 2. Pull the solver image (0.88 GB on Apple Silicon, 1.15 GB on Intel).
docker pull ghcr.io/gchen19/ankusdrive-solvers:latest

# 3. Check it is ours before running your geometry through it (see below).
bash scripts/verify-container-image.sh

# 4. Point AnkusDrive at the container substrate.
export ANKUSDRIVE_SUBSTRATE=container
#    optional: ANKUSDRIVE_CONTAINER_ENGINE=podman|nerdctl   (default docker)
#    optional: ANKUSDRIVE_CONTAINER=<name>                  (default ankusdrive-solvers)

# 5. Create the container. $TMPDIR must be mounted at the SAME path inside, because a
#    case directory has to mean the same thing on both sides. On macOS $TMPDIR is a
#    per-user /var/folders/... path — mount THAT, not /tmp.
docker run -d --name ankusdrive-solvers \
  --user "$(id -u):$(id -g)" -e HOME=/tmp \
  --network none --cap-drop ALL --security-opt no-new-privileges \
  --read-only --tmpfs /tmp:rw,exec,size=2g \
  -v "$TMPDIR:$TMPDIR" \
  ghcr.io/gchen19/ankusdrive-solvers sleep infinity

# 6. Export the in-container paths for the OpenFOAM-backed families. The image
#    publishes them; YADE, Elmer, openEMS and Bempp need no export — AnkusDrive finds
#    them by asking the container.
docker exec ankusdrive-solvers env | grep -E \
  '^ANKUSDRIVE_(OPENFOAM_PATH|OPENFOAM_BASHRC|FSI_OPENFOAM_BASHRC|CCX_PRECICE|PRECICE_LIB|OPENFOAM_ADAPTER_LIB|OPENINJMOLDSIM|OPENINJMOLDSIM_BASHRC)='

# 7. Confirm.
ankusdrive doctor                  # each family: ready via <solver> (in container)
ankusdrive doctor --verify-image   # …and that the image is signed by this repo

The run flags are least privilege, and each is there because the solvers genuinely do not need what it removes — verified by running the live solver suites with them on. --network none in particular: nothing in a mesh is a reason to reach the internet.

Don't copy the image's ANKUSDRIVE_FREECADCMD or ANKUSDRIVE_CALCULIX_PATH — those name paths inside the container, while FreeCAD and ccx run on your Mac.

A config.toml written for a native install is the one trap here: its absolute paths are read as in-container paths. ankusdrive doctor now catches that and says so.

The alternative substrate on macOS is a Multipass VM, which you provision yourself — docs/MACOS.md has the full per-solver reality on a Mac, and docs/CONTAINER_SUBSTRATE.md the container path in depth.

The solver images, and checking they are ours

imagewhat it issize
ghcr.io/gchen19/ankusdrive-solverswhat you want: solvers and their runtime libraries, nothing else1.15 GB amd64 / 0.88 GB arm64
ghcr.io/gchen19/ankusdrive-heavythe CI image — also carries FreeCAD, the driver venv and every build toolchain, because the whole test suite runs inside it5.3 GB / 4.5 GB

Both are public, multi-arch (linux/amd64 + linux/arm64, each built natively) and tagged latest plus sha-<commit>.

Every published manifest is signed through Sigstore with a short-lived GitHub OIDC identity — no key to store or leak — and carries provenance naming the repository, workflow and commit that built it, plus a CycloneDX SBOM of what is inside:

bash scripts/verify-container-image.sh                       # the slim image, :latest
bash scripts/verify-container-image.sh ghcr.io/gchen19/ankusdrive-solvers@sha256:<digest>

Needs the GitHub CLI (gh ≥ 2.49, authenticated). Verify a digest and then run that digest: verifying :latest today and pulling :latest next week are two different images. ankusdrive doctor prints the digest the running container was made from, and --verify-image checks it.

There are three answers, and only one is alarming: verified; unsigned (an image you built yourself, or one published before signing existed — silence it with ANKUSDRIVE_ALLOW_UNVERIFIED_IMAGE=1); and mismatch, an image carrying provenance from somewhere else, which no setting silences. Verification never blocks a solve.

Building your own — a subset, or with your own changes — takes minutes, because nothing is compiled (the prebuilt solver trees are copied):

tools/build_solver_image.sh --solvers "openfoam fsi" -t my-solvers:dev   # 0.69 GB

Windows quickstart (PowerShell)

Windows is a first-class target (core CAD + CalculiX FEM run natively against a stock FreeCAD 1.1 install), and the whole core install is one script — venv, pinned dependencies, doctor, and the MCP registration line with resolved absolute paths:

# 1. Install FreeCAD 1.1.x from https://www.freecad.org/ (default C:\Program Files\FreeCAD 1.1).
#    Nothing needs to go on PATH — AnkusDrive globs the versioned install dir itself.

# 2. Clone and run the core installer. Windows PowerShell 5.1 is enough; no admin needed.
git clone https://github.com/gchen19/AnkusDrive.git
cd AnkusDrive
powershell -ExecutionPolicy Bypass -File scripts\install-core.ps1

That creates .venv, installs AnkusDrive with the pins that matter (notably mcp<2 — mcp 2.x installs cleanly and then breaks ankusdrive mcp), verifies the resolved mcp/numpy/Pillow, runs ankusdrive doctor + ankusdrive ping, completes a real MCP stdio handshake, and finally prints your registration block. Useful switches: -Python 'C:\Program Files\Python313\python.exe' to pick an interpreter, -Extras mbd,fluids for the pip-wheel solver families, -Persist to write the FreeCAD path into %APPDATA%\ankusdrive\config.toml (MCP hosts launch with a minimal environment, so a $env: set in your terminal will not reach them).

3. Register it with your MCP host. The script prints these with your real paths filled in — a GUI host doesn't inherit your shell PATH, so the absolute path matters:

# Claude Code
claude mcp add ankusdrive -- C:\Users\you\AnkusDrive\.venv\Scripts\ankusdrive.exe mcp

# Claude Desktop: %APPDATA%\Claude\claude_desktop_config.json
#   { "mcpServers": { "ankusdrive": {
#       "command": "C:\\Users\\you\\AnkusDrive\\.venv\\Scripts\\ankusdrive.exe",
#       "args": ["mcp"] } } }

Then restart the host; you should see the ankusdrive__* tools appear.

Supported Python: 3.10 – 3.14 (3.14 verified end-to-end on Windows 11 — pip install, MCP stdio handshake, and ankusdrive ping → freecad=1.1.1). The script checks your interpreter before pip runs, so a too-new CPython says so instead of failing inside the resolver.

Optional solvers (SU2, Elmer, PrusaSlicer, WSL-backed OpenFOAM) come afterwards via scripts\install-solvers.ps1. For the Linux-only solvers (OpenFOAM, FSI, injection molding, YADE, openEMS, Bempp), run ankusdrive container setup --install-engine once WSL is installed. It runs them from the prebuilt image with Docker inside WSL (how). Full per-solver reality, the test suite, and the WSL2 route: docs/WINDOWS.md.

Ubuntu 24.04+ / containers (apt has no FreeCAD)

FreeCAD was dropped from Ubuntu 24.04's universe repo, so apt install freecad finds no candidate there, and upstream's snap/flatpak both fail in a container or sandboxed agent environment (no snapd session, no FUSE). The path that works everywhere is the official AppImage, extracted:

scripts/install-freecad-appimage.sh          # or: scripts/install-solvers.sh freecad

It downloads the pinned release AppImage, checks its SHA-256, unpacks it with --appimage-extract (a userspace squashfs unpack — no FUSE, no root, no snapd, which is why it works in a container), symlinks freecadcmd, freecad, ccx and gmsh into /usr/local/bin, and then live-verifies the result with ankusdrive ping plus a real CalculiX solve (ankusdrive fem cantilever). Without a writable /opt it installs to ~/.local/opt/freecad instead; --prefix / --bindir override both, --appimage FILE reuses a download you already have.

The symlink step is optional: AnkusDrive also probes /opt/freecad/squashfs-root/usr/bin (and ~/.local/opt/freecad*/…) directly, so a hand-extracted AppImage in either prefix is auto-discovered. ccx and gmsh ride along inside the AppImage, so structural FEM works off this one download.

Telling AnkusDrive where FreeCAD lives

AnkusDrive auto-discovers freecadcmd in this order: $ANKUSDRIVE_FREECADCMD, then shutil.which(...) on PATH (trying freecadcmd, FreeCADCmd, and freecad.cmd), then a per-OS list of standard install locations:

OSAuto-discovered locations (newest version wins)
macOS/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd
Linux/usr/bin, /usr/local/bin, /snap/bin/freecad.cmd, extracted AppImage under /opt/freecad*/squashfs-root/usr/bin or ~/.local/opt/freecad*/…, ~/.local/bin
WindowsC:\Program Files\FreeCAD *\bin\freecadcmd.exe (version-globbed), C:\Program Files (x86)\…, %LOCALAPPDATA%\Programs\FreeCAD *\bin\…

So a stock installer on any of the three needs no configuration. For a non-default install, point AnkusDrive at the binary directly:

export ANKUSDRIVE_FREECADCMD=/path/to/freecadcmd            # macOS/Linux
$env:ANKUSDRIVE_FREECADCMD = "D:\Apps\FreeCAD\bin\freecadcmd.exe"   # Windows

ankusdrive doctor prints which of the three layers (env / PATH / auto) actually resolved FreeCAD, plus every candidate it checked — the fastest way to debug a "FreeCAD not found" on a new box.

Drawing export (PDF/SVG)

export_drawing builds 2-D mechanical drawings (multi-view PDF/SVG/DXF with dimensions) entirely headless. DXF uses FreeCAD's own writer and needs nothing extra. PDF and SVG are composed and rasterised with reportlab + svglib, and because that runs inside the worker — FreeCAD's bundled Python, not the host venv — the two packages must be installed into FreeCAD's Python:

# Resolve FreeCAD's bundled Python from freecadcmd itself (portable across the
# macOS .app, a Linux distro package, and an extracted AppImage). freecadcmd
# prints a startup banner after the script output, so match a marker line
# rather than taking the last line:
printf 'import sys; print("DPREFIX="+sys.prefix)\n' > /tmp/_fcprefix.py
FREECAD_PREFIX="$(freecadcmd /tmp/_fcprefix.py 2>/dev/null | sed -n 's/^DPREFIX=//p')"
FREECAD_PY="$FREECAD_PREFIX/bin/python"      # some builds: $FREECAD_PREFIX/bin/python3

# Pin svglib<1.6 — newer svglib pulls rlPyCairo -> pycairo, a native build we
# don't use (our drawings are line art, no gradients).
"$FREECAD_PY" -m pip install reportlab "svglib<1.6"

# Verify:
"$FREECAD_PY" -c "import reportlab, svglib; print('drawing export ready')"

Without this, export_drawing still produces .dxf; .pdf/.svg raise a clear ModuleNotFoundError. FreeCAD already bundles Pillow (reportlab needs it), so no separate install is required.

Wiring it into an MCP host

The MCP server speaks stdio. Point your host at the ankusdrive binary and let it run the mcp subcommand.

Claude Desktop — add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "ankusdrive": {
      "command": "ankusdrive",
      "args": ["mcp"]
    }
  }
}

If ankusdrive isn't on the host process's PATH, use an absolute path — e.g. /Users/<you>/.local/bin/ankusdrive (pipx default) or /absolute/path/to/AnkusDrive/.venv/bin/ankusdrive (clone+venv).

Claude Desktop, one click — download ankusdrive-<version>.mcpb from the latest release and open it. Claude Desktop sets up its Python environment with uv, so no pipx step is needed — FreeCAD 1.1 still is. The install dialog has one optional field, the FreeCAD command path, for a FreeCAD that auto-discovery cannot find.

Claude Code — register once:

claude mcp add ankusdrive -- ankusdrive mcp

Other hosts (Cursor, Continue, custom MCP clients) — same shape: stdio transport, command = ankusdrive, args = ["mcp"].

After restarting the host, you should see 280+ ankusdrive__* tools become available. If startup hangs or the host reports a closed connection, run ankusdrive ping directly — that exercises the same worker boot path with cleaner error messages.

Tool families (toolsets). Every tool definition takes up the client's context, and all 283 come to roughly 114k tokens. Tools are grouped into families you can switch on and off: core (always on), drawings, fem, components, sheet_metal, assembly, intent, manufacturing, hand_calcs, simulation, plm, rendering.

  • pip / pipx / uvx / clone: every family is on unless you set ANKUSDRIVE_TOOLSETS, e.g. ANKUSDRIVE_TOOLSETS=drawings,fem,simulation, or toolsets = "..." in config.toml.
  • Claude Desktop extension: core, drawings and fem are on by default (~31k tokens); turn others on in the extension's settings.

setup_status lists the families that are off and exactly how to enable each one.

run_script executes Python the agent writes, with full access to your files and processes. It's controlled by ANKUSDRIVE_ALLOW_RUN_SCRIPT (env, or allow_run_script in config.toml):

  • pip / pipx / uvx / clone: allowed unless set to false.
  • Claude Desktop extension: off by default; turn it on in the extension's settings.

When it's off, the tool isn't offered at all, and setup_status says how to enable it.

Simulation solvers & review-video demos

The base install (FreeCAD + pip install ankusdrive) covers geometry, the analytic oracles, and the MCP surface. The heavy simulation families each shell out to an external solver, discovered at runtime by ankusdrive/solvers.py ($ANKUSDRIVE_<SOLVER>_PATH → PATH → standard install dirs). A family whose solver is absent degrades to a clean {ok: false, reason, install} dict instead of crashing — check what currently resolves with ankusdrive doctor (cross-platform, no server boot needed), the solve_capabilities MCP tool, or the install script's list. The install script installs the pip-wheel solvers and provisions the native ones — scripts/install-solvers.sh on Linux/macOS (apt/conda + source builds), and scripts/install-solvers.ps1 on Windows (pip extras + portable SU2/Elmer/PrusaSlicer downloads; CalculiX auto-detected from FreeCAD's bundle).

Persistent config: every ANKUSDRIVE_* path can instead live in ~/.config/ankusdrive/config.toml (%APPDATA%\ankusdrive\config.toml on Windows; ANKUSDRIVE_CONFIG overrides): freecadcmd = "..." at top level, one lowercased key per solver var under [solvers] (su2_path, elmer_path, openfoam_bashrc, ...). Env vars still win when set; the file is the layer that survives an MCP host's minimal launch environment. ankusdrive doctor reports the file and which layer resolved each value.

Where a solve's files go: every built-in solve writes its deck — a .sif plus mesh, an OpenFOAM case tree, a .inp, a sliced .gcode — into its own directory under <system temp>/ankusdrive-cases, and reports that directory as case_dir. They are kept, because a result names them and a second tool is handed them (a warpage solve consumes the cooling case a fill solve wrote), and they are reaped oldest-first once the root passes 64 directories or 4 GB — never touching one written within the last hour, so a running solve cannot be pulled out from under itself. solve_capabilities reports the root and the live numbers under cases. Tune with ANKUSDRIVE_CASE_ROOT, ANKUSDRIVE_CASE_KEEP, ANKUSDRIVE_CASE_MAX_GB, ANKUSDRIVE_CASE_GRACE_S, or turn reaping off with ANKUSDRIVE_KEEP_SCRATCH=1. A case_dir you supply is never touched, wherever it lives.

Every solve result also carries deck — a manifest of what the solver was handed, taken the moment its first step launched, before it wrote any output into the same directory: {digest, count, bytes, files: {path: hash}}. A session_transcript compares it with s.deck(...) ahead of that solve's checks, so a replayed number that drifted arrives already explained — ~ case.sif printed right above the failing check means the problem changed; deck matches the recording means it did not, and the solver or the environment did. CalculiX FEM results carry their .inp the same way.

Platform note: the solver discovery layer is fully cross-platform (per-OS install dirs, Windows PATHEXT/.exe, env overrides), so ankusdrive doctor gives an honest report on macOS/Linux/Windows. The pip-wheel families (MBD, topology, optics, fluids) install identically everywhere. The native-binary families differ by OS — CalculiX ships inside every FreeCAD install; SU2 and PrusaSlicer have good Windows/macOS binaries; Elmer has a portable Windows zip but no macOS binaries; the OpenFOAM-backed families (CFD, FSI, injection molding) — plus YADE, openEMS and Bempp — rely on a Linux shell + linker glue. On macOS they run through the signed solver container, native on Apple Silicon and with no source builds: see macOS quickstart. On Windows they run through WSL. See docs/WINDOWS.md and docs/MACOS.md for the full per-solver reality and setup on each OS.

The review-video demos under scratch/ turn a solver result into a GIF a human can watch — the real exported geometry in motion with the matching oracle overlaid on the frame (written to artifacts/). Each needs its family's solver plus matplotlib, and the CFD one needs meshio (on top of the base numpy/Pillow):

pip install matplotlib meshio        # frame rendering + reading OpenFOAM's VTK output
Review-video demo (scratch/…)Solver it drivesInstall
dog_clutch_cad_sim.py — rigid-body contact via p.vhacdPyBullet (pip wheel)pip install 'ankusdrive[mbd]'
meshing_gears_video.py — MBD gear trainPyBullet (pip wheel)pip install 'ankusdrive[mbd]'
modal_shape_video.py — FEM modal shapesCalculiX ccx (FreeCAD FEM)apt install calculix-ccx (Linux); FreeCAD finds ccx on PATH
thermal_field_video.py — transient thermal fieldElmerapt install elmerfem-csc; ensure ElmerSolver on PATH (or set ANKUSDRIVE_ELMER_PATH)
cfd_field_video.py — CFD field (lid-driven cavity)OpenFOAM + meshioOpenFOAM via apt/conda, then source <install>/etc/bashrc (or set ANKUSDRIVE_OPENFOAM_BASHRC); pip install meshio

All of them also use FreeCAD for the geometry/meshing, so run each with the same interpreter that launches the worker — e.g. .venv/bin/python3 scratch/cfd_field_video.py.

Optics

Two optics engines sit behind the MCP surface, in two licensing/runtime lanes:

LaneToolsEngineInstall
Sequential — lens design + optimizationoptics_lens_design, optics_lens_optimize, optics_raytraceoptiland / rayoptics (MIT/BSD, in-process)pip install 'ankusdrive[optics]' — or scripts/install-solvers.sh optics
Non-sequential — tracing through STL solidsoptics_solid_traceKrakenOS (GPL-3.0, out-of-process only)pip install 'ankusdrive[optics_gpl]' — or scripts/install-solvers.sh optics_gpl

The sequential engines import in-process, so install the optics extra into the same interpreter that launches the worker (like the other wheels). The non-sequential engine is GPL-3.0 and is therefore never imported by AnkusDrive — it runs in a separate subprocess (ankusdrive/optics_gpl_runner.py), the same arm's-length boundary used for the GPL Elmer/OpenFOAM binaries. The worker locates a Python that can import KrakenOS automatically (from where the wheel is installed); override with ANKUSDRIVE_OPTICS_GPL_PYTHON=/path/to/python. Because of that isolation the GPL extra is opt-in: the no-argument install-solvers.sh run installs only the permissive extras and prints how to add optics_gpl. Rendered examples for both lanes (lens layout, spot diagram, optimization, prism TIR, and a ball-lens spherical-aberration study) live in examples/optics/optics_gallery/ — regenerate with .venv/bin/python examples/optics/optics_gallery.py (and …_3d.py, optics_ball_lens.py), or bootstrap everything in one shot (installs both lanes, then renders every figure):

scripts/install-solvers.sh --optics-gallery

Architecture sketch

 ┌────────────┐      ┌────────────┐      ┌──────────────────────┐
 │  MCP host  │ ───► │ AnkusDrive   │ ───► │  freecadcmd worker   │
 │  (Claude)  │      │ (Python)   │ IPC  │  (long-lived Python) │
 └────────────┘      └────────────┘      └──────────────────────┘
       ▲                    ▲                        │
       │                    │                        ▼
       └── CLI user ────────┘               .FCStd / .inp / .vtk

Key decision: long-lived worker with JSON-over-stdin/stdout, not subprocess-per-call. FreeCAD startup is ~1–2s; re-paying that per tool call is unacceptable for an interactive agent. The worker is a small Python loop launched under freecadcmd, reading commands, dispatching to handlers, returning structured results (including object IDs so follow-up calls can reference created geometry).

FreeCAD API surface we care about

Notes gathered from the scripting docs and the FEM Python tutorial:

Core (App):

  • App.newDocument(name) / App.ActiveDocument / doc.recompute() / doc.save(path)
  • doc.addObject("Part::Box", "name") — typed object creation; properties set after (box.Height = 5)
  • doc.supportedTypes() for introspection; obj.TypeId, obj.isDerivedFrom("Part::Feature")

Modeling:

  • Part — makeBox, makeCylinder, makeSphere, boolean cut/common/fuse, fillets, lofts (OpenCASCADE under the hood)
  • Draft — 2D primitives, move, arrays
  • Sketcher + PartDesign — parametric sketch-driven solids (most "real" mechanical design happens here)
  • FreeCAD.Vector, Placement for positioning

FEM (ObjectsFem + femtools):

  • ObjectsFem.makeAnalysis(doc, "Analysis") — container
  • makeSolverCalculixCcxTools / makeSolverElmer — solver objects with tunables (GeometricalNonlinearity, ThermoMechSteadyState, …)
  • makeMaterialSolid — assign YoungsModulus, PoissonRatio, Density
  • Constraints: makeConstraintFixed, makeConstraintForce, makeConstraintPressure, makeConstraintDisplacement, contact/tie/spring, thermal
  • Mesh: makeMeshGmsh + femmesh.gmshtools.GmshTools(...).create_mesh() (or Netgen)
  • Run: femtools.ccxtools.FemToolsCcx().run()
  • Results: iterate analysis.Group for Fem::FemResultObject; read .DisplacementVectors, stress fields

Headless invocation:

  • freecadcmd script.py — runs script then exits
  • freecadcmd with no args — interactive Python REPL (what the worker will drive)
  • --console, -M <moddir>, -P <pypath>, --pass <args>, FreeCAD.ConfigGet(...) for env info
  • FreeCADGui is not available headless — keep design logic in App/Part/Fem only

How an agent reaches FreeCAD: three layers

AnkusDrive exposes FreeCAD through three layers, each with a different audience and a different cost-of-use. Knowing which layer a feature lives in tells you how to invoke it.

Layer 1 — typed MCP tools (the agent surface)

280+ first-class MCP tools span the core mechanical-design surface, a broad engineering-analysis / simulation surface, and a design-control (PLM) layer. They have validated parameters, structured returns, and stable handles for chaining. This is the happy path — what an agent uses for things people do every day.

DomainWhat's covered
Document lifecyclenew_document, open_document, save_document, list_documents, set_active_document, close_document, restart_worker
Geometry primitivesadd_primitive (box/cyl/sphere), boolean_op, export_shape (STEP/IGES/BREP/STL)
Selection (stable refs)list_faces, list_edges, query_faces, resolve_face, resolve_edge, register_handle, verify_feature
PartDesignmake_body, make_datum_plane, make_sketch, add_sketch_geometry, add_sketch_constraint, add_sketch_external, close_sketch, pad, pocket, revolve, hole, loft, sweep, helix, partdesign_fillet, partdesign_chamfer, linear_pattern, polar_pattern, mirrored, thickness, draft
Direct modeling & feature opsfillet_edges, chamfer_edges, shell_solid, add_rib, engrave_text, oring_groove, transform, scale_shape, copy_shape
Parametric componentsadd_gear, add_rack, add_sprocket, add_pulley, add_spring, add_fastener, add_bearing, add_thread, list_thread_options
Metrology & inspectionmeasure_distance, measure_angle, bounding_box, check_shape, section_view, min_clearance, envelope_check, interference_check
Generic property accessget_object, set_property
Functional intent & invariantsannotate_face, list_face_roles, classify_face_sides, check_airtight_path, declare_intent, verify_intent
Performance contractsdeclare_performance, verify_performance — a quantitative spec ("Cd ≤ 0.30 at 30 m/s", "Δp ≤ 50 Pa", "first mode ≥ 200 Hz") persisted on the part and re-proved after every edit, with a three-state verdict: a measurement whose uncertainty band straddles the limit is indeterminate (escalate), never a pass. The contract is consulted at the gates (#261): merge_assembly, substitutability_check and component_contract_check read the last recorded verdict, so an unmet spec blocks a merge and an unverified one is reported as its own outcome rather than passing silently
Design-space studies (DOE)study_submit — sweep recipe/tool parameters over a full grid or a Latin hypercube and keep the WHOLE search as a table, not just the last point. A response is any AnkusDrive tool + a metric path (including a whole verify_performance verdict, so points stay comparable across fidelity tiers); screening responses evaluate inline, solver responses fan out concurrently behind one collector job. Sampling is deterministic from seed, so re-submitting a crashed or widened study re-runs only the new points and reports the rest as cache hits
Optimize to a specoptimize_submit — vary bounded parameters until every constraint passes, then report whether it was proven. A bounded Nelder-Mead (derivative-free; there is no adjoint through a CFD solve) over the same objective/constraint mapping the contract layer uses, with a screen→solver fidelity ladder. Two rules come from the contract layer: an indeterminate constraint is a measurement problem, not a failed step (it neither attracts nor repels the search), and convergence is not proof — a margin narrower than its own uncertainty band is reported unproven, however tidily the simplex converged
Assembly & interfacesmake_assembly, add_part, list_assembly_parts, merge_assembly, publish_interface, interface_align_check, assembly_lock, assembly_lock_check, bom_extract
Drawings (TechDraw, headless)make_drawing_page, add_projection_group, add_section_view, add_thumbnail, add_dimension, add_annotation, add_feature_note, add_gdt_callout (feature control frames), set_title_block, fit_page, export_drawing (PDF/SVG/DXF), plus completeness/legibility gates drawing_gate, drawing_legibility
Inspection (first-article)balloon_drawing (revision-stable balloon numbering), inspection_plan (characteristic list with a measurement method per row, by the gauge-maker's 10:1 rule), fai_report (AS9102-Form-3-shaped CSV/SVG/PDF — not a certified submission); drawing_gate(require_ballooned=True) makes a ballooned print a release requirement
Release packages (vendor / RFQ)release_package — the one-call deliverable bundle for an item at a revision: STEP + drawings (PDF/SVG/DXF) + recursive BOM + inspection package + a blake2b-checksummed manifest. Gated before anything is written: the item must be in a releasable lifecycle state (or draft=True, which watermarks every artifact PRELIMINARY), drawing_gate must pass for every included page, and the title block's part number / revision / material must match the items registry — a mismatch is a failure with a naming diff, never a silent fix. Byte-reproducible (the same revision re-releases to identical checksums), stamps the ECO into the manifest and the print, and rfq=True adds quantity breaks + the cost_estimate rollup while dropping internal-only artifacts
Off-the-shelf parts (buyability)catalog_search (what standard components exist, in which sizes and stocked lengths), catalog_nearest (snap a wanted size to a real one — asked for an M4×13 it answers 12 and 16), catalog_check, standard_part_designate (canonical designations: ISO 4762 M4×12 A2, 608-2RS, AS568-214 NBR70, stamped on the part at creation), designation_check, bom_extract(orderable=True) (per-line stocked / not_stocked with alternatives)
Visual feedbackrender_view, render_views (8 preset views, multi-view sheets), render_photoreal / render_photoreal_submit (Blender studio scene with per-part appearance for whole assemblies, or the FreeCAD Render add-on renderers; renderer="auto"), render_capabilities
FEM (FreeCAD/CalculiX/Elmer)fem_new_analysis, fem_set_solver, fem_set_material, fem_set_nonlinear_material, fem_add_constraint (fixed/force/pressure/displacement/temperature/heatflux/initial_temperature), contact_setup, fem_mesh, fem_mesh_refinement, fem_modal, fem_buckling, fem_run, fem_run_submit (the same CalculiX solve off the MCP channel; results readers accept its job_id), fem_results, fem_result_probe (stress/disp/temp at a point or face), fem_modal_results, fem_buckling_results, fem_thermal_results, plus the legacy fem_cantilever_demo
Engineering oracles & hand-calcsmachine elements (gear_rating, bearing_life, belt_drive, spring_check, bolted_joint_check, press_fit_stress, seal_check), structural (beam_modal, beam_buckling, plate_check, hertz_contact, elastica_deflection, plastic_collapse, random_vibration, harmonic_response), durability (fatigue_check, fracture_check, creep_flag, wear_estimate), thermal (thermal_lumped, thermal_transient_1d, thermal_composite_wall, h_estimate), tolerance/GD&T (tolerance_stackup, fit_check, fit_class, gdt_check)
Simulation families (external solvers, async)screens + full solves that shell out to OpenFOAM/Elmer/CalculiX/openEMS/YADE/KrakenOS, most via a submit→poll job pattern: thermal/CHT (cht_channel_submit, cht_graetz_submit, thermal_transient_submit, thermal_radiation_submit), CFD (cfd_pipe_flow, cfd_body_drag, cfd_internal_flow_submit, cfd_external_flow_submit — including the virtual wind tunnel: hand it a solid and get Cd/Cl/Cm from an integrated force, gated against the sphere drag curve; every steady solve carries a trust block (convergence, checkMesh, measured y+) and cfd_mesh_independence_submit/grid_convergence put a Richardson/GCI error band on geometry with no analytic twin), EM (em_skin_depth, em_dc_resistance, em_field, em_conduction_submit, em_induction_submit, em_fullwave_submit), acoustics (acoustic_screen, acoustic_fem_submit, acoustic_radiation_submit), FSI (fsi_*), molding (molding_screen, molding_fill_submit, molding_warpage_submit), drop/impact (drop_impact, bar_impact, impact_dynamics_submit — the meshed part flown into a rigid floor, flat / edge / corner, gated against the exact St-Venant bar), granular/DEM (granular_screen, dem_pack_submit, dem_flow_submit), optics (optics_lens_design, optics_lens_optimize, optics_raytrace, optics_solid_trace), multibody (mechanism_kinematics, mechanism_simulate_submit), topology (topology_optimize_submit, topology_to_solid)
Async jobsjob_status, job_result, job_list — poll/collect any *_submit long-running solve; solve_capabilities reports which solvers currently resolve
Materials & fluidsmaterial_list, material_get, material_select, fluid_props — mechanical-property / molding / CoolProp thermophysical corpora behind a typed lookup
Sheet metalsheet_base (base flange), sheet_flange / sheet_tab / sheet_hem (bends placed by stable edge tag), sheet_unfold (K-factor flat pattern + per-bend allowance/deduction, with the K in force and its source echoed into every result), sheet_refold (round-trip verification against the folded solid), sheet_flat_export (layered DXF — CUT / BEND_UP / BEND_DOWN, the file a laser/brake shop quotes from), sheet_check (min bend radius by material, min flange, hole-to-bend, refold collision)
Manufacturing & Design-for-Xdfm_check (also runs the sheet-metal press-brake rules when handed a sheet part), dfa_check, moldability_check, optics_moldability_check, pack_check, cost_estimate, slice_estimate, slice_gcode_submit, laminate_properties, drop_impact
CNC (machinability + machining time)cnc_machinability_check (setups from the tool-approach census, undercuts, tool L/D, sharp/small internal corners, thin walls — pure geometry, no CAM engine), cnc_time_estimate (material-removal-rate model: removed volume / MRR plus finishing area, ±50 % against the flat table's ±100 %; feeds cost_estimate(machine_time_hr=…))
Tolerance ↔ costtolerance_cost_check (per-dimension IT grade, the cheapest process that holds it naturally, a relative cost index, and a flag when a dimension is tighter than the declared process can hold without a secondary operation), suggest_loosening (the loosest tolerance that works — greedy loosening, every

Documentation truncated — see the full README on GitHub.

Reviews

No reviews yet

Be the first to review this server!