fairchem-mcp
# fairchem-mcp
An **agent-steerable MCP server** for [FAIRChem](https://github.com/facebookresearch/fairchem)
and [ASE](https://wiki.fysik.dtu.dk/ase/) simulations.
Most LLM-driven simulation today is *batch*: the agent writes a script, runs it,
waits, and reads the output. `fairchem-mcp` makes it *interactive*. The model is
loaded once and kept resident; relaxations and MD run in the background; and the
agent can **watch a simulation as it runs and steer it mid-flight** — switch the
optimizer when it stalls, tighten `fmax`, change temperature, pause, or abort.
## Why
- **Resident model.** Load a UMA/eSEN model once; reuse it across every call. No
reloading the model (seconds–minutes) on each run.
- **Live monitoring.** `get_status` returns step, energy, max force, and a
`trend` verdict (`decreasing` / `plateaued` / `stuck` / `diverging`) so the
agent can decide whether to intervene.
- **Mid-flight steering.** `steer` can `pause` / `resume` / `abort`, `set_fmax`,
`switch_optimizer`, or `set_temperature` on a running job. Switching optimizer
carries the atomic positions over — it just rebuilds the driver.
- **Hybrid namespace.** High-level tools (`start_relaxation`, …) and the
`execute` / `inspect_expr` escape hatch share one Python namespace, so the
agent can drop to raw Python on the very same live `Atoms`.
- **Code awareness.** `introspect` reads the *installed* API (real signatures and
docstrings) and live objects via `jedi` — not possibly-stale docs.
## Install
```bash
pip install -e . # core: mcp + ase + jedi + numpy (works with EMT)
pip install -e ".[fairchem]" # add FAIRChem (torch + models) for UMA/eSEN
pip install -e ".[lammps]" # add LAMMPS as a classical force engine
pip install -e ".[saddles]" # add Sella + POUNCE for transition-state searches
```
ASE, numpy, jedi and mcp are **core** dependencies (installed automatically) — the
server is fully usable out of the box with the built-in EMT calculator.
### FAIRChem (optional)
`pip install -e ".[fairchem]"` pulls in `fairchem-core` (PyTorch + models). To
actually load a UMA model you also need:
- a **Hugging Face account** with approved access to the `facebook/UMA` model
repository, and `huggingface-cli login` (or `HF_TOKEN`) set;
- PyTorch for your platform (CUDA build for GPU; CPU works but is slower).
Everything except `load_model` works with a plain ASE calculator (`attach_emt`),
so you can develop and test without a GPU or model.
### LAMMPS (optional)
`pip install -e ".[lammps]"` installs the `lammps` Python package. LAMMPS is used
as a **force engine** (classical potentials) while ASE drives the dynamics, so
`attach_lammps` works with every steerable job (MD, relaxation, NEB, phonons,
EOS, minima search).
**macOS note (Homebrew MPICH):** the PyPI `lammps` wheel links
`@rpath/libmpi.12.dylib` and `libpmpi.12.dylib`, which the dynamic loader can't
find by default. If you have Homebrew's `mpich` (`brew install mpich`),
`attach_lammps` **auto-symlinks** those libs into the `lammps` package directory
on first use. If that can't be applied (read-only install, non-Homebrew MPI),
either symlink them yourself or export before launching the server:
```bash
export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH
```
Verify with: `python -c "from lammps import lammps; lammps(cmdargs=['-log','none','-screen','none']).close(); print('ok')"`.
### Transition-state searches (optional)
`pip install -e ".[saddles]"` adds two extra saddle-point backends:
[`sella`](https://github.com/zadorlab/sella) (a rational-function ASE optimizer)
and [`pounce-solver`](https://pypi.org/project/pounce-solver/) (multistart
eigenvector following). The **dimer** method (`start_saddle_search`) is pure ASE
and needs neither. See [`examples/saddles/`](examples/saddles/) for all three.
## Register with Claude Code
Add to your MCP config (see `examples/claude_mcp_config.json`):
```json
{
"mcpServers": {
"fairchem": { "command": "fairchem-mcp" }
}
}
```
## Tools
| Tool | Purpose |
|---|---|
| `list_models` | List FAIRChem pretrained models |
| `load_model` | Load a UMA/eSEN model as a resident calculator |
| `attach_emt` | Attach a fast EMT calculator (no GPU/model) |
| `attach_lammps` | Attach LAMMPS (classical potentials) as the force engine |
| `build_structure` / `load_structure` | Make/register an ASE structure |
| `start_relaxation` / `start_md` | Launch a background relaxation / MD (returns a `job_id`) |
| `start_neb` | Launch a steerable nudged-elastic-band (reaction barrier) |
| `start_saddle_search` | Transition state via the dimer method (Hessian-free) |
| `start_sella_search` | Transition state via Sella (RFO + approximate Hessian) |
| `start_pounce_saddles` | Enumerate saddles by Morse index (POUNCE eigenvector following) |
| `start_phonons` | Launch a finite-displacement phonon calculation |
| `start_eos_scan` | Scan cell strain → fit equation of state (V0, E0, bulk modulus) |
| `start_elastic_scan` | Stress-vs-strain → elastic tensor (C_ij), VRH moduli, Born stability |
| `start_convex_hull` | Formation energies + convex hull (phase stability, energy above hull) |
| `start_minima_search` | Find multiple distinct relaxed geometries via deflation/flooding |
| `get_status` / `get_trajectory` | Observe a running job |
| `get_results` | Final results: NEB barrier/energies, phonon frequencies & stability, distinct minima, EOS fit |
| `steer` | `pause`/`resume`/`abort`/`set_fmax`/`switch_optimizer`/`set_temperature`/`set_climb` |
| `introspect` | Signatures/docstrings/members of installed code or live objects |
| `execute` / `inspect_expr` | Run/eval Python in the shared session namespace |
Resources: `sim://models`, `sim://job/{id}/status`, `sim://job/{id}/trajectory`.
## Example flow
```
attach_emt() -> calc_1
build_structure({"kind":"bulk","name":"Cu","crystalstructure":"fcc",
"a":3.6,"repeat":[2,2,2],"rattle":0.2}) -> struct_1
start_relaxation("struct_1","calc_1",optimizer="FIRE",fmax=0.01) -> job_1
get_status("job_1") -> {status:"running", trend:{label:"stuck", ...}}
steer("job_1","switch_optimizer",optimizer="LBFGS")
get_status("job_1") -> {status:"converged", ...}
introspect("atoms", live=True) -> live object signature/docstring
```
## Finding multiple relaxed geometries
`start_minima_search` finds several *distinct* local minima of the PES — useful
for surface adsorption sites, cluster isomers, or conformers. It relaxes
repeatedly from the starting structure on a PES **biased to repel the minima
already found**, then polishes each escape on the true PES:
- `kernel="flooding"` (default) adds Gaussian bumps (`sigma` Å, `amplitude` eV);
`kernel="deflation"` adds inverse-distance poles (`eta`, `power`). Both are best
for **fixed-frame** problems (an adsorbate on a frozen slab, an anchored
conformer).
- `kernel="basinhopping"` (random kick + relax + Metropolis accept) is the right
tool for **free clusters / nanoparticles**, whose rigid-body rotation defeats a
spatial bias. Pair it with `comparator="fingerprint"` (see below).
- New minima are deduplicated by energy (`energy_tol`) plus a structure
`comparator`: `"rmsd"` (raw coords, frame-dependent — fine for a fixed frame) or
`"fingerprint"` (sorted pairwise distances; rotation/translation/permutation
invariant — use for free clusters and molecules, or rotated copies get
miscounted as distinct). Each accepted minimum is registered as its own
structure.
This reuses the *escape mechanism* from POUNCE's `find_minima` (deflation /
flooding) but drives it with ASE's gradient optimizers — the right inner solver
for a PES — so each escape relaxation is a normal steerable job (watch the trend,
`switch_optimizer`, `set_fmax`, pause/abort). POUNCE's interior-point solver is
deliberately *not* used as the inner relaxer.
## Examples
[`examples/catalysis/`](examples/catalysis/) has four end-to-end catalysis
workflows — adsorption energy, adsorption-site search, diffusion-barrier NEB, and
surface-stability phonons — each as a runnable script **and** an MCP tool-call
walkthrough. They run on EMT out of the box; set `FAIRCHEM_MCP_EXAMPLE_MODEL` for
UMA.
[`examples/saddles/`](examples/saddles/) covers the three single-ended
transition-state routes — dimer, Sella, and POUNCE eigenvector following — on one
shared system so you can compare them.
[`examples/alloys/`](examples/alloys/) builds up to alloy design: the elastic
stiffness tensor (`start_elastic_scan`), the formation-energy convex hull
(`start_convex_hull`), and a design loop that gates candidates on stability then
ranks the survivors by a mechanical property.
## Safety
`execute` / `inspect_expr` run arbitrary Python in-process. This is a **trusted
local developer tool** — do not expose it to untrusted input or over a network.
## Notes
- Only one job runs at a time (serializes model/GPU access).
- All optimizers/integrators use `logfile=None`; the stdio transport reserves
stdout for the MCP protocol.
TDQS
Scored across 17 tools
Each tool has a clearly distinct purpose. Potential overlaps (e.g., attach_emt vs load_model) are resolved by descriptions specifying different types of calculators. Similarly, build_structure vs load_structure, and the various start_* tools are differentiated by their specific computational tasks.
Tools mostly follow a verb_noun pattern (e.g., build_structure, load_model, start_relaxation). A few exceptions like execute and introspect are verb-only, but overall the naming is predictable and well-organized with prefixes like get_, start_, load_.
With 17 tools, the server covers the essential workflows for computational chemistry (structure handling, calculator setup, multiple simulation types, monitoring, and introspection) without being bloated. Each tool earns its place.
The tool set provides complete lifecycle coverage: structure creation/loading, calculator attachment, running various simulations (relaxation, MD, NEB, phonons, minima search), job control (steering, status, results), and introspection. No obvious gaps for typical usage.