Skip to main content
Glama
ymzkd

FEMNet MCP Apps Server

by ymzkd

FEMNet MCP Apps サーバー

FEMNet(C++ 製 FEM ライブラリの Python バインディング)を解析エンジンとして、 Claude Desktop のチャット画面内に vtk.js の 3D 構造ビューアを開く MCP Apps サーバー。

Agent はモデル定義・解析実行・表示切り替えをツール呼び出しだけで行う。 描画コードを毎回生成させないので、トークン消費と生成エラーを避けられる。

Claude Desktop ──stdio── server.py ── fem_core.py ── femnet (.pyd)
       │                     │
       │ ui://fem/app.html   └── fem_view.py ──┐ 表示メッシュを
       │  (iframe)                             │ バイナリで配信
       └──────── fetch http://127.0.0.1:8781/view ┘

メッシュは数 MB になるため MCP の JSON-RPC には載せず、 UI(iframe)からローカル HTTP へ直接 fetch させる。 HTTP が塞がれた場合は fem_view_chunk ツールで MCP チャネル経由の分割取得にフォールバックする。

必要環境

項目

条件

OS

Windows(FEMNet のビルド成果物が Windows 向け)

Python

3.12 固定_femnet.pydpython312.dll にリンク済み)

パッケージ

mcp>=2.0.0, numpy>=2.0pydanticmcp 経由)

外部依存

FEMNet のビルド済み _femnet.pyd、Intel oneAPI MKL

ホスト

Claude Desktop(ローカル stdio 接続)

.venvuv が管理する。uv.lock をコミット済み。

Related MCP server: freecad-mcp

セットアップ

1. 依存の取得

uv sync

2. FEMNet の場所を教える

FEMNet のビルド成果物はこのリポジトリの外にあり、置き場所はマシンごとに違う。 そのためパスはコードに書かず、femnet_env.py が次の順で解決する。

  1. 環境変数 FEMNET_PYTHON_DIR

  2. femnet_local.py(リポジトリ管理外)に書いた FEMNET_PYTHON_DIR

どちらも無ければ起動時に明示的なエラーになる。手軽なのは 2 の方法。

# femnet_local.py(.gitignore 済み。自分の環境のパスを書く)
FEMNET_PYTHON_DIR = r"C:\path\to\StructureMM\lib\FEMNet\python"

指定するのは femnet/_femnet.pyd を含む python/ ディレクトリ。

.pyd は MKL の DLL に依存する。Python 3.8 以降は PATH を DLL 検索に使わないため、 os.add_dll_directory() での明示登録が必須(femnet_env.setup() が行う)。 oneAPI の既定パスを見にいくので通常は設定不要だが、別の場所に入れている場合は 環境変数 FEMNET_MKL_DLL_DIRS; 区切り)で上書きできる。

3. Claude Desktop に登録

claude_desktop_config.json:

{
  "mcpServers": {
    "femnet": {
      "command": "C:\\Users\\<user>\\.local\\bin\\uv.exe",
      "args": ["run", "--directory", "C:\\path\\to\\MCPAppTest", "server.py"],
      "env": { "FEMNET_PYTHON_DIR": "C:\\path\\to\\FEMNet\\python" }
    }
  }
}

command は絶対パスにする(GUI 起動の Claude Desktop は PATH 解決が不安定)。 設定変更後はトレイから完全終了して再起動する。

ツール

ツール

UI

役割

build_model

節点・材料・断面・要素・支持・荷重を JSON で一括定義し、形状を表示

run_static

線形静解析 → 変形図+変位コンター

show_view

解析済み結果の表示切り替え(変形図 / 板応力コンター / 部材断面力図)

run_modal

固有値解析 → モード形状アニメーション

get_results

節点変位・反力・部材応力を数値で返す(Agent の考察用)

fem_view_chunk

UI 専用。表示メッシュを base64 で分割取得するフォールバック

show_view の指定:

  • view="deformation"component = mag / ux / uy / uz

  • view="beam_diagram"component = N / Qy / Qz / Mx / My / Mz(M 図なら Mz、Q 図なら Qy

  • view="plate_stress"component = Mx / My / Mxy / Qx / Qy / Nx / Ny / Qxy

LLM が書きがちな別名(moment, 曲げ, axial など)は server.py 側で正規名に吸収している。

モデル定義

単位系は N-mm 系

単位

例(鋼)

長さ・座標

mm

N

モーメント

N·mm

ヤング係数 e

N/mm²

205000

密度 density

kg/m³

7850

節点付加質量 mass_kg

kg

分布荷重 w0 / w1

N/mm

要素タイプ(ElementSpec.type):

type

節点数

必須

beam

2

section

truss

2

section

tri_plate

3

thickness

quad_plate

4

thickness

支持は preset="fixed" / "pinned" か、uxrz の個別フラグ(True = 拘束)。 固有値解析には質量が要る(材料の densitynode_masses)。

スキーマの全体は fem_core.pyModelSpec を参照。

ファイル構成

ファイル

役割

server.py

MCP サーバー本体。ツール定義、UI リソース登録、起動

fem_core.py

モデル定義スキーマ(pydantic)と FEMNet 呼び出し・解析実行

fem_view.py

解析結果 → 表示メッシュ変換、バイナリ化、ローカル HTTP 配信

femnet_env.py

_femnet.pyd と MKL DLL の検索パス解決

femnet_local.py

環境固有のパス設定。リポジトリには含めない(.gitignore 済み)

ui/fem.html

ビューア UI。起動時に JS バンドルを差し込んで単一リソースとして配信

ui/vtk.js

vtk.js のツリーシェイク済みバンドル(693 KB)

ui/ext-apps.js

@modelcontextprotocol/ext-apps のバンドル(383 KB)

mesh.py, ui/mesh.html, ui/app.html

経路検証に使った初期プロトタイプ。現在のサーバーからは参照していない

ui/*.js はビルド成果物だがリポジトリに含める。これによりサーバー実行時は Python / uv だけで完結し、Node はバンドル生成時のみ必要になる。

npm install --no-save @kitware/vtk.js
npx esbuild entry.js --bundle --format=iife --global-name=VTK --minify \
  --define:process.env.NODE_ENV='"production"' --loader:.glsl=text --outfile=ui/vtk.js

実装上の制約メモ

実機でしか判明しなかった、変更時に踏み抜きやすい点。

  • structuredContent は UI に届かない。 Claude Desktop は ui/notifications/tool-resultstructuredContent を落とし、content の text(JSON 文字列)だけを渡す。UI 側は必ず両方を見る。

  • ローカル HTTP への fetch は CORS だけでは通らない。 iframe は公開オリジン(https)で動くため http://127.0.0.1 への要求は Private Network Access の対象になる。 Access-Control-Allow-Private-Network: true と OPTIONS への応答が要る。

  • CSP の宣言が必須で、ポート確定との順序制約がある。 localhost も _meta.ui.cspconnect_domains に書く。ポート番号を含むので、HTTP サーバーを起動して ポートを確定させてから add_html_resource(csp=...) を呼ぶ。

  • バイナリのヘッダ長は 4 バイト境界に揃える。 new Float32Array(buffer, offset, ...) は offset が 4 の倍数でないと RangeError になる。JSON ヘッダは空白でパディングする。

  • <script src="..."> は使えない。 iframe の中には取得元サーバーが無い。 load_ui() が HTML 内のプレースホルダにバンドルを差し込む。

  • 支持条件は要素生成より前に適用する。 このビルドの FEMNet は要素生成後の Fix 代入で 板要素の解が壊れる(単純支持板のたわみが 1/10 になるのを実測)。

  • MassData.Mass は質量ではなく重量 [N]。 材料 density も内部では単位体積重量 [N/mm³] として扱われる(質量行列で g で割られる)。fem_core.py が kg / kg·m⁻³ から換算している。

  • 取得経路は二重化してある。 UI は ontoolresult(push)で data_url を受け取り、 ローカル HTTP → 失敗時は fem_view_chunk の MCP 分割取得、の順に落とす。 CSP 違反は securitypolicyviolation で拾って画面に出す。

表示メッシュのバイナリ形式(FEM4)

"FEM4" | uint32 ヘッダ長 | JSON ヘッダ(4 バイト境界) |
verts f32×3V | tris u32×3T |
fields × (f32×3V)              … 変位ベクトル場(静解析=1本、モード=モード数)
plate_components × (f32×T)     … 三角形ごとの板応力(板由来でなければ NaN)
beam_values f32 × (B × components × stations)   … 部材断面力ダイアグラム用

スカラー(|u| や成分)は UI 側でベクトルから計算する。 変形倍率の変更は非変形座標と変位ベクトルを別に持つことで、再取得なしに反映される。

Related MCP Connectors

Related MCP Servers