{
  "markdown": "# mcp-fea\n\n[![ci](https://github.com/benchwire/mcp-fea/actions/workflows/ci.yml/badge.svg)](https://github.com/benchwire/mcp-fea/actions/workflows/ci.yml)\n[![validation](https://img.shields.io/badge/validation-receipts-blue)](docs/validation.md)\n[![license](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-2026--07--28-black)](https://modelcontextprotocol.io/specification/2026-07-28)\n\n**Real finite element analysis from a conversation.** An AI assistant sends\na STEP file and a plain-English question (\"does this arm survive a 6 g\nmaneuver with a 400 g motor?\"); this MCP server maps the words onto\ngeometry, **rejects bad setups deterministically before any compute is\nspent**, meshes and solves with CalculiX on Modal as an async MCP task, and\nreturns a verdict backed by numbers — peak von Mises stress, factor of\nsafety, deflection, natural frequencies — plus rendered stress plots and an\ninteractive 3D viewer. Every study ends with a non-skippable\nreaction-balance check; every benchmark on the\n[validation page](docs/validation.md) is regenerated by CI on every commit.\n\n![mode 1 animation](demo/assets/mode1.gif)\n\n*Measured on the live deployment (2026-07-31): cold start **3.8 s** ·\ncantilever study submit→completed **9.3 s** warm / **12.9 s** with a cold\nsolver container · **~$0.002 per study** at Modal's posted rates.*\n\n| Benchmark | Reference | Computed | Error |\n|---|---:|---:|---:|\n| Cantilever tip deflection (PL³/3EI) | 0.58055 mm | 0.58046 mm | 0.02% |\n| NAFEMS LE10 σ_yy @ D | −5.38 MPa | −5.456 MPa | 1.41% |\n| Plate-with-hole peak (Heywood Kt) | 62.8 MPa | 61.92 MPa | 1.40% |\n| Cantilever f₁ (Euler–Bernoulli) | 816.0 Hz | 815.4 Hz | 0.08% |\n\nFull table, mesh stats, and an explicit limitations section:\n**[docs/validation.md](docs/validation.md)** — regenerated by CI, never\nhand-edited.\n\nProtocol: **MCP 2026-07-28** (stateless core) with the\n`io.modelcontextprotocol/tasks` extension implemented per SEP-2663 (the\nofficial Python SDK v2 shipped without it — this repo also contains the\n[client-side extension](src/client/tasks_ext.py) that teaches the official\nSDK to consume task-augmented servers) and an MCP Apps (SEP-1865) 3D\nviewer. See [docs/decisions.md](docs/decisions.md) for every engineering\ncall and why.\n\n## Deploy (3 commands)\n\n```bash\nuv sync\nuv run modal secret create mcp-fea-token MCP_FEA_TOKEN=$(openssl rand -hex 24)\nuv run modal deploy -m src.server.modal_app\n```\n\nModal prints the endpoint URL (`https://<workspace>--mcp-fea-mcp-endpoint.modal.run`).\nConnect any 2026-07-28 MCP client with `Authorization: Bearer <your token>`.\nFirst-time Modal users: `uv run modal token new` once. Then verify the live\ndeployment end to end:\n\n```bash\nMCP_FEA_LIVE_URL=https://... MCP_FEA_TOKEN=... make verify-live\n```\n\n## Tools\n\n| Tool | Sync/async | What it does |\n|---|---|---|\n| `inspect_geometry` | sync | STEP → face table (face_id, type, centroid, normal/axis, area, bbox) + part bbox/volume. Call first: it's how English maps to face ids. |\n| `list_materials` | sync | Built-in library: Al 6061-T6, Al 7075-T6, Steel 4140, Stainless 304, Ti-6Al-4V, ABS, PLA, Nylon 12, PC (E, ν, yield, density). |\n| `submit_fea_study` | **task** | Linear static: validate → mesh (C3D10, curvature-adaptive, local thin-wall refine) → CalculiX → verdict/stress/FoS + 3 stress PNGs. |\n| `submit_modal_study` | **task** | Natural frequencies + mode shapes (`*FREQUENCY`), first N modes (default 6), one exaggerated-deformation PNG per mode. Requires constraints and density. |\n| `get_report` | sync | Full payload for a completed task; `include_mesh_data: true` adds the surface-mesh payload the 3D viewer app fetches. |\n\nTask lifecycle: `working` (statusMessage walks `validating → meshing →\nsolving → postprocessing`) → `completed | failed | cancelled`;\n`tasks/cancel` kills the running container. Tasks are retained 24 h.\n\n## Units policy\n\n**One conversion boundary, at ingest.** Internally everything is consistent\nmm–N–MPa(–tonne): lengths mm, forces N, stress/moduli MPa, density tonne/mm³\n(kg/m³ × 1e-12 — done once, in `Material.density_tonne_mm3`), which makes\nmodal frequencies come out directly in Hz. STEP coordinates are interpreted\nin the *declared* units (`mm|m|in`); gmsh's own unit conversion is disabled\nso the declared unit is authoritative — and sanity-checked (a 100 mm part\ndeclared \"m\" gets flagged).\n\n## Validation codes\n\nEvery rejection returns `{code, message, suggested_fix}`.\n\n| Code | Meaning | Stage |\n|---|---|---|\n| `GEOMETRY_REF_INVALID` | STEP missing/unreadable/over 25 MB, bad units field | parse |\n| `GEOMETRY_KERNEL_CRASH` / `GEOMETRY_TIMEOUT` | CAD kernel crashed / hung on the file (isolated subprocess; endpoint unaffected) | inspect |\n| `MATERIAL_UNKNOWN` / `MATERIAL_UNPHYSICAL` | Not in library / E ∉ [0.01, 1500] GPa, ν ∉ (0, 0.5), yield ≤ 0, density ∉ [10, 25000] kg/m³, missing density for modal | parse |\n| `CONSTRAINT_INVALID` / `CONSTRAINT_CONFLICT` | Bad type; node pinned in two local frames | parse/deck |\n| `LOAD_INVALID` / `LOAD_MISSING` / `MODAL_INVALID` | Bad type/units/direction; zero loads; bad n_modes | parse |\n| `GEOM_NO_SOLID` / `GEOM_MULTIPLE_SOLIDS` | Not a single solid body (assemblies = v2) | pre-mesh |\n| `FACE_ID_INVALID` | Face id not on this part | pre-mesh |\n| `RIGID_BODY_MOTION` | Constraints leave free motion (named, e.g. \"rotation about the hole axis\") — SVD rank check | pre-mesh |\n| `LOAD_ON_FIXED_FACE` | Load applied to a fully fixed face | pre-mesh |\n| `UNITS_SUSPECT` *(warning)* | Part > 5 m or < 0.5 mm — declared units probably wrong | pre-mesh |\n| `LOAD_MAGNITUDE_EXTREME` / `_NEGLIGIBLE` *(warnings)* | σ ≈ F/A > 10× yield or < 1e-6× yield | pre-mesh |\n| `MESH_ELEMENT_CAP` | > 400k elements even after coarsening / local refine | mesh |\n| `MESH_QUALITY_JACOBIAN` / `MESH_QUALITY_ASPECT` / `MESH_THIN_WALLS` | Quality gates; thin walls get one local auto-refine first | mesh |\n| `MESH_TIMEOUT` | Meshing exceeded its wall budget (near-tangent slivers, helical threads) — the worker is killed from outside | mesh |\n| `SOLVER_SINGULAR` / `SOLVER_MESH_DEGENERATE` / `SOLVER_TIMEOUT` / `SOLVER_FAILED` | Mapped ccx failures (>150k elements auto-switch to the iterative solver) | solve |\n| `EQUILIBRIUM_FAIL` *(warning, untrusted result)* | ΣRF vs free-node applied loads off by > 0.5% | post |\n| `LOAD_INTO_SUPPORT` *(warning)* | > 5% of the load lands on constrained nodes (loaded face touches a fixed face) and is carried directly by the support | post |\n| `BEYOND_YIELD` / `LARGE_DEFLECTION` *(warnings)* | FoS < 1 (post-yield numbers flagged non-physical) / u > 5% of min dimension | post |\n\n## Abuse limits (all env-tunable)\n\n25 MB STEP cap · 40 MB request cap (413) · per-token rate limit\n(`MCP_FEA_RATE_LIMIT`/`_WINDOW_S`, 429) · concurrent-solve cap\n(`MCP_FEA_MAX_CONCURRENT`) · daily budget (`MCP_FEA_MAX_SPAWNS_PER_DAY`) ·\nkill switch (`MCP_FEA_DISABLE_SPAWN=1`). CAD parsing never runs in the\nendpoint process (see [SECURITY.md](SECURITY.md)).\n\n## Local development\n\n```bash\nuv sync\ncurl -Ls https://micro.mamba.pm/api/micromamba/osx-arm64/latest | tar -xj bin/micromamba\n./bin/micromamba create -y -p .tools/ccx-env -c conda-forge calculix=2.23\nmake test        # fast suite (protocol, benchmarks, validation, hardening)\nmake torture     # 21 real-world STEP files through the endpoint\nmake validation-page\nuv run uvicorn --factory src.server.app:create_local_app --port 8000  # local server\n```\n\n## Repo map\n\n`src/pipeline/` geometry, meshing, deck, solver, validation, post, render ·\n`src/materials/` library · `src/server/` protocol, tools, tasks, limits,\nworker host, Modal app, Apps UI · `src/client/` SDK tasks extension ·\n`tests/` (incl. `torture/`) · `demo/` · `docs/` decisions, validation, upstream notes. License: Apache-2.0.\n",
  "bytes": 7819,
  "sha": "fe19004d85a1b5cbb08fe444e0346626fd6645c71deac3f7fe8395fe7262a84e",
  "repo_slug": "therobomaster123/mcp-fea",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_therobomaster123_mcp_fea_dde2ee23/readme"
}