Nabla ships a Model Context Protocol server. You talk to an LLM client — Claude Code, Claude Desktop, anything that speaks MCP — in ordinary English: draw a 50 mm square, make it 1020 steel, mesh it, solve it, plot |B|. The client calls tools; the tools drive the same Python API a script would use; the API mutates the in-process model and runs the solver.
Wiring an LLM to an existing API is a weekend. Making the result trustworthy is the actual project, and it is more interesting than the plumbing.
The shape of it
LLM client <--- MCP, stdio JSON-RPC ---> nabla_mcp (Python)
| async tool handlers
v
single JVM worker thread
| JPype-backed Nabla API
v
solver.exe (.mfs -> .ems)
Stdio transport, because that is what MCP clients configure natively and it raises no networking or authentication questions. One JVM worker thread, because the model is stateful and a second thread mutating it concurrently is not a race you want to debug through a chat transcript. 121 tools, 7 read-only reference resources, and 6 guided workflow prompts, in loadable packs — geometry, materials, machines, thermal, performance — so a session can carry only the surface it needs.
1. Truthfulness: the model must not be able to report success it did not get
A language model asked whether an operation worked will answer from whatever text it has. If a tool returns nothing on failure, it will conclude success — not from dishonesty, but because nothing said otherwise. So the tools do not return what they were asked to do; they return what the model became.
That means diffing the solver's log buffer around each call so warnings surface instead of scrolling past; resolving names to the entity actually affected rather than echoing the argument back; and reading the state back after a mutation and reporting the read-back. “Assigned material 1020 steel to region 3” should be a measurement, not a restatement of the request.
2. A state machine, because the model cannot see the GUI
A human knows the mesh is stale because they just moved a line. An LLM has only what the tools tell it. So the server carries an explicit model state: a revision number, per-tool declarations of what mutates and what requires what, dirty tracking, and explicit units on every quantity that has one.
That last point is not a small thing. Half the errors a human makes in FEA are unit
errors, and an LLM is worse at units than a human, not better, because it pattern-matches
plausible numbers. A payload that carries {"value": 1.83, "unit": "T"}
is checkable; a bare 1.83 is a guess waiting to be made.
3. Solves take minutes; chat turns take seconds
A transient motor solve is not a function call. The server runs long operations as jobs on a second worker lane, with per-step progress notifications, cancellation through a stop file, and convergence reporting when they finish. The model can start a 400-step transient, answer a question about the geometry while it runs, and come back for the result — instead of blocking a conversation for six minutes and then timing out.
4. Results have to fit in a context window without lying
A field solution has hundreds of thousands of values. Sending all of them is impossible; sending every tenth one is worse than impossible, because it silently removes the peaks — and the peak is usually the answer. Torque ripple, peak flux density, the worst-case demagnetisation point: all of them are extremes, and naive decimation is precisely the operation that deletes extremes.
So decimation is peak-preserving, and the exact statistics — min, max, mean, RMS — are computed on the full data and sent alongside the reduced series. The model sees a curve it can reason about and numbers that are true. For anything visual there are rendered images: geometry and field plots produced by the same renderer as the GUI's surface plot, so what the model reports matches what you would have seen on screen.
Why bother
Two reasons, and the second is the bigger one.
The first is the obvious productivity story: parametric sweeps, batch studies and “try it with a 0.5 mm thicker magnet” without writing the script each time. That is real, and it is a straightforward extension of what the Python API already did.
The second is the onboarding barrier. FEA tools are hard to start using not because the physics is hard but because the interface encodes decades of conventions — which boundary condition where, what the sliding band wants, why the mesh refused. A model that has read the manual and can drive the tool turns a week of learning into an afternoon of asking. The guided workflow prompts exist for exactly that: they encode the order in which a competent user does things.
What it does not do is replace the engineer. The solver still needs a sensible mesh, the right boundary condition and a check against something known — which is what the validation dossier is for, and why every claim in it is published with the case that produced it.
The MCP server and the Python API are part of Nabla Core. The automation chapter of the manual covers installation and the client configuration.
Related: several motion zones in one model, second-order elements, and the Python API reference.