What it is
ovito-mcp wraps OVITO's Python module as an MCP server so an LLM can drive
headless molecular analysis entirely in-process — no GUI, no localhost socket,
no display. A single create_session opens any GROMACS/LAMMPS/XYZ/CIF file;
subsequent tool calls run OVITO modifiers, the reused ClavisCage analysis layer,
and OSPRay/Tachyon ray-tracing — returning summaries, tables, counts, and inline PNGs.
Raw per-atom arrays are never returned.
Key capabilities (v0.2.0):
- 68 native OVITO modifiers — RDF, coordination, CNA/PTM/IdentifyDiamond,
clusters, expression-select, Voronoi, Wigner-Seitz
- Clathrate-hydrate suite — F3/F4 order parameters (scalar + per-atom),
H-bond network, cage identification (TRACE or HTR primitive-ring mode),
GRADE structure classification with confidence, cage occupancy with guest source
- Headless OSPRay rendering — photorealistic (AO + denoising), orthographic by default; snapshots, cage overlays, color by order
parameter, animated sequences
- stdio-safe — a permanent fd-1 guard keeps OVITO's C++ output off the
JSON-RPC channel
Install
1. Install OVITO Python module
pip install ovito # 3.15+ required; bundles OSPRay, Tachyon, ffmpeg
pip install fastmcp numpy scipy # MCP framework + analysis deps
Or with the package itself:
git clone https://github.com/wjgoarxiv/ovito-mcp.git
cd ovito-mcp
uv pip install -e ".[dev]" # add ,md for MDAnalysis trajectory bridge
2. Register the MCP server
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"ovito": {
"command": "uvx",
"args": ["ovito-mcp"],
"env": {
"OVITO_DATA_ROOT": "/path/to/your/data"
}
}
}
}
Project .mcp.json (or mcp.json):
{
"mcpServers": {
"ovito": {
"command": "uvx",
"args": ["ovito-mcp"],
"env": {
"OVITO_DATA_ROOT": "/path/to/your/data"
}
}
}
}
Set OVITO_DATA_ROOT to the directory containing your trajectory files. When
set, every file path argument is validated against this root — it acts as an
allowlist. Leave it unset only in trusted single-user environments.
Tool cheat-sheet
Session / IO
| Tool |
Key args |
Returns |
create_session |
path, length_unit="A" |
session_id, n_frames, n_atoms, particle_types, cell |
get_trajectory_info |
session_id |
frames, atoms, types, cell, pbc |
load_trajectory |
session_id, traj_path |
attaches .xtc/.trr/.dump; updated n_frames |
load_guests |
session_id, guest_path |
attaches a second pipeline for guest molecules; enables merged occupancy |
export_data |
session_id, out_path, fmt="xyz", frame=0 |
written path |
list_sessions |
— |
open session IDs |
close_session |
session_id |
closed: bool |
Native analysis
| Tool |
Key args |
Returns |
compute_rdf |
cutoff=5.0, bins=200, frame=0 |
r[], g_r[] table |
coordination_analysis |
cutoff=3.5, frame=0 |
mean / min / max / std coordination |
structure_identification |
method=cna|ptm|diamond, frame=0 |
per-structure counts + fractions |
cluster_analysis |
cutoff=3.5, frame=0 |
n_clusters, largest-cluster sizes |
select_expression |
expression, frame=0 |
n_selected / n_atoms |
voronoi_analysis |
session_id, frame=0 |
mean atomic volume, mean face count, max cavity radius |
wigner_seitz |
session_id, ref_frame, frame |
vacancy count, interstitial count |
Rendering
| Tool |
Key args |
Returns |
render_snapshot |
frame=0, width=800, height=600, color_by=None|"F4"|"F3", projection="ortho", renderer="ospray" |
inline PNG (OSPRay photoreal + Ortho by default) |
render_cages |
session_id, frame=0, method="trace"|"htr", renderer="ospray", projection="ortho", line_width=0.16 |
inline PNG — cages as colour-coded wireframe polyhedra: member-O vertices + H-bond edges share the cage-type colour (5¹² cyan, 5¹²6⁴ red), inside a yellow box |
render_animation |
session_id, start, end, fps=10, renderer="tachyon", projection="ortho" |
absolute path to .mp4 (GIF fallback if ffmpeg absent) |
Hydrate suite
| Tool |
Key args |
Returns |
order_parameters |
frame=0, oo_cutoff_nm=0.35 |
F4, F3, hydrate_like, n_pairs, n_waters |
hbond_network |
frame=0, rcut_nm=0.36, angle_deg=35 |
n_hbonds, mean_coordination, max_coordination |
identify_cages |
frame=0, rcut_nm=0.36, angle_deg=35, method="trace"|"htr" |
cage_counts (5¹²/5¹²6²/5¹²6⁴/…), structure |
classify_structure |
frame=0, rcut_nm=0.36, angle_deg=35 |
structure (sI/sII/sH/mixed/unknown), cage_counts, confidence |
cage_occupancy |
frame=0, rcut_nm=0.36, angle_deg=35 |
total_cages, occupied, overall_occupancy, by_type |
Common workflows
1. Identify a GROMACS hydrate structure
User: Load sII_hydrate.gro and tell me what hydrate structure it is.
1. create_session(path="sII_hydrate.gro", length_unit="A")
2. classify_structure(session_id=..., frame=0)
→ {"structure": "sII", "confidence": 0.93,
"cage_counts": {"5^12": 128, "5^12 6^4": 64}}
3. Report: sII clathrate, 128 small + 64 large cages, confidence 93%.
2. LAMMPS dump — RDF then structure type
User: Open water.dump, plot the O-O RDF and check for ice vs liquid.
1. create_session(path="water.dump", length_unit="A")
2. compute_rdf(session_id=..., cutoff=8.0, bins=400, frame=0)
3. structure_identification(session_id=..., method="diamond", frame=0)
→ IdentifyDiamond gives ice-Ih / ice-Ic fractions
4. order_parameters(session_id=...) → F4 ≈ -0.4 confirms ice Ih
3. Render a snapshot colored by F4 order parameter
User: Render the hydrate colored by the F4 order parameter.
1. create_session(path="hydrate.gro")
2. render_snapshot(session_id=..., color_by="F4", width=800, height=600)
→ ovito-mcp writes per-water F4 values onto O atoms as a particle
property, then ColorCoding maps them; returns inline PNG
4. Cage-overlay render (v0.2.0)
User: Show me where the cages are in this sII frame.
1. create_session(path="sII_hydrate.gro")
2. render_cages(session_id=..., frame=0, method="trace")
→ cage centres added as marker particles; member O atoms colored
by cage type; inline PNG returned
5. Cage occupancy with a separate guest file
User: I have waters in host.gro and CO2 guests in guests.gro. What fraction
of cages are occupied?
1. create_session(path="host.gro")
2. load_guests(session_id=..., guest_path="guests.gro")
3. cage_occupancy(session_id=..., frame=0)
→ {"total_cages": 192, "occupied": 192, "overall_occupancy": 1.0,
"by_type": {"5^12": {...}, "5^12 6^4": {...}}}
6. Defect analysis — Voronoi + Wigner-Seitz
User: Find vacancies and interstitials in the irradiated frame vs reference.
1. create_session(path="cascade.dump")
2. voronoi_analysis(session_id=..., frame=50)
→ mean_volume, mean_faces, max_cavity_radius
3. wigner_seitz(session_id=..., ref_frame=0, frame=50)
→ {"vacancies": 23, "interstitials": 21}
7. Animation export
User: Export frames 0-99 as an mp4 at 15 fps.
1. create_session(path="trajectory.xtc")
2. render_animation(session_id=..., frame_range=[0, 99],
width=640, height=480, fps=15)
→ "/tmp/ovito_anim_abc123.mp4"
Reference values (validation fixtures)
These golden values are pinned. If the server returns different numbers on these
fixtures, report it as a bug rather than accepting the output.
| Fixture |
Waters |
Tool |
Expected |
examples/sII_222_S2.gro |
1 088 |
identify_cages (TRACE) |
{5^12: 128, 5^12 6^4: 64} → sII |
examples/sII_222_S2.gro |
1 088 |
classify_structure |
structure=sII, confidence≈0.93 |
examples/sII_222_S2.gro |
1 088 |
order_parameters |
F4≈0.9649, F3≈0.003 |
examples/sI_222_S1.gro |
368 |
identify_cages (TRACE) |
{5^12: 16, 5^12 6^2: 48} → sI |
examples/sI_222_S1.gro |
368 |
classify_structure |
structure=sI, confidence=1.00 |
examples/sII_222_S2.gro |
1 088 |
hbond_network |
mean_coordination≈4.0 |
| Subset first 10 waters |
10 |
order_parameters (cutoff 0.35 nm) |
F4=0.926698 (exact) |
Safety notes
OVITO_DATA_ROOT (strongly recommended in production)
When this environment variable is set, every file path passed to any tool is
validated against it. Paths outside the root are rejected before OVITO sees
them. In multi-user or CI environments, always set this variable.
OVITO_MCP_MAX_SESSIONS (default 8)
Sessions are LRU-evicted when the cap is reached. Close sessions explicitly
with close_session after long analyses to free memory — OVITO keeps full
pipeline state per session.
OVITO_MCP_ALLOW_CODE_EXEC (off by default)
A future arbitrary-code execution tool is disabled unless this variable is
set. There is no sandbox; enable only in trusted environments. Do not set
it in shared or automated deployments.
stdio purity
ovito-mcp installs a permanent file-descriptor guard before importing OVITO
so that OVITO's C++ diagnostic output never corrupts the JSON-RPC channel.
If you see MCP protocol errors, check that nothing in your startup environment
redirects stdout before the guard runs.
Data discipline
Every tool returns summaries, tables, counts, images, or file paths — never
raw per-atom coordinate arrays. If a workflow needs atom-level data, use
export_data to write a file and process it outside the server.
1---2name: ovito-mcp3description: Drive the ovito-mcp MCP server to analyze MD trajectories and clathrate-hydrate structures headlessly via OVITO in-process. TRIGGER when: the user mentions OVITO, GROMACS (.gro/.xtc/.trr), LAMMPS (dump/data), trajectory analysis, RDF, coordination, CNA/PTM/structure identification, clathrate hydrates, cage identification (sI/sII/sH), F3/F4 order parameters, hydrogen-bond networks, cage occupancy, or headless molecular rendering. DO NOT TRIGGER when: the user is working with PyMOL, VMD, or a pure-Python script with no OVITO dependency; the ovito-mcp server is not registered in the MCP config; or the request is a general MD question requiring no tool calls.4---56## What it is78**ovito-mcp** wraps OVITO's Python module as an MCP server so an LLM can drive9headless molecular analysis entirely in-process — no GUI, no localhost socket,10no display. A single `create_session` opens any GROMACS/LAMMPS/XYZ/CIF file;11subsequent tool calls run OVITO modifiers, the reused ClavisCage analysis layer,12and OSPRay/Tachyon ray-tracing — returning summaries, tables, counts, and inline PNGs.13Raw per-atom arrays are never returned.1415Key capabilities (v0.2.0):1617- **68 native OVITO modifiers** — RDF, coordination, CNA/PTM/IdentifyDiamond,18 clusters, expression-select, Voronoi, Wigner-Seitz19- **Clathrate-hydrate suite** — F3/F4 order parameters (scalar + per-atom),20 H-bond network, cage identification (TRACE or HTR primitive-ring mode),21 GRADE structure classification with confidence, cage occupancy with guest source22- **Headless OSPRay rendering** — photorealistic (AO + denoising), orthographic by default; snapshots, cage overlays, color by order23 parameter, animated sequences24- **stdio-safe** — a permanent fd-1 guard keeps OVITO's C++ output off the25 JSON-RPC channel262728## Install2930### 1. Install OVITO Python module3132```bash33pip install ovito # 3.15+ required; bundles OSPRay, Tachyon, ffmpeg34pip install fastmcp numpy scipy # MCP framework + analysis deps35```3637Or with the package itself:3839```bash40git clone https://github.com/wjgoarxiv/ovito-mcp.git41cd ovito-mcp42uv pip install -e ".[dev]" # add ,md for MDAnalysis trajectory bridge43```4445### 2. Register the MCP server4647**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):4849```json50{51 "mcpServers": {52 "ovito": {53 "command": "uvx",54 "args": ["ovito-mcp"],55 "env": {56 "OVITO_DATA_ROOT": "/path/to/your/data"57 }58 }59 }60}61```6263**Project `.mcp.json`** (or `mcp.json`):6465```json66{67 "mcpServers": {68 "ovito": {69 "command": "uvx",70 "args": ["ovito-mcp"],71 "env": {72 "OVITO_DATA_ROOT": "/path/to/your/data"73 }74 }75 }76}77```7879Set `OVITO_DATA_ROOT` to the directory containing your trajectory files. When80set, every file path argument is validated against this root — it acts as an81allowlist. Leave it unset only in trusted single-user environments.828384## Tool cheat-sheet8586### Session / IO8788| Tool | Key args | Returns |89|---|---|---|90| `create_session` | `path`, `length_unit="A"` | `session_id`, `n_frames`, `n_atoms`, `particle_types`, `cell` |91| `get_trajectory_info` | `session_id` | frames, atoms, types, cell, pbc |92| `load_trajectory` | `session_id`, `traj_path` | attaches `.xtc`/`.trr`/`.dump`; updated `n_frames` |93| `load_guests` | `session_id`, `guest_path` | attaches a second pipeline for guest molecules; enables merged occupancy |94| `export_data` | `session_id`, `out_path`, `fmt="xyz"`, `frame=0` | written path |95| `list_sessions` | — | open session IDs |96| `close_session` | `session_id` | `closed: bool` |9798### Native analysis99100| Tool | Key args | Returns |101|---|---|---|102| `compute_rdf` | `cutoff=5.0`, `bins=200`, `frame=0` | `r[]`, `g_r[]` table |103| `coordination_analysis` | `cutoff=3.5`, `frame=0` | mean / min / max / std coordination |104| `structure_identification` | `method=cna\|ptm\|diamond`, `frame=0` | per-structure counts + fractions |105| `cluster_analysis` | `cutoff=3.5`, `frame=0` | `n_clusters`, largest-cluster sizes |106| `select_expression` | `expression`, `frame=0` | `n_selected` / `n_atoms` |107| `voronoi_analysis` | `session_id`, `frame=0` | mean atomic volume, mean face count, max cavity radius |108| `wigner_seitz` | `session_id`, `ref_frame`, `frame` | vacancy count, interstitial count |109110### Rendering111112| Tool | Key args | Returns |113|---|---|---|114| `render_snapshot` | `frame=0`, `width=800`, `height=600`, `color_by=None\|"F4"\|"F3"`, `projection="ortho"`, `renderer="ospray"` | inline PNG (OSPRay photoreal + Ortho by default) |115| `render_cages` | `session_id`, `frame=0`, `method="trace"\|"htr"`, `renderer="ospray"`, `projection="ortho"`, `line_width=0.16` | inline PNG — cages as colour-coded wireframe polyhedra: member-O vertices + H-bond edges share the cage-type colour (5¹² cyan, 5¹²6⁴ red), inside a yellow box |116| `render_animation` | `session_id`, `start`, `end`, `fps=10`, `renderer="tachyon"`, `projection="ortho"` | absolute path to `.mp4` (GIF fallback if ffmpeg absent) |117118### Hydrate suite119120| Tool | Key args | Returns |121|---|---|---|122| `order_parameters` | `frame=0`, `oo_cutoff_nm=0.35` | `F4`, `F3`, `hydrate_like`, `n_pairs`, `n_waters` |123| `hbond_network` | `frame=0`, `rcut_nm=0.36`, `angle_deg=35` | `n_hbonds`, `mean_coordination`, `max_coordination` |124| `identify_cages` | `frame=0`, `rcut_nm=0.36`, `angle_deg=35`, `method="trace"\|"htr"` | `cage_counts` (5¹²/5¹²6²/5¹²6⁴/…), `structure` |125| `classify_structure` | `frame=0`, `rcut_nm=0.36`, `angle_deg=35` | `structure` (sI/sII/sH/mixed/unknown), `cage_counts`, `confidence` |126| `cage_occupancy` | `frame=0`, `rcut_nm=0.36`, `angle_deg=35` | `total_cages`, `occupied`, `overall_occupancy`, `by_type` |127128129## Common workflows130131### 1. Identify a GROMACS hydrate structure132133```134User: Load sII_hydrate.gro and tell me what hydrate structure it is.1351361. create_session(path="sII_hydrate.gro", length_unit="A")1372. classify_structure(session_id=..., frame=0)138 → {"structure": "sII", "confidence": 0.93,139 "cage_counts": {"5^12": 128, "5^12 6^4": 64}}1403. Report: sII clathrate, 128 small + 64 large cages, confidence 93%.141```142143### 2. LAMMPS dump — RDF then structure type144145```146User: Open water.dump, plot the O-O RDF and check for ice vs liquid.1471481. create_session(path="water.dump", length_unit="A")1492. compute_rdf(session_id=..., cutoff=8.0, bins=400, frame=0)1503. structure_identification(session_id=..., method="diamond", frame=0)151 → IdentifyDiamond gives ice-Ih / ice-Ic fractions1524. order_parameters(session_id=...) → F4 ≈ -0.4 confirms ice Ih153```154155### 3. Render a snapshot colored by F4 order parameter156157```158User: Render the hydrate colored by the F4 order parameter.1591601. create_session(path="hydrate.gro")1612. render_snapshot(session_id=..., color_by="F4", width=800, height=600)162 → ovito-mcp writes per-water F4 values onto O atoms as a particle163 property, then ColorCoding maps them; returns inline PNG164```165166### 4. Cage-overlay render (v0.2.0)167168```169User: Show me where the cages are in this sII frame.1701711. create_session(path="sII_hydrate.gro")1722. render_cages(session_id=..., frame=0, method="trace")173 → cage centres added as marker particles; member O atoms colored174 by cage type; inline PNG returned175```176177### 5. Cage occupancy with a separate guest file178179```180User: I have waters in host.gro and CO2 guests in guests.gro. What fraction181 of cages are occupied?1821831. create_session(path="host.gro")1842. load_guests(session_id=..., guest_path="guests.gro")1853. cage_occupancy(session_id=..., frame=0)186 → {"total_cages": 192, "occupied": 192, "overall_occupancy": 1.0,187 "by_type": {"5^12": {...}, "5^12 6^4": {...}}}188```189190### 6. Defect analysis — Voronoi + Wigner-Seitz191192```193User: Find vacancies and interstitials in the irradiated frame vs reference.1941951. create_session(path="cascade.dump")1962. voronoi_analysis(session_id=..., frame=50)197 → mean_volume, mean_faces, max_cavity_radius1983. wigner_seitz(session_id=..., ref_frame=0, frame=50)199 → {"vacancies": 23, "interstitials": 21}200```201202### 7. Animation export203204```205User: Export frames 0-99 as an mp4 at 15 fps.2062071. create_session(path="trajectory.xtc")2082. render_animation(session_id=..., frame_range=[0, 99],209 width=640, height=480, fps=15)210 → "/tmp/ovito_anim_abc123.mp4"211```212213214## Reference values (validation fixtures)215216These golden values are pinned. If the server returns different numbers on these217fixtures, report it as a bug rather than accepting the output.218219| Fixture | Waters | Tool | Expected |220|---|---|---|---|221| `examples/sII_222_S2.gro` | 1 088 | `identify_cages` (TRACE) | `{5^12: 128, 5^12 6^4: 64}` → sII |222| `examples/sII_222_S2.gro` | 1 088 | `classify_structure` | structure=sII, confidence≈0.93 |223| `examples/sII_222_S2.gro` | 1 088 | `order_parameters` | F4≈0.9649, F3≈0.003 |224| `examples/sI_222_S1.gro` | 368 | `identify_cages` (TRACE) | `{5^12: 16, 5^12 6^2: 48}` → sI |225| `examples/sI_222_S1.gro` | 368 | `classify_structure` | structure=sI, confidence=1.00 |226| `examples/sII_222_S2.gro` | 1 088 | `hbond_network` | mean_coordination≈4.0 |227| Subset first 10 waters | 10 | `order_parameters` (cutoff 0.35 nm) | F4=0.926698 (exact) |228229230## Safety notes231232**`OVITO_DATA_ROOT`** (strongly recommended in production)233When this environment variable is set, every file path passed to any tool is234validated against it. Paths outside the root are rejected before OVITO sees235them. In multi-user or CI environments, always set this variable.236237**`OVITO_MCP_MAX_SESSIONS`** (default 8)238Sessions are LRU-evicted when the cap is reached. Close sessions explicitly239with `close_session` after long analyses to free memory — OVITO keeps full240pipeline state per session.241242**`OVITO_MCP_ALLOW_CODE_EXEC`** (off by default)243A future arbitrary-code execution tool is disabled unless this variable is244set. There is no sandbox; enable only in trusted environments. Do not set245it in shared or automated deployments.246247**stdio purity**248ovito-mcp installs a permanent file-descriptor guard before importing OVITO249so that OVITO's C++ diagnostic output never corrupts the JSON-RPC channel.250If you see MCP protocol errors, check that nothing in your startup environment251redirects stdout before the guard runs.252253**Data discipline**254Every tool returns summaries, tables, counts, images, or file paths — never255raw per-atom coordinate arrays. If a workflow needs atom-level data, use256`export_data` to write a file and process it outside the server.