Control FreeCAD from Claude Desktop using the Model Context Protocol (MCP).
Inspired by blender-mcp.
Instagram: https://www.instagram.com/perromods/
Current: 1.2.2 | Target FreeCAD: 1.0.2
14 fixes from a second full audit. Validated live against FreeCAD 1.0.2 — smoke test 11/11 (3 new checks). Highlights:
- ✅ No more double execution — the bridge client no longer resends a request after a read timeout; slow operations (large booleans, meshing) could previously run twice.
- ✅ No more ghost tasks — a main-thread dispatch timeout now cancels the queued task instead of letting it mutate the document later.
- ✅
run_pythongated in FreeCAD itself — new "Allow run_python" checkbox in the workbench dock; the port can no longer be used to execute arbitrary code while the option is off. - ✅ Handlers survive the dock closing — the main-thread pump timer is owned by the server, not the UI.
- ✅ Clean
Shapeguards everywhere — fillet/chamfer/arrays/measurements on sketches or spreadsheets now returnINVALID_PARAMSinstead of raw tracebacks; failed fillets/chamfers are rolled back instead of leaving broken objects. - ✅ Sketch plane orientation corrected — XZ back to +90° about X (1.2.1 had inverted it), YZ uses FreeCAD's origin-plane quaternion.
- ✅
fem_add_forcereports honestly — response includesdirection_applied(FreeCAD 1.0 cannot take a raw vector direction). - ✅ Misc:
get_distancename pairing validated,sides >= 3for polygon prisms,save_documentpath validation, screenshot error envelope,geo_indexrequired for constraints.
Full audit: 18 bugs identified and corrected across handlers, server, MCP envelope and smoke tests. Validated live against FreeCAD 1.0.2 — smoke test 8/8.
User / agent-visible changes:
- ✅ Coherent error classification —
ValueError/KeyErrorfrom handlers now returnINVALID_PARAMS(previously fell through to an opaqueFREECAD_ERROR). - ✅
freecad_import_fileactually supports STL — used to advertise STL butPart.insertrejected it; now dispatches by extension toMesh.insert. - ✅
freecad_polar_arrayfixed — removed dead conditional and div-by-zero withcount=0; semantics clarified (360°→total/count; partial →total/(count-1)). - ✅
freecad_fem_add_forceapplies direction — thedirectionparameter is no longer silently dropped; assigned toDirectionVectorwhen the build exposes it. - ✅
freecad_duplicatevalidatesShape— clear error when trying to duplicate sketches/spreadsheets. - ✅
freecad_gui_screenshotvalidates active GUI — informativeLookupErrorin headless mode. - ✅
freecad_fillet/freecad_chamfer— validate edge index range (1..N) before calling FreeCAD. - ✅
freecad_set_property— accepts{x,y,z}dicts in addition to primitives. - ✅
freecad_create_sketchXZ plane — orientation fixed (rotation −90° around X). - ✅
freecad_export/freecad_export_stl— validate that the destination directory exists. - ✅ Bridge envelope —
error.details.tracefrom the backend is now preserved through to the MCP client. - ✅ More robust server —
listen(8)instead oflisten(1),stop()joins the thread. - ✅ Smoke test extended with
invalid_params_error(8 checks instead of 7). - ✅ Array docstrings clarify that
countincludes the original (count-1duplicates generated). - ✅ FEM init without the dead
from femtools import ccxtoolsimport. - ✅
_get_obj(None)now producesINVALID_PARAMS: Missing 'name'. - ✅
bridge_clientretry regenerates UUID so logs can distinguish attempts.
This release focuses on agent decision quality — fewer dead-ends, fewer wrong-workbench attempts, fewer wasted tool calls:
- ✅ Three new MCP prompts steering Claude toward the correct workbench and workflow:
workbench_selection_strategy— decision tree Part/PartDesign/Draft/Sketcher/Arch/TechDraw/FEM/Spreadsheet/Mesh/CAMdrafting_2d_strategy— canonical Draft workflow for 2D deliverables (DXF/SVG)fem_workflow_strategy— strict FEM precondition order (single solid → material → BCs → mesh → solve)
- ✅ Hardened
freecad_parametric_modeling_strategywith 5 parametric-correctness rules (sketch-on-face, External Geometry, fully-constrained before extrude, Pad locked to sketch, prefer Pattern over duplication) and an explicit internal-name reference policy - ✅ Refined tool docstrings:
freecad_create_sketchdocuments face attachment + constraint state,freecad_boolean_cutdocuments selection order + the oversize trick to avoid face-on-face failures - ✅ All guidance sourced from the official FreeCAD manual (Yorik) — verbatim rules where applicable
Compared to 1.0.0, this release focuses on production stability and agent UX predictability:
- ✅ Standardized MCP response envelope:
{ok, result|error}for consistent parsing - ✅ Actionable bridge error taxonomy with stable error codes (ADDON_NOT_CONNECTED, BRIDGE_IO_TIMEOUT, etc.)
- ✅ Runtime capability reporting with per-domain status objects (
enabled+ guidance message) - ✅ Text-first scene inspection policy (
freecad_get_scene_info) — preferred over screenshots - ✅ Deterministic smoke tests with process-friendly exit codes
- ✅ Explicit Error Code Reference for operator troubleshooting
- ✅ FreeCAD-native parametric workflow alignment (Body → Sketch → constraints → Pad/Pocket)
79 tools organized in 12 domains:
| Domain | Tools |
|---|---|
| Documents | new, open, save, list, set active |
| Capabilities | get_capabilities (status-first runtime checks) |
| Inspection | get_scene_info (rich textual snapshot — preferred over screenshot) |
| Primitives | box, cylinder, sphere, cone, torus, polygon prism |
| Booleans | cut, fuse, common |
| Transformations | translate, rotate, set placement, mirror |
| Operations | extrude, revolve, fillet, chamfer, loft, sweep |
| Arrays | linear, polar |
| Sketcher | sketch, line, circle, arc, rectangle, constraints (basic & advanced) |
| PartDesign | body, pad, pocket, hole |
| Measurements | bounding box, volume, area, distance |
| Mesh | shape to mesh, export STL |
| TechDraw | page, view |
| Draft | line, dimension, text |
| FEM | analysis, material, fixed, force |
| CAM | job, profile |
| Spreadsheet | create, set cell, get cell |
| GUI | screenshot, viewport control, selection, color, transparency |
| Dev | run_python (arbitrary FreeCAD API access) |
When you ask Claude to "look at," "describe," "analyze," or "take a screenshot" of the scene:
Claude uses freecad_get_scene_info — a single textual snapshot containing:
- Active document (name, label, path, modified state, object count)
- Every object (name, label, type, placement, visibility, bounding box, volume, area)
- Active viewport camera (type, position, orientation)
- Current GUI selection
Why? You already see the FreeCAD viewport in real time. Sending an image back wastes tokens. freecad_gui_screenshot is still available for explicit image requests, but text inspection is the default.
Five global MCP prompts guide Claude's behavior:
-
scene_inspection_strategy
Always start withfreecad_get_scene_info; usefreecad_gui_screenshotonly on explicit image request. -
workbench_selection_strategy
Decision tree for picking the right workbench/domain before picking tools. Covers Part vs PartDesign, Draft vs Sketcher, Arch/BIM, TechDraw, FEM, Spreadsheet, Mesh and CAM. -
freecad_parametric_modeling_strategy
FollowBody → Sketch 2D → constraints → Pad/Pocket (and details)workflow until final part. Includes hard parametric-correctness rules: sketch-on-face for face extension, External Geometry for constraints against existing solids, fully-constrained sketch before Pad/Pocket, never move a Pad (move its sketch), prefer Pattern over manual duplication, reference objects by internal name. -
drafting_2d_strategy
Canonical Draft workflow when the deliverable is paper/DXF/SVG. Guidelines first, snapped final geometry on top, dimensions/text last. Never mix Draft and Sketcher for the same deliverable. -
fem_workflow_strategy
Strict FEM order: capability gate → single fused solid → analysis → material → fixed support → load → mesh → solve. Surfaces typical root causes when results look wrong.
All five enforce:
- Prefer structured text over images to reduce token usage
- Recompute and validate before declaring completion
- Do not export automatically — only on explicit user request
- Status-first checks (
freecad_get_capabilities) before optional domains (FEM/CAM/TechDraw/Mesh/Assembly)
- FreeCAD 1.0+
- Python 3.10+
- Claude Desktop
Open a terminal (PowerShell on Windows) and run:
python -m pip install --upgrade mcpVerify:
python -c "import mcp; print(mcp.__version__)"git clone https://github.com/Perro-Megabass/freecad-mcp.git
cd freecad-mcpFind your FreeCAD user Mod directory.
Open FreeCAD → View → Panels → Python console, and run:
import os, FreeCAD
print(os.path.join(FreeCAD.getUserAppDataDir(), "Mod"))Typical paths:
| OS | Path |
|---|---|
| Windows | C:\Users\<you>\AppData\Roaming\FreeCAD\Mod |
| macOS | ~/Library/Preferences/FreeCAD/Mod |
| Linux | ~/.local/share/FreeCAD/Mod |
Copy the FreeCADMCP folder into that Mod directory:
<MOD_DIR>/FreeCADMCP/
__init__.py
Init.py
InitGui.py
server.py
handlers.py
ui.py
Restart FreeCAD after copying.
In FreeCAD:
- View → Workbench → MCP Bridge
- A dock panel appears on the right
- Click Connect
Expected status: Listening on 127.0.0.1:9877
Locate the config file:
| OS | Path |
|---|---|
| Windows (Store) | %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json |
| Windows (classic) | %APPDATA%\Claude\claude_desktop_config.json |
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Add the freecad entry. Replace <PROJECT_DIR> with the absolute path where you cloned this repo:
Windows:
{
"mcpServers": {
"freecad": {
"command": "python",
"args": ["-m", "freecad_mcp"],
"cwd": "<PROJECT_DIR>",
"env": {
"PYTHONPATH": "<PROJECT_DIR>",
"FREECAD_ALLOW_RUN_PYTHON": "true"
}
}
}
}macOS / Linux:
{
"mcpServers": {
"freecad": {
"command": "python3",
"args": ["-m", "freecad_mcp"],
"cwd": "<PROJECT_DIR>",
"env": {
"PYTHONPATH": "<PROJECT_DIR>",
"FREECAD_ALLOW_RUN_PYTHON": "true"
}
}
}
}Fully quit Claude Desktop (system tray → Quit), then relaunch.
Verify:
Settings → Developer → Local MCP servers → freecad → status: running
With FreeCAD running and the bridge connected, test the integration independently:
cd <PROJECT_DIR>
python test_bridge.pyThis smoke test validates:
| Check | Validates |
|---|---|
ping |
Bridge connectivity and FreeCAD version |
get_capabilities |
Workbench availability and per-domain status |
new_document |
Document creation |
list_objects |
Object enumeration |
recompute |
Parametric model updates and stable response envelope |
get_scene_info |
Rich textual scene snapshot (preferred tool) |
bogus_action_error |
Error envelope and code taxonomy (expects INVALID_PARAMS) |
invalid_params_error |
Missing-parameter dispatch maps to INVALID_PARAMS (not FREECAD_ERROR) |
Expected output:
[PASS] ping
[PASS] get_capabilities
[PASS] new_document
[PASS] list_objects
[PASS] recompute
[PASS] get_scene_info
[PASS] bogus_action_error
[PASS] invalid_params_error
Summary: 8/8 checks passed
Exit code: 0 (success) or 1 (failures) or 2 (connection error).
| Variable | Default | Description |
|---|---|---|
FREECAD_HOST |
127.0.0.1 |
Bridge host |
FREECAD_PORT |
9877 |
Bridge port |
FREECAD_TIMEOUT_SEC |
60 |
Per-call timeout (seconds) |
FREECAD_ALLOW_RUN_PYTHON |
false |
Enable freecad_run_python tool (MCP side; FreeCAD side also requires the Allow run_python dock checkbox) |
FREECAD_MCP_TELEMETRY |
true |
Enable lightweight per-tool telemetry |
FREECAD_MCP_TELEMETRY_PATH |
system temp | JSONL telemetry output path |
- Open FreeCAD → activate MCP Bridge workbench → click Connect
- Open Claude Desktop
- Ask Claude to operate FreeCAD
Example prompts:
- "Create a 20×20×20 mm box in a new document"
- "Boolean cut the box with a cylinder of radius 5"
- "Export the result as STEP to my Desktop"
- "Create a TechDraw page with a top view at scale 1:1"
Bridge and client errors use stable, actionable codes. When something fails, the error code tells you exactly what to check:
| Code | Meaning | Action |
|---|---|---|
ADDON_NOT_CONNECTED |
FreeCAD bridge is not listening | Open FreeCAD, activate MCP Bridge workbench, click Connect |
CONNECTION_TIMEOUT |
TCP handshake timed out | Confirm FreeCAD is responsive; check port availability |
BRIDGE_CONNECT_FAILED |
Socket connection error (OS/network) | Verify FREECAD_HOST and FREECAD_PORT; check firewall |
BRIDGE_IO_TIMEOUT |
Request/response timed out mid-call | Retry; if persistent, simplify operation or restart bridge |
EMPTY_RESPONSE |
Connection accepted but no payload | Reconnect bridge; ensure port is not hijacked by another service |
INVALID_BRIDGE_RESPONSE |
Non-JSON response on configured port | Verify FREECAD_PORT points to FreeCAD MCP bridge (not another service) |
NOT_CONNECTED |
Generic client-side connection failure | Verify FreeCAD is running and MCP Bridge workbench is connected |
INVALID_PARAMS |
Unknown action or malformed parameters | Check action name; verify parameter schema in tool docs |
NOT_FOUND |
Requested object/document not found | Verify object/document names; check active document context |
FREECAD_ERROR |
Handler or runtime error inside FreeCAD | Review error message; verify inputs; retry with corrected values |
NOT_CONNECTED or ADDON_NOT_CONNECTED
→ Open FreeCAD, activate MCP Bridge workbench, click Connect. Check status shows Listening on 127.0.0.1:9877.
No module named freecad_mcp
→ Verify PYTHONPATH and cwd in Claude Desktop config point to the correct directory (where you cloned the repo).
No module named mcp
→ The Python interpreter Claude uses doesn't have the MCP SDK. Install it:
<python_path> -m pip install mcpPort 9877 already in use
→ Set FREECAD_PORT to a free port and edit PORT = 9877 in FreeCADMCP/server.py.
run_python disabled
→ Two gates must both be open: add "FREECAD_ALLOW_RUN_PYTHON": "true" to the env section in Claude Desktop config, and enable the Allow run_python checkbox in the FreeCAD MCP Bridge dock (or launch FreeCAD with the same env var set).
Workbench not visible in FreeCAD
→ Confirm FreeCADMCP/ folder was copied to the correct Mod directory and restart FreeCAD.
- The socket binds to
127.0.0.1only — not exposed to the network. FREECAD_ALLOW_RUN_PYTHON=trueenables arbitrary Python execution inside FreeCAD (file system access included). Only enable on machines you control.- Keep the workbench Disconnected when not actively using Claude with FreeCAD.
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
This is a third-party integration and not made by FreeCAD. Made by PerroMods.