Alpha software — this project is in early development. Expect rough edges, breaking changes, and the occasional FreeCAD crash from LLM-generated code. Use with caution and save your work frequently.
An AI-powered assistant workbench for FreeCAD that generates and executes Python code to create 3D models from natural language descriptions.
- Chat interface — dock widget with streaming LLM responses
- Plan / Act modes — review code before execution (Plan) or auto-execute (Act)
- Tool calling — 48 structured FreeCAD operations (Act mode) for safer, more reliable modeling
- Skills — reusable instruction sets the model invokes autonomously or via
/command(enclosure, gear, fastener holes, etc.) - Skill optimizer — automatically improve skill instructions via iterative test-evaluate-modify loop (
/optimize-skill) - Hooks — user-defined Python hooks for lifecycle events (block tools, modify input, log activity, convert file attachments)
- User extension tools — register your own Python functions as LLM-callable tools (
.pyor.FCMacro) - Vision routing — auto-detects LLM vision capability; non-vision models use MCP fallback via llm-vision-mcp, no-vision-path disables image controls
- File attachments — attach images, text files, and documents to chat messages (capture, attach, drag-drop, paste); extensible via
file_attachhook for binary formats (PDF, DOCX) or MCP servers for rich conversion - Image support — viewport screenshots and images sent as vision blocks; auto-detected binary format rejection (PDF, ZIP, Office docs)
- Thinking mode — enable LLM reasoning for complex multi-step tasks (Off / On / Extended)
- Context compacting — automatically summarizes older messages when approaching context limits
- Session resume — save and load chat sessions to continue work later
- 20 LLM providers — Anthropic, OpenAI, Ollama, Gemini, OpenRouter, Moonshot, DeepSeek, Qwen, Groq, Mistral, Together, Fireworks, xAI, Cohere, SambaNova, MiniMax, Llama, GitHub Models, HuggingFace, Zhipu, plus any OpenAI-compatible endpoint via Custom
- Context-aware — automatically includes document state (objects, properties, selection) in prompts
- Error self-correction — failed code is sent back to the LLM for automatic retry (up to 3 attempts)
- AGENTS.md support — project-level instructions with include directives and variable substitution
- Dark mode — chat widget automatically adapts to FreeCAD's light/dark theme (theme changes require FreeCAD restart)
- Zero external dependencies — uses only Python stdlib (
urllib,json,threading,ssl)
- FreeCAD 1.0+ (tested with 1.0.2 and 1.1.0)
- An LLM provider (local Ollama, or an API key for a cloud provider)
Clone or copy this repository into FreeCAD's Mod directory:
ln -s /path/to/freecad-ai ~/.local/share/FreeCAD/Mod/freecad-ailn -s /path/to/freecad-ai ~/Library/Application\ Support/FreeCAD/Mod/freecad-ai# Run as Administrator
New-Item -ItemType SymbolicLink -Path "$env:APPDATA\FreeCAD\Mod\freecad-ai" -Target "C:\path\to\freecad-ai"Or manually copy the repository into %APPDATA%\FreeCAD\Mod\freecad-ai.
Restart FreeCAD. The FreeCAD AI workbench will appear in the workbench selector.
- Switch to the FreeCAD AI workbench
- Click the gear icon to open settings
- Select your LLM provider and enter your API key (if needed)
- Click Test Connection to verify
Configuration is stored at ~/.config/FreeCAD/FreeCADAI/config.json.
| Provider | API Key Required | Notes |
|---|---|---|
| Ollama | No | Local models, default http://localhost:11434 |
| Anthropic | Yes | Claude models via native API |
| OpenAI | Yes | GPT models |
| Gemini | Yes | Google AI via OpenAI-compatible endpoint |
| OpenRouter | Yes | Multi-provider gateway |
| Moonshot | Yes | Kimi models (default params pre-configured) |
| DeepSeek | Yes | DeepSeek-V3 |
| Qwen | Yes | Alibaba DashScope (international) |
| Groq | Yes | Ultra-fast inference |
| Mistral | Yes | Mistral models |
| Together | Yes | Open model hosting |
| Fireworks | Yes | Fast inference |
| xAI | Yes | Grok models |
| Cohere | Yes | Command models |
| SambaNova | Yes | Fast inference |
| MiniMax | Yes | MiniMax models |
| Llama | Yes | Meta's official Llama API |
| GitHub | Yes (PAT) | GitHub Models marketplace |
| HuggingFace | Yes (hf_...) |
Serverless inference API |
| Zhipu | Yes | GLM models (z.ai international) |
| Custom | Varies | Any OpenAI-compatible endpoint |
The Model Parameters table in Settings lets you set arbitrary sampling parameters (temperature, top_p, top_k, etc.) that are sent with each API request. Parameters are saved per model name — when you switch models, the saved parameters for that model are loaded automatically.
Click Load Defaults to reset to the provider's recommended values. For most providers the only default is temperature: 0.3. Moonshot ships with pre-configured values required by Kimi-K2.5.
The "Strip thinking from conversation history" checkbox in Settings controls whether thinking/reasoning content is removed from previous turns before sending to the API. Some models (e.g. Gemma 3/4) reject thinking content in multi-turn conversations, while others (e.g. Kimi-K2.5) require it.
The checkbox is tristate: partially checked = auto-detect by model name (Gemma models strip automatically), checked = always strip, unchecked = never strip.
Type a request like "Create a box 50mm x 30mm x 20mm". The AI generates Python code and displays it for review. Click Execute to run it, or Copy to copy to clipboard.
Same workflow, but with two execution paths:
- Tool calling (default): The LLM invokes structured tools like
create_primitive,boolean_operation,fillet_edges, etc. These are pre-validated operations wrapped in undo transactions — safer and more reliable than raw code. - Code generation (fallback): When tools are disabled or the LLM generates code blocks instead, code executes with the same safety layers as before (static validation, subprocess sandbox, undo transactions).
Tool calling is enabled by default. Disable it by setting enable_tools: false in ~/.config/FreeCAD/FreeCADAI/config.json.
| Tool | Description |
|---|---|
create_primitive |
Box, Cylinder, Sphere, Cone, Torus |
create_body |
Create a PartDesign Body for parametric modeling |
create_sketch |
Sketch with lines, circles, arcs, rectangles + constraints (supports plane offset) |
pad_sketch |
Extrude a sketch |
pocket_sketch |
Cut a pocket from a sketch (auto-detects correct direction) |
revolve_sketch |
Revolve a sketch around an axis (vase, wheel, bottle) |
loft_sketches |
Loft between sketches for tapered/organic shapes |
sweep_sketch |
Sweep a profile along a spine path (pipe, tube) |
boolean_operation |
Fuse, Cut, or Common between two objects |
transform_object |
Move and/or rotate an object |
fillet_edges |
Round edges |
chamfer_edges |
Chamfer edges |
shell_object |
Hollow out a solid (PartDesign::Thickness) |
mirror_feature |
Mirror a PartDesign feature across a plane |
create_wedge |
Create a wedge/ramp shape |
scale_object |
Scale an object uniformly or per-axis |
section_object |
Cross-section through a plane or another object |
linear_pattern |
Repeat a feature in a line |
polar_pattern |
Repeat a feature in a circular pattern |
multi_transform |
Chain linear pattern + polar pattern + mirror in one feature |
create_inner_ridge |
Add a snap-fit ridge inside a rectangular hollow |
create_snap_tabs |
Add snap tabs on a lid lip (pairs with ridge) |
create_enclosure_lid |
Generate a snap-fit enclosure lid with correct dimensions |
measure |
Volume, area, bounding box, distance, edge listing |
describe_model |
Comprehensive geometry summary: dimensions, wall thickness, hollow/solid detection |
list_faces |
List all faces with names, labels, normals, areas (optional filter) |
list_edges |
List all edges with names, labels, lengths (optional filter) |
list_documents |
List all open documents with object counts and active indicator |
switch_document |
Switch the active FreeCAD document by name or label |
get_document_state |
Inspect current objects and properties |
create_variable_set |
Create a VarSet with named variables for parametric modeling |
create_spreadsheet |
Create a Spreadsheet with named variables for parametric modeling |
set_expression |
Bind object properties to expressions (parametric relationships) |
modify_property |
Change any object property (supports relative: +10%, *1.5) |
export_model |
Export to STL, STEP, or IGES |
execute_code |
Fallback: run arbitrary Python |
undo |
Undo last N operations, or undo until a named transaction |
redo |
Redo previously undone operations |
undo_history |
Show the undo/redo stack with named transactions |
capture_viewport |
Save a screenshot of the 3D viewport |
set_view |
Set camera orientation (front, top, isometric, etc.) |
zoom_object |
Zoom the viewport to a specific object |
use_skill |
Load a skill's instructions for complex tasks (enclosure, gear, etc.) |
create_assembly |
Create an Assembly with grounded base part |
add_assembly_joint |
Joint two parts (Fixed, Revolute, Cylindrical, Slider, Ball) |
add_part_to_assembly |
Add a part to an existing assembly |
select_geometry |
Interactive viewport picking for edges, faces, vertices |
Skills are reusable instruction sets stored in ~/.config/FreeCAD/FreeCADAI/skills/. Invoke them by typing /command in the chat.
Built-in skills:
| Command | Description |
|---|---|
/enclosure |
Parametric electronics enclosure with base, lid, screw posts |
/thread-insert |
Heat-set thread insert holes (M2–M5) with correct dimensions |
/gear |
Involute spur gear from module and tooth count |
/fastener-hole |
Clearance, counterbore, or countersink holes (ISO dimensions) |
/lattice |
Grid, honeycomb, or diagonal infill patterns |
/skill-creator |
Create new skills interactively from the chat |
Creating custom skills:
You can create skills manually (see below) or use /skill-creator in the chat to have the AI walk you through it.
~/.config/FreeCAD/FreeCADAI/skills/
my-skill/
SKILL.md # Instructions injected into the LLM prompt
handler.py # Optional: Python handler with execute(args) function
The SKILL.md file contains instructions for the LLM. When invoked, these are injected into the prompt along with any arguments you provide. For example:
/enclosure 100x60x40mm, 2mm walls, snap-fit lid
If a handler.py exists with an execute(args) function, it runs directly instead of prompting the LLM.
You can register your own Python functions as tools that the LLM can call. Place .py or .FCMacro files in ~/.config/FreeCAD/FreeCADAI/tools/. Functions with type hints are automatically discovered and registered:
import math
def bolt_circle(diameter: float, count: int = 8, bolt_size: float = 6.5) -> str:
"""Create a bolt hole circle pattern on the XY plane."""
import Part
import FreeCAD as App
# ... create geometry ...
return f"Created {count} bolt holes on {diameter}mm PCD"Requirements:
- Public functions (no
_prefix) with type-hinted parameters - Supported types:
float,int,str,bool - Return a
str(success message) ordictwithoutput/datakeys - Docstring first line becomes the tool description
User tools are prefixed with user_ and available to the LLM, skills, and MCP server. Manage them in Settings → User Tools (Add, Remove, Reload). Enable "Scan FreeCAD macro directory" to also pick up compatible macros from FreeCAD's macro folder.
Enable LLM reasoning in Settings (gear icon). Three levels:
| Level | Description |
|---|---|
| Off | Standard responses (default) |
| On | LLM shows reasoning before responding |
| Extended | Longer reasoning budget for complex tasks |
Thinking is displayed dimmed in the chat. Provider support varies — Anthropic Claude, OpenAI o-series, and some Ollama models (qwen3) support thinking. Models that don't support it will silently ignore the setting.
Conversations are auto-saved after each LLM response. Click the Load button in the chat footer to resume a previous session from the last 20 saved conversations.
Attach files to chat messages via the Attach button, drag-and-drop, or paste:
| File type | Handling |
|---|---|
| Images (PNG, JPG, etc.) | Sent as vision blocks (requires vision-capable model or MCP fallback) |
| Text files (TXT, MD, CSV, PY, JSON, etc.) | Read as text and included in the message |
| Binary files (PDF, DOCX, XLSX, etc.) | Converted via file_attach hook or MCP server |
Why no built-in PDF/DOCX support? FreeCAD AI has a strict zero-external-dependencies policy — it uses only Python stdlib and runs inside FreeCAD's bundled Python. PDF/DOCX parsing requires external C libraries that cannot be bundled portably. Instead, two extensibility paths are provided:
- Hooks — lightweight, user-installed shell commands. Create a
file_attachhook that calls CLI tools likepdftotextorpandoc. Seedocs/hooks/file-attach-example/for a ready-to-use example. Text-only output. - MCP servers — for rich conversion with images and structure preservation. Install a server like markdownify-mcp in Settings → MCP Servers.
To install the example PDF hook:
cp -r docs/hooks/file-attach-example/ ~/.config/FreeCAD/FreeCADAI/hooks/file-attach/
# Requires: sudo apt install poppler-utils (provides pdftotext)Binary files are detected by magic bytes (PDF, ZIP/Office, PNG, JPEG, ELF, etc.) and null-byte content scanning.
Place an AGENTS.md or FREECAD_AI.md file next to your .FCStd file to provide project-specific instructions:
# AGENTS.md
This project uses metric units (mm).
All parts should have 1mm fillets on external edges.
Use PartDesign workflow (Body -> Sketch -> Pad), not Part primitives.Search order: document directory → parent directories (up to 3 levels) → ~/.config/FreeCAD/FreeCADAI/AGENTS.md
Include directives — split instructions across files:
<!-- include: materials.md -->
<!-- include: conventions.md -->Variable substitution — use live document values:
Document: {{document_name}}
Objects: {{object_count}}
Active body: {{active_body}}The UI supports internationalization via Qt's .ts/.qm translation system. Currently available:
| Language | Status |
|---|---|
| English | Default (built-in) |
| German | Complete |
To add a new language, copy translations/freecad_ai_de.ts to freecad_ai_<lang>.ts, translate the strings (or use Qt Linguist), and compile with lrelease.
To re-extract strings after code changes:
cd translations && bash update_translations.shfreecad-ai/
├── Init.py # Non-GUI init
├── InitGui.py # Workbench registration + commands
├── package.xml # FreeCAD addon metadata
├── freecad_ai/
│ ├── config.py # Settings (provider, API key, mode, tools, thinking)
│ ├── i18n.py # Internationalization helpers
│ ├── paths.py # Path utilities
│ ├── llm/
│ │ ├── client.py # HTTP client with SSE streaming + tool calling
│ │ └── providers.py # Provider registry
│ ├── tools/
│ │ ├── registry.py # Tool abstractions + registry
│ │ ├── freecad_tools.py # 39 FreeCAD tool handlers
│ │ └── setup.py # Default registry factory
│ ├── ui/
│ │ ├── compat.py # PySide2/PySide6 shim
│ │ ├── chat_widget.py # Chat dock + agentic tool loop
│ │ ├── message_view.py # Message + tool call rendering
│ │ ├── code_review_dialog.py
│ │ └── settings_dialog.py
│ ├── core/
│ │ ├── active_document.py # GUI-aligned active document resolution
│ │ ├── executor.py # Code execution with safety layers
│ │ ├── context.py # Document state inspector
│ │ ├── system_prompt.py # System prompt builder
│ │ └── conversation.py # Conversation history + compacting + save/load
│ ├── hooks/
│ │ ├── __init__.py # Hooks package
│ │ └── registry.py # Hook lifecycle event dispatch
│ └── extensions/
│ ├── agents_md.py # AGENTS.md loader (multi-location, includes, vars)
│ ├── skills.py # Skills registry + execution
│ └── user_tools.py # User extension tools (discover, validate, register)
├── translations/
│ ├── freecad_ai_de.ts # German translation source
│ └── update_translations.sh # Re-extract strings with pylupdate5
├── skills/ # Built-in skill definitions
│ ├── enclosure/SKILL.md
│ ├── gear/SKILL.md
│ ├── fastener-hole/SKILL.md
│ ├── thread-insert/SKILL.md
│ ├── lattice/SKILL.md
│ └── skill-creator/SKILL.md
├── hooks/ # Built-in hook examples
│ └── log-tool-calls/
└── resources/
└── icons/
└── freecad_ai.svg
Contributions are welcome! See CONTRIBUTING.md for how to set up a dev environment, submit pull requests, and create new skills or providers.
- Code: LGPL-2.1 — see LICENSE-CODE
- Icons: CC0-1.0 (public domain) — see LICENSE-ICON