# Ovito MCP

> 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.

- Skill: `wjgoarxiv/ovito-mcp` (Agent Skill)
- Install (CLI): `npx skillmds@latest add wjgoarxiv/ovito-mcp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wjgoarxiv/ovito-mcp/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: wjgoarxiv (https://skillmd.com/u/wjgoarxiv)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/wjgoarxiv/ovito-mcp

---


## 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

```bash
pip install ovito        # 3.15+ required; bundles OSPRay, Tachyon, ffmpeg
pip install fastmcp numpy scipy   # MCP framework + analysis deps
```

Or with the package itself:

```bash
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`):

```json
{
  "mcpServers": {
    "ovito": {
      "command": "uvx",
      "args": ["ovito-mcp"],
      "env": {
        "OVITO_DATA_ROOT": "/path/to/your/data"
      }
    }
  }
}
```

**Project `.mcp.json`** (or `mcp.json`):

```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.

