freecad-mcp
The freecad-mcp server allows an AI assistant (e.g., Claude Desktop) to control FreeCAD for automated 3D CAD modeling, modification, analysis, and verification.
Document management: Create, list, reload, and manage FreeCAD documents.
Object management: Create, edit, delete, and inspect Part/Draft/PartDesign/FEM objects.
Sketch-based modeling: Create sketches and add fully-constrained rectangles, circles, polygons; pad/pocket to make solids; apply fillets, chamfers, linear/polar patterns.
Advanced geometry: Perform boolean operations (cut, fuse, intersect), loft, sweep, and round edges on any solid.
Parts and libraries: List and insert parts from the FreeCAD parts library; instantiate parametric part families (L-brackets, flanged discs, box enclosures).
Placement and measurement: Precisely place objects using bounding boxes/origins; measure volume, area, bounding box, validity, face/edge/vertex counts; probe material presence; measure gaps between parts.
FEM analysis: Run CalculiX finite element analyses to obtain stress, displacement, and node counts.
Visualization: Capture screenshots of active or standard views, optionally focused on specific objects.
Code execution: Run arbitrary Python synchronously or asynchronously for custom automation.
Build workflow: Begin/finish tracked builds, save verified examples, and find similar past builds; apply visual materials.
Provides tools to control FreeCAD, a 3D CAD application, enabling creation, editing, and measurement of 3D models, sketches, and parts through Claude.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@freecad-mcp Create a parametric box with dimensions 50x30x20 mm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
FreeCAD MCP
This repository is a FreeCAD MCP that allows you to control FreeCAD from Claude Desktop.
Demo
Design a flange

Design a toy car

Design a part from 2D drawing
Input 2D drawing

Demo

This is the conversation history. https://claude.ai/share/7b48fd60-68ba-46fb-bb21-2fbb17399b48
Related MCP server: freecad-mcp
Install addon
FreeCAD Addon directory is
Windows:
%APPDATA%\FreeCAD\Mod\Mac:
FreeCAD 1.1:
~/Library/Application\ Support/FreeCAD/v1-1/Mod/FreeCAD 1.0:
~/Library/Application\ Support/FreeCAD/v1-0/Mod/
Linux:
Ubuntu:
~/.FreeCAD/Mod/or~/snap/freecad/common/Mod/(if you install FreeCAD from snap)Debian:
~/.local/share/FreeCAD/ModArch / CachyOS (FreeCAD 1.1 from
extra/freecad):~/.local/share/FreeCAD/v1-1/Mod/
Please put addon/FreeCADMCP directory to the addon directory.
git clone https://github.com/neka-nat/freecad-mcp.git
cd freecad-mcp
# For Linux (Ubuntu/Debian)
cp -r addon/FreeCADMCP ~/.FreeCAD/Mod/
# For Linux (Arch/CachyOS, FreeCAD 1.1 from extra/freecad)
mkdir -p ~/.local/share/FreeCAD/v1-1/Mod/
cp -r addon/FreeCADMCP ~/.local/share/FreeCAD/v1-1/Mod/
# For macOS (FreeCAD 1.1)
cp -r addon/FreeCADMCP ~/Library/Application\ Support/FreeCAD/v1-1/Mod/When you install addon, you need to restart FreeCAD. You can select "MCP Addon" from Workbench list and use it.

And you can start RPC server by "Start RPC Server" command in "FreeCAD MCP" toolbar.

Auto-Start RPC Server
By default, the RPC server must be started manually each time FreeCAD opens. To start it automatically:
Open the FreeCAD MCP menu (switch to the MCP Addon workbench first)
Check Auto-Start Server
The setting is saved to freecad_mcp_settings.json and persists across sessions. On the next FreeCAD launch, the RPC server will start automatically once the application finishes loading.
You can disable it at any time by unchecking Auto-Start Server in the same menu.
Setting up Claude Desktop
Pre-installation of the uvx is required.
And you need to edit Claude Desktop config file, claude_desktop_config.json.
For user.
{
"mcpServers": {
"freecad": {
"command": "uvx",
"args": [
"freecad-mcp"
]
}
}
}If you want to save token, you can set only_text_feedback to true and use only text feedback.
{
"mcpServers": {
"freecad": {
"command": "uvx",
"args": [
"freecad-mcp",
"--only-text-feedback"
]
}
}
}For developer. First, you need clone this repository.
git clone https://github.com/neka-nat/freecad-mcp.git{
"mcpServers": {
"freecad": {
"command": "uv",
"args": [
"--directory",
"/path/to/freecad-mcp/",
"run",
"freecad-mcp"
]
}
}
}Remote Connections
By default the RPC server does not accept remote connections and listens on localhost. To control FreeCAD from another machine on your network:
1. Enable remote connections in FreeCAD
In the FreeCAD MCP toolbar:
Check Remote Connections — the RPC server will bind to
0.0.0.0(all interfaces) on the next restart. For security reasons, it only accepts connections from the IP addresses or CIDR subnets specified in the Allowed IPs field. By default this is127.0.0.1.Click Configure Allowed IPs and enter a comma-separated list of IP addresses or CIDR subnets that are allowed to connect, e.g.:
192.168.1.100, 10.0.0.0/24127.0.0.1is always the default. Invalid entries are rejected with an error dialog. Restart the RPC server after changing these settings.
2. Point the MCP server at the remote host
Pass the --host flag with the IP address or hostname of the machine running FreeCAD:
{
"mcpServers": {
"freecad": {
"command": "uvx",
"args": [
"freecad-mcp",
"--host", "192.168.1.100"
]
}
}
}The --host value is validated on startup — it must be a valid IPv4/IPv6 address or hostname.
Tools
create_document: Create a new document in FreeCAD.create_object: Create a new object in FreeCAD.edit_object: Edit an object in FreeCAD.delete_object: Delete an object in FreeCAD.execute_code: Execute arbitrary Python code in FreeCAD. See Security model below.insert_part_from_library: Insert a part from the parts library.get_view: Get a screenshot of the active view.get_objects: Get all objects in a document.get_object: Get an object in a document.get_parts_list: Get the list of parts in the parts library.run_fem_analysis: Run the CalculiX solver on an existingFem::FemAnalysisand return summary results (max von Mises stress, max displacement, node count, working directory). Auto-creates aSolverCcxToolsif the analysis has none. Seeexamples/cantilever_fem.pyfor an end-to-end usage example.
Verified geometry tools
Every tool above reports "success" once the underlying FreeCAD call didn't
raise — which a CAD kernel will do for a lot of operations that don't
actually accomplish anything (a pocket cut over empty space, a pattern with
an axis reference that silently resolves to nothing, a placement that gets
reverted by its owning body on the next recompute). The tools below instead
report measured geometry (volume, bounding box, isValid) so a caller can
tell the difference between "the call succeeded" and "the call did what I
wanted":
measure: Volume, area, bounding box,isValid, and face/edge/vertex counts for any object.probe_material_at: Checks whether solid material actually exists inside a given box — "is there a floor/wall here?"place: Positions an object by its real bounding-box corner (not a derived offset), and automatically moves the owningPartDesign::Bodywhen needed, since a feature's ownPlacementis silently reverted by its Body otherwise.create_sketch,add_rectangle,add_circle,add_polygon: Build sketches that are fully constrained as they're drawn (no separate dimensioning step, and no "closed but unpinned" loops that PartDesign refuses to pad).pad,pocket: Extrude/cut a sketch, reportingvolumeBefore/volume/isValid.pocketfails loudly if no material was removed. Through-holes always cut from the midplane so they penetrate regardless of which side the material is on.linear_pattern,polar_pattern: Repeat features along/around a sketch axis, resolving the axis reference correctly so a pattern doesn't silently pattern nothing.fillet,chamfer: Round/bevel a solid feature's edges; reject a sketch as the base outright (with an explanation) instead of poisoning the feature tree.measure_gap: Measures the real gap between two parts along one axis, so a spanning part (e.g. a shelf between two supports) can be sized from measured geometry instead of a number derived independently of where those parts actually ended up.instantiate_family: Builds a parametric part from a small library of part-family templates (l_bracket,flanged_disc,box_enclosure) instead of a manual op sequence.apply_material: Maps a material name (steel, oak, glass, ...) to concrete ShapeColor/Transparency/Shininess values from a small deterministic table, applies them, and reports what was actually read back off the object's ViewObject afterward — instead of leaving a part default-gray or trusting an unverified assignment. Falls back to setting ShapeColor/Transparency throughedit_objecton an addon older than this tool.get_views: Captures Isometric/Front/Top/Right (plus an optional object-centred fifth) of a document in one call, each image labeled, alongside a per-object bounding-box summary that's measured from the real shapes rather than derived. Prefer this overget_viewwhen you need to understand or verify a whole document/assembly's structure; preferget_viewfor one specific angle or a quick re-check.
Strategy prompt
The server ships an asset_creation_strategy MCP prompt that tells the client model which tools to prefer and in what order. It now includes a clarification policy: state an assumption and keep going for cheap-to-change choices like dimensions, materials, and colors; ask before building only when the ambiguity is structural (component count, topology, what the thing is for); batch any remaining questions into a single turn instead of drip-feeding them; and reach for find_similar_builds or get_views before reaching for a question.
Security model
execute_code/execute_code_asyncrun unsandboxed Python on the machine hosting FreeCAD —exec(code, globals())with no import allowlist, AST restrictions, or resource limits. Anything reachable from this tool (import os; os.system(...),subprocess, arbitrary file access) is reachable by anyone who can call it. This is a deliberate design choice, not an oversight, but it means connecting to the RPC port is equivalent to full shell access to that machine — treat network exposure accordingly (see Remote Connections below). The MCP server blocks a denylist of known-dangerous patterns (import os,import subprocess,subprocess.,os.system(,eval(,exec(,open(,__import__) plus three Sketcher calls observed to hang FreeCAD's solver indefinitely on an already-constrained sketch (.solve(,deleteAllGeometry(,movePoint() before running code — this raises the bar against accidental misuse, it is not a security sandbox against an adversarial caller.execute_code's Python namespace is shared and persists across everyexecute_code/execute_code_asynccall for the life of the FreeCAD process (it is therpc_servermodule's ownglobals(), not a fresh namespace per call). This is what letsexecute_code_asyncstash a result in a module-level variable for a laterexecute_codecall to read — but it also means a variable, import, or class defined by one call remains visible (and can collide) in every later, unrelated call, with no reset short of restarting FreeCAD or the RPC server.edit_object's property assignment is partial, not atomic. If several properties are passed and one fails to apply, the properties that did succeed remain applied on the object — only the failure is reported. Always re-check withget_object/measureafter an edit rather than assuming all-or-nothing semantics.The only network access control is an IP allowlist (see Remote Connections) — there is no authentication token and no TLS. It stops connections from unlisted addresses; it does not stop a request that is merely spoofed or already inside the same trusted network.
Contributors
Made with contrib.rocks.
Available Tools
36 toolsadd_circleA
Add a fully-constrained circle to a sketch, centered at (x, y).
Args:
doc_name: The name of the document.
sketch_name: The name of the sketch to add geometry to.
radius: Circle radius.
x: X coordinate of the circle's center in sketch-local space.
y: Y coordinate of the circle's center in sketch-local space.
Inputs: radius (required), doc_name (required), sketch_name (required), x, +1 more. Returns: result
add_polygonA
Add a fully-constrained closed polygon to a sketch from [x, y] points.
This is the only way to express an L, T, hexagon, or other custom
outline. Points are connected in order and the loop closes automatically
(do not repeat the first point at the end).
Args:
doc_name: The name of the document.
sketch_name: The name of the sketch to add geometry to.
points: List of [x, y] points in sketch-local space, at least 3.
Inputs: points (required), doc_name (required), sketch_name (required). Returns: result
add_rectangleA
Add a fully-constrained rectangle to a sketch, corner at (x, y).
The rectangle is locked in place (not just shaped) as it is created, so
the sketch is immediately safe to pad/pocket — no separate dimensioning
step needed.
Args:
doc_name: The name of the document.
sketch_name: The name of the sketch to add geometry to.
width: Rectangle width along the sketch's local X axis.
height: Rectangle height along the sketch's local Y axis.
x: X coordinate of the rectangle's corner in sketch-local space.
y: Y coordinate of the rectangle's corner in sketch-local space.
Inputs: width (required), height (required), doc_name (required), sketch_name (required), +2 more. Returns: result
begin_buildA
Start tracking a non-trivial build, and check for similar prior examples.
Call this FIRST for anything beyond a single simple part — a multi-part
assembly (a chair, an enclosure with hardware, anything with more than
one body) — before creating any geometry. State the request in `prompt`
(as close to verbatim as you can) and list the components you expect to
build in `expected_components` (e.g. ["seat", "leg_fl", "leg_fr",
"leg_bl", "leg_br", "stretcher_front", ...]).
This does two things: it forces an explicit decomposition up front
(plan-shape mistakes — building a "chair" from 2 components instead of
9 — are much easier to avoid by committing to a component list before
building than to catch after), and it returns any similar builds already
verified in this project's corpus, which you should use as a reference
for tool sequence and structure, not copy blindly.
Always follow up with finish_build once you've built (and separately
verified with measure/probe_material_at) the components you declared
here — only builds that are re-verified as valid AND match this
declared component count get saved as reusable examples.
Args:
doc_name: The document this build is happening in.
prompt: The request you're building, close to verbatim.
expected_components: Names you expect to create for this build.
Inputs: prompt (required), doc_name (required), expected_components (required). Returns: result
booleanA
Cut, fuse, or intersect ARBITRARY existing solids.
This is the assembly primitive. `pocket` cuts a sketch out of its own
PartDesign Body and cannot subtract one part from another — use this
whenever the tool is a separate object: hollowing a piston with a
cavity solid, subtracting a shaft from a housing, merging bosses onto a
body.
A `cut` that removes no material FAILS loudly rather than reporting
success: the usual cause is that the tool solid does not actually
overlap the base, which is invisible in a render.
A powerful pattern this enables: build the cutting tool itself by
subtraction, so hollowing preserves internal structure. Cutting a
piston with (cavity MINUS bosses) hollows the piston while leaving the
pin bosses standing — material survives where it is outside the cavity
OR inside a boss.
Args:
doc_name: The document.
name: Name for the resulting object.
operation: "cut", "fuse", or "common".
base: Name of the base solid.
tools: Names of the solids to cut/fuse/intersect with the base.
Multiple tools are combined automatically for a cut.
Inputs: base (required), name (required), tools (required), doc_name (required), +1 more. Returns: result
chamferA
Bevel edges of a solid feature (PartDesign::Chamfer).
base_feature MUST be a solid feature, never a sketch — see fillet's
docstring for why.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body.
name: The name to give the new Chamfer feature.
base_feature: The solid feature whose edges to bevel.
edges: Edge names on base_feature's shape (e.g. ["Edge3", "Edge7"]).
size: Chamfer size in mm.
Inputs: name (required), size (required), edges (required), doc_name (required), +2 more. Returns: result
create_documentA
Create a new document in FreeCAD.
Args:
name: The name of the document to create.
Returns:
A message indicating the success or failure of the document creation.
Examples:
If you want to create a document named "MyDocument", you can use the following data.
```json
{
"name": "MyDocument"
}
```
Inputs: name (required). Returns: result
create_objectA
Create a new object in FreeCAD. Object type is starts with "Part::" or "Draft::" or "PartDesign::" or "Fem::".
For sketch-based solids (a body you'll pad/pocket/fillet/pattern), prefer
create_sketch + add_rectangle/add_circle/add_polygon + pad/pocket instead
— those tools verify the result (volume, isValid, fullyConstrained)
instead of only reporting whether the FreeCAD call raised.
Args:
doc_name: The name of the document to create the object in.
obj_type: The type of the object to create (e.g. 'Part::Box', 'Part::Cylinder', 'Draft::Circle', 'PartDesign::Body', etc.).
obj_name: The name of the object to create.
obj_properties: The properties of the object to create.
Returns:
A message indicating the success or failure of the object creation and a screenshot of the object.
The response's object_name is the name FreeCAD actually assigned,
which can differ from the obj_name you requested: a FreeCAD document
is one flat namespace, so a name collision (e.g. two components both
naming a pad "leg_pad") is silently resolved by FreeCAD appending a
suffix ("leg_pad001"). Always use the returned object_name in later
calls, not the name you requested.
Examples:
If you want to create a cylinder with a height of 30 and a radius of 10, you can use the following data.
```json
{
"doc_name": "MyCylinder",
"obj_name": "Cylinder",
"obj_type": "Part::Cylinder",
"obj_properties": {
"Height": 30,
"Radius": 10,
"Placement": {
"Base": {
"x": 10,
"y": 10,
"z": 0
},
"Rotation": {
"Axis": {
"x": 0,
"y": 0,
"z": 1
},
"Angle": 45
}
},
"ViewObject": {
"ShapeColor": [0.5, 0.5, 0.5, 1.0]
}
}
}
```
If you want to create a circle with a radius of 10, you can use the following data.
```json
{
"doc_name": "MyCircle",
"obj_name": "Circle",
"obj_type": "Draft::Circle",
}
```
If you want to create a FEM analysis, you can use the following data.
```json
{
"doc_name": "MyFEMAnalysis",
"obj_name": "FemAnalysis",
"obj_type": "Fem::AnalysisPython",
}
```
If you want to create a FEM constraint, you can use the following data.
```json
{
"doc_name": "MyFEMConstraint",
"obj_name": "FemConstraint",
"obj_type": "Fem::ConstraintFixed",
"analysis_name": "MyFEMAnalysis",
"obj_properties": {
"References": [
{
"object_name": "MyObject",
"face": "Face1"
}
]
}
}
```
If you want to create a FEM mechanical material, you can use the following data.
```json
{
"doc_name": "MyFEMAnalysis",
"obj_name": "FemMechanicalMaterial",
"obj_type": "Fem::MaterialCommon",
"analysis_name": "MyFEMAnalysis",
"obj_properties": {
"Material": {
"Name": "MyMaterial",
"Density": "7900 kg/m^3",
"YoungModulus": "210 GPa",
"PoissonRatio": 0.3
}
}
}
```
If you want to create a FEM mesh, you can use the following data.
The `Shape` property is required (legacy `Part` is also accepted).
On FreeCAD 1.x the size limits are `CharacteristicLengthMax/Min`;
the legacy `ElementSizeMax/Min` keys are also accepted.
```json
{
"doc_name": "MyFEMMesh",
"obj_name": "FemMesh",
"obj_type": "Fem::FemMeshGmsh",
"analysis_name": "MyFEMAnalysis",
"obj_properties": {
"Shape": "MyObject",
"CharacteristicLengthMax": 10,
"CharacteristicLengthMin": 0.1
}
}
```
Inputs: doc_name (required), obj_name (required), obj_type (required), analysis_name, +1 more. Returns: result
create_sketchA
Create an empty sketch attached to one of a PartDesign Body's base planes.
plane is one of "XY", "XZ", "YZ". Populate it with add_rectangle/
add_circle/add_polygon (each one locks its own geometry so the sketch
stays fully constrained), then pad or pocket it.
Args:
doc_name: The name of the document.
body_name: The name of the existing PartDesign::Body to sketch in.
name: The name to give the new sketch.
plane: Which base plane to attach to: "XY", "XZ", or "YZ".
Inputs: name (required), doc_name (required), body_name (required), plane. Returns: result
delete_objectA
Delete an object in FreeCAD.
Args:
doc_name: The name of the document to delete the object from.
obj_name: The name of the object to delete.
Returns:
A message indicating the success or failure of the object deletion and a screenshot of the object.
Inputs: doc_name (required), obj_name (required). Returns: result
edit_objectB
Edit an object in FreeCAD.
This tool is used when the create_object tool cannot handle the object creation.
Args:
doc_name: The name of the document to edit the object in.
obj_name: The name of the object to edit.
obj_properties: The properties of the object to edit.
Returns:
A message indicating the success or failure of the object editing and a screenshot of the object.
Inputs: doc_name (required), obj_name (required), obj_properties (required). Returns: result
execute_codeA
Execute arbitrary Python code in FreeCAD.
This is an unverified escape hatch: prefer the typed tools (pad, pocket,
place, measure, probe_material_at, fillet, chamfer, linear_pattern,
polar_pattern, create_sketch/add_rectangle/add_circle/add_polygon,
instantiate_family) for normal geometry work, since they check what
actually happened (volume, isValid, bounding box) instead of only
reporting whether the code raised — the usual CAD failure mode is an
operation that silently does nothing, not one that throws. Code
containing process-level imports (os/subprocess), eval/exec/open/
__import__, or Sketcher calls known to hang FreeCAD's solver (.solve(),
deleteAllGeometry(), movePoint() on an already-constrained sketch) is
rejected before it runs.
Args:
code: The Python code to execute.
Returns:
A message indicating the success or failure of the code execution, the output of the code execution, and a screenshot of the object.
Inputs: code (required). Returns: result
execute_code_asyncA
Execute Python code in FreeCAD without waiting for completion.
This is an unverified escape hatch: prefer the typed tools (pad, pocket,
place, measure, probe_material_at, fillet, chamfer, linear_pattern,
polar_pattern, create_sketch/add_rectangle/add_circle/add_polygon,
instantiate_family) for normal geometry work, since they check what
actually happened instead of only reporting whether the code raised.
Code containing process-level imports (os/subprocess), eval/exec/open/
__import__, or Sketcher calls known to hang FreeCAD's solver (.solve(),
deleteAllGeometry(), movePoint() on an already-constrained sketch) is
rejected before it runs.
Use this ONLY for long-running background computations that do NOT touch the
FreeCAD GUI or mutate the FreeCAD document tree directly.
This tool runs the submitted code in a background thread and returns
immediately. Because it does not run on FreeCAD's main GUI thread, the code
must NOT call FreeCADGui APIs, manipulate the active view or selection, create
or edit document objects, change object properties, call doc.recompute(), or
save documents.
For code that touches FreeCAD documents, document objects, FreeCADGui, the
active view, selection, recompute, or save operations, use execute_code instead.
execute_code runs on the FreeCAD GUI thread and is the safe default for normal
FreeCAD automation.
Use execute_code_async only for background-safe work such as long-running
pure OCCT geometry calculations (e.g. fuse/cut/loft on already-fetched shapes)
or other CPU-bound computations that do not interact with the document or GUI.
Typical usage pattern:
1. Fetch shapes into local variables first (via execute_code on the GUI thread).
2. Store intermediate results in a module-level Python variable (not in the
FreeCAD document) so execute_code can read them later.
3. Run the heavy computation via execute_code_async.
4. After the expected computation time has elapsed, apply results to the
document via execute_code (which runs on the GUI thread).
Args:
code: Background-safe Python code to execute.
Returns:
A message confirming that background execution has started.
Inputs: code (required). Returns: result
filletA
Round edges of a solid feature (PartDesign::Fillet).
base_feature MUST be a solid feature (e.g. a Pad or Pocket), never a
sketch — filleting a sketch makes that broken feature the Body's Tip,
and the NEXT pad/pocket then fails with an unrelated "shape is invalid"
error that points nowhere near the real mistake. This tool rejects a
sketch base outright with that explanation instead of letting it happen.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body.
name: The name to give the new Fillet feature.
base_feature: The solid feature whose edges to round.
edges: Edge names on base_feature's shape (e.g. ["Edge3", "Edge7"]).
radius: Fillet radius in mm.
Inputs: name (required), edges (required), radius (required), doc_name (required), +2 more. Returns: result
fillet_edgesA
Round the edges of ANY solid — the cheapest large gain in realism.
Real machined parts have a radius on essentially every edge. Geometry
built from primitives and booleans reads as obviously synthetic
precisely because everything meets at a sharp corner, so filleting is
usually the single highest-impact finishing step on a part.
Unlike the `fillet` tool (which works on a PartDesign feature inside a
Body), this works on any solid, including boolean results.
Omit `edges` to fillet every edge. Use `max_edge_length` to round only
small detail edges (bosses, ribs, pocket corners) while leaving long
structural edges sharp — usually what you want on a large part.
A radius bigger than the local geometry allows makes the operation fail
rather than silently filleting a subset; reduce the radius or select
fewer edges.
IMPORTANT — fillet parts BEFORE fusing them, not after. Filleting every
edge of a large fused assembly usually fails: the fusion creates many
short, awkward edges where components meet, and one unfilletable edge
rejects the whole operation. Rounding each component while it is still
a simple solid succeeds far more often and gives the same result. If a
fillet on a fused body is refused, that is the first thing to change.
Args:
doc_name: The document.
name: Name for the resulting filleted object.
base: Name of the solid whose edges to round.
radius: Fillet radius in mm.
edges: Optional 1-based edge indices. Omit to fillet all edges.
max_edge_length: Optional filter — only fillet edges no longer than this.
Inputs: base (required), name (required), radius (required), doc_name (required), +2 more. Returns: result
find_similar_buildsA
Search the verified-build corpus for prior examples similar to a request.
Returns full (prompt, tool-call sequence, verified outcome) records for
the closest matches — use these as concrete reference for how a similar
assembly was actually built and verified, not as something to copy
mechanically (dimensions, names, and positions will differ). Useful to
call again mid-build for a specific sub-assembly, not just once via
begin_build.
Args:
query: A natural-language description of what you're building.
top_k: Maximum number of examples to return (default 3).
Inputs: query (required), top_k. Returns: result
finish_buildA
Close out a build: re-verify every claimed component, and save it as a reusable example if it actually checks out.
Call this after begin_build and after you believe the build is
complete. Every name in component_names is re-measured with `measure`
(never trusted from a prior tool's self-reported success) — the build
is only exported to the corpus if every one of them is a valid shape
AND the count matches what begin_build declared as expected. A build
that's missing pieces or has an invalid shape is reported back to you
with the specific reason, but not saved.
Args:
doc_name: The document passed to begin_build for this build.
component_names: Names of every component you built (should match,
in count, what you declared in begin_build's expected_components).
Inputs: doc_name (required), component_names (required). Returns: result
get_objectA
Get an object from a document. You can use this tool to get the properties of an object to see what you can check or edit.
Args:
doc_name: The name of the document to get the object from.
obj_name: The name of the object to get.
Returns:
The object and a screenshot of the object.
Inputs: doc_name (required), obj_name (required). Returns: result
get_objectsA
Get all objects in a document. You can use this tool to get the objects in a document to see what you can check or edit.
Args:
doc_name: The name of the document to get the objects from.
Returns:
A list of objects in the document and a screenshot of the document.
Inputs: doc_name (required). Returns: result
get_parts_listA
Get the list of parts in the parts library addon.
Inputs: no named parameters. Returns: result
get_viewA
Get a screenshot of the active view.
Args:
view_name: The name of the view to get the screenshot of.
The following views are available:
- "Isometric"
- "Front"
- "Top"
- "Right"
- "Back"
- "Left"
- "Bottom"
- "Dimetric"
- "Trimetric"
width: The width of the screenshot in pixels. If not specified, uses the viewport width.
height: The height of the screenshot in pixels. If not specified, uses the viewport height.
focus_object: The name of the object to focus on. If not specified, fits all objects in the view.
Returns:
A screenshot of the active view.
Inputs: view_name (required), width, height, focus_object. Returns: result
insert_part_from_libraryB
Insert a part from the parts library addon.
Args:
relative_path: The relative path of the part to insert.
Returns:
A message indicating the success or failure of the part insertion and a screenshot of the object.
Inputs: relative_path (required). Returns: result
instantiate_familyA
Build a named part family from parameters instead of a manual op sequence.
Available families:
- "l_bracket": parameters leg_a, leg_b, thickness, width, optional hole_d.
An L-bracket is a cross-section swept along its width, not a footprint
extruded by its thickness — this template encodes that so you don't
have to re-derive it.
- "flanged_disc": parameters diameter, thickness, bolt_circle_diameter,
hole_diameter, optional bolt_count (default 6). Builds a disc with a
verified polar bolt-hole pattern.
- "box_enclosure": parameters outer_width, outer_length, outer_height,
wall_thickness, optional floor_thickness (defaults to wall_thickness).
Cuts the cavity with a separate cutter solid positioned at
z=floor_thickness, so the enclosure actually has a floor — a
sketch-based pocket from the base plane cannot leave one.
All created objects are named with name_prefix so multiple instances can
coexist in one document.
Args:
doc_name: The name of the document.
family: One of "l_bracket", "flanged_disc", "box_enclosure".
name_prefix: Prefix for every object this family creates.
parameters: Family-specific parameters (see the list above).
Inputs: family (required), doc_name (required), parameters (required), name_prefix (required). Returns: result
linear_patternA
Repeat features along a sketch axis (PartDesign::LinearPattern).
axis_sketch/axis MUST name the sketch that owns the H_Axis/V_Axis/N_Axis
datum you want to pattern along — never a feature's Profile link. Passing
a feature reference there resolves to a nested link FreeCAD rejects, or
silently patterns nothing.
`originals` MUST be base features (a Pad or Pocket), never another
pattern. PartDesign cannot pattern a pattern: given one it produces a
perfectly valid solid that changed nothing, which this tool now reports
as a failure rather than a success.
So to build a GRID of holes, do NOT pattern a row along the
perpendicular axis. Put the whole row in ONE sketch (several add_circle
calls), pocket it once so the row is a single base feature, then pattern
that once. Verified: a 2-circle sketch pocketed and patterned gives
exactly 4 holes, while patterning a row feature gives 2 and silently
claims success.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body.
name: The name to give the new LinearPattern feature.
originals: Names of the feature(s) to repeat.
axis_sketch: Name of the sketch owning the axis datum to pattern along.
axis: Which datum on axis_sketch: "H_Axis", "V_Axis", or "N_Axis".
length: Total span of the pattern in mm.
occurrences: Total number of copies, including the original (>= 2).
Inputs: name (required), doc_name (required), body_name (required), originals (required), +4 more. Returns: result
list_documentsA
Get the list of open documents in FreeCAD.
Returns:
A list of document names.
Inputs: no named parameters. Returns: result
loftA
Blend between two or more profiles — tapered and organic shapes.
`pad` extrudes ONE profile at a constant section, so it can only make
prisms. Real parts taper and blend: a connecting-rod shaft narrows from
the big end to the small end, a boss blends into a web, a duct changes
section. Those need a loft.
Give the profiles in order along the blend. They are usually sketches
placed at different heights or on different planes.
`ruled=False` (default) produces a smooth blended surface, which is
what makes a part read as designed rather than as stacked prisms.
`ruled=True` gives straight transitions between sections.
Args:
doc_name: The document.
name: Name for the resulting loft.
profiles: Names of 2+ profile objects (usually sketches), in order.
solid: Produce a solid rather than a surface.
ruled: Straight transitions instead of a smooth blend.
Inputs: name (required), doc_name (required), profiles (required), ruled, +1 more. Returns: result
measureA
Measure an object's real geometry: volume, area, bounding box, isValid, and face/edge/vertex counts (including a rough cylindrical-vs-planar face count, useful for "does this have N holes" sanity checks).
Use this to check your own work instead of assuming a mutating tool did
what you intended — FreeCAD's kernel tolerates degenerate input, so "the
call didn't raise" is not evidence that anything useful happened.
Args:
doc_name: The name of the document containing the object.
obj_name: The name of the object to measure.
Inputs: doc_name (required), obj_name (required). Returns: result
measure_gapA
Measure the real gap between two parts' bounding boxes along one axis.
Use this before building a part meant to span two others (e.g. a chair
stretcher between two legs): size and position the spanning part from
the measured gap, instead of deriving a span independently of where
those parts actually ended up — that mismatch is exactly how a
stretcher ends up overhanging its legs by tens of mm.
This is an axis-aligned bounding-box measurement (v0 of a joint model),
not a full face-to-face mate — it does not handle rotated parts or
non-axis-aligned relationships.
Args:
doc_name: The name of the document.
part_a: Name of the first part.
part_b: Name of the second part.
axis: Which axis to measure the gap along: "x", "y", or "z".
Inputs: axis (required), part_a (required), part_b (required), doc_name (required). Returns: result
padA
Extrude a fully-constrained sketch into a solid (PartDesign::Pad).
Fails if the sketch is not fully constrained, or if the resulting solid
is invalid — always reports volumeBefore/volume/isValid/boundingBox so
you can confirm real material was added, not just that the call
succeeded.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body to add this feature to.
sketch_name: The name of the fully-constrained profile sketch.
name: The name to give the new Pad feature.
length: Extrusion length in mm.
midplane: Extrude symmetrically about the sketch plane.
reversed_: Flip the extrusion direction.
Inputs: name (required), doc_name (required), body_name (required), sketch_name (required), +3 more. Returns: result
placeA
Position an object by its real bounding box instead of a derived offset.
Pass exactly one of bbox_min or origin:
- bbox_min: the object's bounding-box minimum corner is moved to this
point. The server computes the offset from the object's CURRENT real
bounding box, so you express intent ("put this corner here") instead
of deriving a translation yourself and getting it wrong.
- origin: sets the placement's base position directly.
rotation ({"Axis": {"x","y","z"}, "Angle": degrees}) is applied before
the bbox/origin move, so a subsequent bbox_min still lands correctly.
IMPORTANT: if obj_name is a feature inside a PartDesign::Body (e.g. a
Pad), this tool automatically moves the owning Body instead — a
feature's own Placement is silently reverted by its Body on recompute,
so setting it directly on the feature appears to work and does nothing.
Check the response's movedBody/placementOwner fields to see when this
substitution happened.
Pass expect_size ([x, y, z]) to verify the resulting bounding box matches
what you intended — this catches the common mistake of mapping a
sketch/pad's width/height/length onto the wrong real-world axis (e.g.
building a 320mm-long rail as 22x22x320 instead of 22x320x22).
Args:
doc_name: The name of the document containing the object.
obj_name: The name of the object to place.
bbox_min: [x, y, z] target for the object's bounding-box minimum corner.
origin: [x, y, z] target for the placement's base position.
rotation: {"Axis": {"x","y","z"}, "Angle": degrees} to set before moving.
expect_size: [x, y, z] expected bounding box, checked after placement.
rotation_center: [x, y, z] to rotate ABOUT, instead of about the
object's own origin. This is what posing a linkage needs — a
connecting rod swings about its wrist pin, a crank arm sweeps
about the main-bearing axis. Without it you would have to
pre-compute the compensating translation yourself, which is the
derived-number mistake this tool exists to prevent.
Inputs: doc_name (required), obj_name (required), origin, bbox_min, +3 more. Returns: result
pocketA
Cut material out of a body with a sketch profile (PartDesign::Pocket).
Fails if no material is removed — a profile positioned over empty space
is the most common mistake, and would otherwise silently "succeed" while
doing nothing. Through-holes (through_all=True) always cut from the
midplane: a sketch sits ON its base plane, so a one-directional
ThroughAll either cuts into empty space or produces an invalid solid
depending on which side of the plane the material is on.
NOTE: a sketch-based pocket can only cut FROM the plane it sits on, so it
can never leave material (a "floor") beneath itself on that side. For a
container cavity, use instantiate_family("box_enclosure", ...) or build
the cavity as a separate cutter solid positioned at z=floor_thickness.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body to cut.
sketch_name: The name of the fully-constrained profile sketch.
name: The name to give the new Pocket feature.
length: Cut depth in mm (ignored if through_all=True).
through_all: Cut all the way through, from the midplane.
Inputs: name (required), doc_name (required), body_name (required), sketch_name (required), +2 more. Returns: result
polar_patternA
Repeat features radially around a sketch axis (PartDesign::PolarPattern).
axis_sketch/axis MUST name the sketch that owns the H_Axis/V_Axis/N_Axis
datum to rotate around — see linear_pattern for why a feature's Profile
link cannot be used directly. This is the tool for bolt circles and
gear-tooth rings.
Args:
doc_name: The name of the document.
body_name: The name of the PartDesign::Body.
name: The name to give the new PolarPattern feature.
originals: Names of the feature(s) to repeat (e.g. one hole Pocket).
axis_sketch: Name of the sketch owning the axis datum to rotate around.
axis: Which datum on axis_sketch: "H_Axis", "V_Axis", or "N_Axis".
angle: Total angular span in degrees (360 for a full bolt circle).
occurrences: Total number of copies, including the original (>= 2).
Inputs: name (required), doc_name (required), body_name (required), originals (required), +4 more. Returns: result
probe_material_atA
Check whether solid material actually exists inside an axis-aligned box.
Answers "is there a floor/wall/boss here" directly by computing the real
geometric intersection of the object with a probe box, instead of
inferring it from a screenshot or trusting that a cut/pad did what was
intended. A classic failure this catches: an "open-top enclosure" pocket
cut from the base-plane sketch leaves the box closed on TOP and open on
the BOTTOM (a sketch sits on its plane and cannot leave a floor beneath
itself) — probing the intended floor location returns 0 material there.
Args:
doc_name: The name of the document containing the object.
obj_name: The name of the object to probe.
box_min: [x, y, z] of the probe box's minimum corner.
box_size: [x, y, z] size of the probe box (all must be positive).
Inputs: box_min (required), box_size (required), doc_name (required), obj_name (required). Returns: result
reload_documentA
Close and re-open a document to pick up external file changes.
Use this AFTER the document's .FCStd file has been modified by
something outside of FreeCAD's GUI process — for example, a
headless `freecadcmd` script that edited and saved the file. The
open GUI document is otherwise unaware of on-disk changes; this
tool closes the stale in-memory copy and reopens the file from
disk so the GUI shows current geometry.
Args:
doc_name: The name of the open document to reload. Must match
the name shown by ``list_documents``.
Returns:
A message confirming the document was reloaded, or describing
the failure (document not loaded, no associated file, etc).
Examples:
```json
{
"doc_name": "chassis"
}
```
Inputs: doc_name (required). Returns: result
run_fem_analysisA
Run the CalculiX solver on an existing Fem::FemAnalysis container and return summary results.
Prerequisites in the document:
- A Part-derived solid (e.g. Part::Box, PartDesign::Body) acting as the geometry.
- A Fem::AnalysisPython container created via `create_object`.
- A Fem::MaterialCommon assigned to the geometry, added to the analysis.
- A Fem::FemMeshGmsh referencing the geometry, added to the analysis (the
mesh is generated automatically when created via `create_object`).
- At least one Fem::ConstraintFixed and one Fem::ConstraintForce (or
ConstraintPressure) bound to faces of the geometry, added to the analysis.
A SolverCcxTools is auto-created if the analysis has none.
The solver runs synchronously on the FreeCAD GUI thread and blocks all
other RPC calls for its duration; do not fan out parallel requests.
Returns max von Mises stress (MPa), max/min displacement (mm), node count,
and the working directory CalculiX wrote to. On failure, returns the
prerequisite-check or solver error along with the working directory for
triage.
Args:
doc_name: Name of the FreeCAD document.
analysis_name: Name of the Fem::AnalysisPython object.
timeout: Seconds to wait for the solver (default 600).
Inputs: doc_name (required), analysis_name (required), timeout. Returns: result
sweepA
Run a profile along a path — genuinely curved parts.
This is how you get curvature that pad/pocket/loft cannot express: a
shaft that bends, a pipe, a curved rib, a hose. The path can be any
wire, including an arc or spline, so the result follows a real curve
rather than a series of straight segments.
`frenet=True` keeps the profile's orientation consistent along the
path, which is what you want for a bent shaft; the alternative can
twist the section unpredictably.
Args:
doc_name: The document.
name: Name for the resulting swept solid.
profile: Name of the profile object to sweep (a sketch or face).
path: Name of the path object (a wire, arc, or spline) to follow.
solid: Produce a solid rather than a surface.
frenet: Keep the profile orientation consistent along the path.
Inputs: name (required), path (required), profile (required), doc_name (required), +2 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 36 tools
Most tools have clearly distinct purposes, aided by detailed descriptions. However, 'fillet' vs 'fillet_edges' and 'execute_code' vs 'execute_code_async' are close pairs that could cause misselection, as the names don't immediately convey their different scopes.
The tool names mix patterns: verb_noun (create_object, list_documents), bare verbs (pad, pocket, loft), and noun_noun (linear_pattern, measure_gap). While each name is readable and mostly intuitive, the lack of a consistent convention makes the set feel less predictable.
With 36 tools, the surface is large and exceeds the 25+ threshold that typically signals bloat. Several tools overlap in purpose (fillet/fillet_edges, measure/measure_gap, execute_code/execute_code_async), inflating the count without adding distinct value, though the complexity of CAD does justify a larger set.
The server covers a broad range of CAD workflows—sketching, padding, pocketing, patterning, booleans, fillets, lofts, sweeps, part families, measurement/verification, and FEM. However, common operations like revolve, mirror/shell, and document lifecycle management (e.g., close/delete document) are missing, leaving notable gaps for a full-featured CAD tool.
Related MCP Connectors
CAD in ChatGPT and Claude: editable parts, STEP/STL/drawings, in-chat 3D, automatic checks.
Text-to-CAD: build exact STEP or 3MF/STL on the server, each stated requirement measured.
Parametric 3D CAD for AI agents: build print-ready parts, check them, export STL, 3MF or STEP.
Use AI models for chat, image, and video generation from Claude Code and other MCP hosts.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables control of FreeCAD CAD software from Claude Desktop through natural language commands. Supports creating, editing, and managing 3D objects, executing Python code, and generating screenshots of designs.10MIT
- AlicenseAqualityDmaintenanceEnables to control FreeCAD from Claude Desktop through MCP, allowing CAD operations like creating and editing objects, taking screenshots, and executing Python code.111MIT
- AlicenseAqualityDmaintenanceEnables Claude Desktop to control FreeCAD for 3D CAD modeling, including creating, editing, and deleting objects, executing Python code, and running FEM analyses.14MIT
- AlicenseCqualityDmaintenanceAllows AI agents like Claude to create, edit, and query 3D models in FreeCAD through natural language.579MIT