mcpField
F
freecad

freecad

github.com/jablonkaiデザイン・UI462 回インストール今日更新
リポジトリ

freecad について

Pythonスクリプトを実行中のFreeCADセッションに送り、モデルを作成・検査するスキルです。測定、スクリーンショット、エクスポートを含む反復作業に対応し、ヘッドレス実行方法も説明します。

主な機能

  1. 01稼働中のFreeCADセッションでPythonを実行
  2. 02パラメトリックモデルとソリッドの作成
  3. 03形状を検査して測定値を出力
  4. 04ビューポート画像の取得
  5. 05STEP、STL、FCStd形式でエクスポート

活用シーン

  1. 01Pythonスクリプトから部品を生成
  2. 02モデルの寸法とソリッドの妥当性を確認
  3. 03ビューポートの画像でモデルを確認
  4. 04編集や製造向けにモデルをエクスポート

MCP 対応

ローカルのFreeCAD Bridgeは、付属のシェルスクリプトからPythonスクリプトを受け取り、稼働中のGUIセッションで実行します。このコード実行サーバーはlocalhostにバインドされ、認証はありません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jablonkai/skills --skill freecad

インストール後、Claude Code で/freecadを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api-reference.md
scripts/
example-bracket.py
freecad-bridge.py
freecad-send.sh

詳細

FreeCAD の操作

FreeCAD(/Applications/FreeCAD.app)では、FreeCAD / FreeCADGui / Part / Sketcher / PartDesign / Draft / Mesh / TechDraw モジュールを使用して、Python でスクリプトを実行できます。FreeCAD Bridge を介して操作します。これは、実行中の FreeCAD GUI 内で動作する小さなスクリプトで、127.0.0.1:8735 に POST されたビルドスクリプトを実行します。スクリプトは GUI スレッド上のライブセッションで実行されるため、構築に合わせてビューポートが更新され、Gui.* / saveImage をそのまま使用できます。以下の内容はすべて FreeCAD 1.1.3 で動作確認済みです。

  • scripts/freecad-bridge.py — FreeCAD 内で実行するブリッジです。
  • scripts/freecad-send.sh — .py ファイル(または -c 'inline code')をブリッジに送信し、キャプチャした出力を表示します。--ping で稼働状況を確認できます。
  • scripts/example-bracket.py — ビルド→チェック→計測→エクスポート→スクリーンショットまでを行う完全なスクリプトです。コピーして使用できます。
  • references/api-reference.md — バージョン確認済みの Python API の要約です(ドキュメント、Part、Sketcher のアタッチメント、PartDesign フィーチャー、Draft、Mesh、TechDraw の図面、インポート/エクスポート、GUI)。以下のチートシートの範囲を超えて何かを書く前に、必ずお読みください。PartDesign / TechDraw のプロパティには、推測と異なるものがいくつかあります。

制御ループ

  1. ブリッジが起動していることを確認します:bash scripts/freecad-send.sh --ping。ブリッジが応答していれば、{"ok": true, "bridge": "freecad", "version": "1.1.3"} を返します。応答がない場合はブリッジを起動する必要があります。ブリッジはローカルのコード実行サーバーなので(セキュリティを参照)、起動する前にそのことを伝えてください。
    • FreeCAD が起動していない場合(pgrep -xi freecad で何も見つからない場合):ブリッジを起動スクリプトとして指定して起動します。FreeCAD はコマンドラインで渡された .py を実行します。 open -a FreeCAD --args "$PWD/scripts/freecad-bridge.py"(絶対パス)を実行し、GUI の起動中、最大約 60 秒間 --ping をポーリングします。
    • FreeCAD がすでに起動している場合:open は起動中のアプリに引数を渡さないため、ユーザーに scripts/freecad-bridge.py を Python コンソール(View ▸ Panels ▸ Python console)に貼り付けるか、ユーザーマクロフォルダー(ダイアログに表示されます)にコピーしてから Macro ▸ Macros… ▸ Execute で実行するよう依頼します。
  2. ビルドスクリプトを作成し、スクラッチパッドに保存します。API を使った Python スクリプトを作成してください(references/api-reference.md と scripts/example-bracket.py を参考にします)。
  3. 送信します:OUT=/path/to/outdir bash scripts/freecad-send.sh /path/build.py。ブリッジはライブセッションでスクリプトを実行し、スクリプトの キャプチャされた標準出力を返します。Python 例外が発生すると、トレースバックを返し、送信側はゼロ以外の終了コードで終了します。 重いビルドには FREECAD_SEND_TIMEOUT=600(秒)を指定します。スクリプトの名前空間には App/FreeCAD/Gui があらかじめインポートされ、OUT は $OUT からグローバル変数と os.environ["OUT"] の両方に設定されます。
  4. 確認してから計測します:フィーチャーの計算に失敗しても、例外は発生しません。以下の再計算チェックを参照してください。次に、metrics.json(バウンディングボックス、体積、isValid()、ソリッド数)を書き出して読み込みます。画像を確認する場合は、同じスクリプトで saveImage(...) を実行して PNG を読み込みます。納品物として STEP/STL/FCStd を $OUT にエクスポートします。
  5. 反復します:出力/PNG を確認し、スクリプトを修正して再送信します。スクリプトは再実行可能にしてください。App.newDocument(...) の前に、同じ名前のドキュメントを閉じます(FreeCAD は重複した名前を黙って thing1 に変更し、古いドキュメントがセッション内に残って煩雑になります)。自分が作成していないドキュメントは閉じないでください。セッションはユーザーのものです。

送信に失敗すると、ブリッジがキャプチャした Python トレースバックが出力されます。ブリッジは送信後も起動したままです。応答しなくなった場合は、ユーザーにブリッジスクリプトを再実行するよう依頼してください。

GUI は不要ですか? スクリーンショットや TechDraw PDF を作成せず、ビルド→計測→エクスポートのみを行う場合は、/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd build.py で同じスクリプトをヘッドレス実行できます。ブリッジを起動できないユーザーに便利です。この場合、Gui は未定義です(globals().get("Gui") で確認してください)。標準出力には FreeCAD 自身のログが混在し、捕捉されない例外が発生しても終了コードは 0 のままです。失敗を検出できるよう、スクリプトを try/except で囲み、sys.exit(1) を実行してください。

モデリングのチートシート(実証済みのパターン)

単位は mm と 度です。App は FreeCAD と同じです(どちらもブリッジによってあらかじめインポートされています)。計測の前に必ず recompute() を実行してください。

プリミティブとブーリアン演算によるソリッド(検証済み:穴を開けたプレート)
import os, json, Part, Mesh
OUT = globals().get("OUT") or os.environ["OUT"]
if "bracket" in App.listDocuments():                # re-runnable: drop the previous build
    App.closeDocument("bracket")
doc = App.newDocument("bracket")
box = doc.addObject("Part::Box", "Plate"); box.Length, box.Width, box.Height = 40, 20, 10
hole = doc.addObject("Part::Cylinder", "Hole"); hole.Radius, hole.Height = 5, 10
hole.Placement.Base = App.Vector(20, 10, 0)          # centre of the plate
cut = doc.addObject("Part::Cut", "Bracket"); cut.Base, cut.Tool = box, hole
doc.recompute()                                      # REQUIRED before measuring
確認、計測、スクリーンショット(ライブフィードバック)

doc.recompute() は例外を発生させません。フィーチャーが壊れていても(スケッチが不正、ポケットがソリッドに届かない、フィレットが大きすぎるなど)、Invalid としてマークされるだけで、.Shape は古いままか空になります。そのため、メトリクスには誤ったパーツの情報が気づかれないまま記録される可能性があります。まず確認してください。

bad = [(o.Name, o.State) for o in doc.Objects if "Invalid" in o.State or "Error" in o.State]
if bad:
    raise RuntimeError("recompute failed: %s" % bad)   # comes back as the send's traceback

shp = cut.Shape; bb = shp.BoundBox
m = {"bbox": [round(bb.XLength,3), round(bb.YLength,3), round(bb.ZLength,3)],
     "volume": round(shp.Volume,3), "valid": shp.isValid(), "solids": len(shp.Solids)}
json.dump(m, open(OUT+"/metrics.json","w"), indent=2)
print("METRICS", json.dumps(m))                      # returned to the sender

view = Gui.getDocument(doc.Name).activeView()        # live GUI — snapshot inline
view.setAnimationEnabled(False)                      # else saveImage catches the camera mid-turn
view.viewIsometric(); view.fitAll()
view.saveImage(OUT+"/check.png", 900, 675, "White")

メトリクスをユーザーの要望(たとえば手計算した想定体積)と比較してください。これにより、正しく有効な単一ソリッドの形状だけでは見つけられない、穴の位置違いやパッドの方向違いを検出できます。

エクスポート(形式に応じてモジュールを選択します。リファレンスの一覧を参照)
Part.export([cut], OUT+"/bracket.step")   # B-rep: .step/.stp .iges/.igs .brep
Mesh.export([cut], OUT+"/bracket.stl")    # mesh: .stl .obj .ply .3mf .amf .off
doc.saveAs(OUT+"/bracket.FCStd")          # native, re-editable
パラメトリックな手順(Sketcher → PartDesign、検証済み:長方形 → パッド)
import Part, Sketcher
body = doc.addObject("PartDesign::Body", "Body")
sk = body.newObject("Sketcher::SketchObject", "Sketch")   # unattached = lies on XY
V = App.Vector
for a, b in [((0,0),(30,0)), ((30,0),(30,20)), ((30,20),(0,20)), ((0,20),(0,0))]:
    sk.addGeometry(Part.LineSegment(V(*a,0), V(*b,0)), False)
for i in range(4):
    sk.addConstraint(Sketcher.Constraint("Coincident", i, 2, (i+1) % 4, 1))
doc.recompute()
pad = body.newObject("PartDesign::Pad", "Pad"); pad.Profile = sk; pad.Length = 10
doc.recompute()

後から寸法を編集する場合は、寸法拘束( DistanceX、DistanceY、Diameter、…)で寸法を指定し、sk.FullyConstrained が True であることを確認します。別の平面や面にスケッチするには、sk.AttachmentSupport と sk.MapMode = "FlatFace" を設定します。貫通ポケットは pocket.Type = "ThroughAll" です。Linear/PolarPattern を追加した後は、body.Tip = pattern を自分で設定します。これらはすべてリファレンスに記載されています。回転軸、フィレット、そして Edge1 を当てずっぽうで指定するのではなく、形状を基準にエッジを選択する方法も説明しています。

フィーチャーの種類、拘束の種類、Draft/Mesh の呼び出し、TechDraw の図面、エクスポートの完全な対応表については、references/api-reference.md を参照してください。

検証

  • 返された stdout は即時に確認できるシグナルです。スクリプト内の print(...) の出力は、送信元を通じてそのまま返されます。
  • Invalid 状態のチェックによって、無言で発生する再計算の失敗がトレースバックとして表示されます。
  • metrics.json(bbox / volume / isValid() / solid count)は構造化されたチェックです。ファイルに書き込み、読み込むことで、目視に頼らず形状を確認できます。
  • インラインスクリーンショット:ブリッジは実行中の GUI で動作するため、同じスクリプト内で saveImage(...) を実行すると、読み込めるビューポート PNG が生成されます。別途レンダリングする必要はありません。TechDraw の PDF の場合は、qlmanage -t -s 1000 -o <dir> page.pdf を実行すると、読み込める PNG サムネイルがレンダリングされます。
  • 引き渡し:エクスポートした .stl / .step は、ユーザーがすでに使用している任意のスライサー/CAD ツールで開けます。

注意点(この環境で苦労して分かったこと)

  • ブリッジは実行中のセッション内で動作するため、状態は送信間で保持されます。開いているドキュメント、インポート済みのアセット、ビュー設定はすべて残ります。再送時にクリーンな状態で始められるよう、ドキュメントは自分で名前を指定して閉じてから作成してください。
  • newDocument は名前が重複していても失敗しません。黙って name1 を作成するため、後で App.getDocument("name") を呼び出すと、古いビルドが返されます。
  • 機能が失敗しても例外は発生しません。recompute() は通常どおり戻り、オブジェクトは Invalid のままになります。測定値を信頼する前に o.State を確認してください。
  • ここでは Gui が有効です。saveImage、viewIsometric、fitAll はすべて動作します。(ヘッドレスの freecadcmd では定義されていません。)
  • 空白や角度のずれたスクリーンショットは、ビューのアニメーションが原因です。viewIsometric() はカメラのアニメーションを開始しますが、saveImage はその完了を待ちません。最初に view.setAnimationEnabled(False) を実行してください。渡す前に必ず PNG を読み込んで確認してください。
  • print() とキャプチャされた標準出力はレスポンスに返されます。App.Console.PrintMessage は FreeCAD のレポートビューに出力され、送信元には返されません。返したい内容には print() を使用してください。
  • 測定またはエクスポートの前には毎回 recompute() を実行してください。実行するまでは、パラメトリック属性は .Shape に反映されません。
  • TechDraw ビューはバックグラウンドスレッドで計算されます。recompute() 直後はビューにエッジがなく、Edge0 を参照する寸法は 0.0 になります。寸法を追加する前に view.getVisibleEdges() を待ってください(参考例にパターンがあります)。ビューの Scale は ScaleType = "Custom" でない限り無視され、エッジ長はスケール適用後の値で返されます。
  • ページを GUI で開く(page.ViewObject.doubleClicked())までは、TechDraw のエクスポートで枠が省略されます。また、既定の A4 テンプレートは空白のシートです。ISO テンプレートを使用してください(参考例)。
  • パターンを作成しても、Body の Tip にはなりません。body.Tip = pattern を設定するまで、body.Shape にはパターン適用前のソリッドが表示されます。
  • 推測したエッジ番号や面番号では問題が発生します。Edge1 は継ぎ目であることが多いため、曲線の種類、半径、位置で選択してください(参考例にスニペットがあります)。
  • PySide バインディング:FreeCAD 1.1 には PySide6(Qt6)が同梱されています。ブリッジは PySide2 へのフォールバック付きでインポートします。Qt を直接スクリプトで扱う場合も、同じ方法でインポートしてください。
  • ブーリアン演算では入力オブジェクトがツリーに残ります。Part::Cut/Fuse は Base/Tool を自動的に非表示にしますが、Part::Fillet/Chamfer はベースを表示したままにするため、スクリーンショットに映り込みます。入力オブジェクトは自分で非表示にし、結果フィーチャーだけをエクスポートしてください。
  • STL の解像度:GUI では Mesh.export は粗い表示メッシュを再利用します。印刷するものは、MeshPart.meshFromShape(...)(参考例)で明示的にメッシュ化し、ファセット数を報告してください。
  • パラメトリック作業を保持して後から再編集できるよう、スクリプトからネイティブの .FCStd を保存してください 既存ファイルに上書き保存すると、その隣にタイムスタンプ付きの *.FCBak が残ります。フォルダーが納品物である場合は、$OUT から削除してください。

セキュリティ

ブリッジを実行することは、ユーザーのマシン上でコード実行サーバーを動かすことを意味します。起動を依頼する前に、そのことを伝えてください。

  • ブリッジは 127.0.0.1:8735(FREECAD_BRIDGE_PORT)にバインドし、/run に POST された Python を実行中のセッション内で実行します。ユーザーの権限で、ユーザーが開いているドキュメントを操作します。リクエストに認証はありません。ローカルのすべてのプロセスと、共有マシン上の他のすべてのユーザーが、ブリッジ経由で FreeCAD を操作できます。
  • ウェブページからはアクセスできません。Origin ヘッダーまたはクロスサイトの Sec-Fetch-Site を含むリクエストは 403 で拒否されるため、ユーザーのブラウザーで開いたページからブリッジにはアクセスできません。このチェックだけが保護手段であり、トークンはありません。
  • ブリッジを起動するまで、リスナーは動作しません。 3 つの起動方法(スクリプトを引数に指定して起動、Python コンソールに貼り付け、Macro ▸ Execute)はすべてセッション単位です。Macro フォルダーへのコピーは貼り付けの手間を省くだけで、自動起動はしません。自動読み込みマクロにすると FreeCAD の起動のたびにリスニングを開始するため、そのことを伝えずに提案しないでください。
  • ブリッジを停止するには、FreeCAD を終了してください。 リモートからのシャットダウン方法はありません。ポートはプロセスの終了とともに解放されます。

コメント