Content
# shelx-mcp
An [MCP](https://modelcontextprotocol.io) server that wraps the **SHELX**
console crystallography programs so an LLM can build `.ins` instruction files,
run structure solutions and refinements, and read the results back — all from a
chat interface.
It ships with the authoritative **SHELXL instruction reference** and the
**SHELXS / SHELXD / SHELXE solution-program references** bundled offline, so the
model can look up what any command or solution keyword does without leaving the
conversation.
---
## What it does
The server exposes **11 tools** split into two coverage areas:
- **Refinement (SHELXL)** — the cards that build and refine a model.
- **Structure solution (SHELXS / SHELXD / SHELXE)** — the keywords that *find*
a structure in the first place.
| Tool | Coverage | Purpose |
|------|----------|---------|
| `run_shelx` | both | Execute any allowed SHELX program on supplied input files. |
| `solve` | solution | Run structure solution (SHELXS direct methods, or SHELXT dual-space). |
| `refine` | refinement | Run least-squares refinement with SHELXL (or CGLS via SHELXT). |
| `list_commands` | refinement | List every SHELXL instruction the server understands. |
| `command_help` | refinement | Full signature + description for one instruction (e.g. `ANIS`, `L.S.`). |
| `search_commands` | refinement | Free-text search across instruction names and descriptions. |
| `list_solution_keywords` | solution | List SHELXS/SHELXD `.ins` cards or SHELXE command-line options. |
| `solution_help` | solution | Full signature + description for one solution keyword (e.g. `TREF`, `-aN`). |
| `validate_ins` | refinement | Check an `.ins` file for the required SHELXL header cards. |
| `analyze_results` | both | Extract R1, wR2, GooF (S), max shift/esd, atom count and warnings. |
| `shelx_info` | both | Report the configured SHELX directory and available programs. |
> **Refinement vs. solution coverage.** SHELXL refines a model you already
> have; SHELXS/SHELXD/SHELXE find the model from diffraction data. Use
> `list_commands` / `command_help` / `search_commands` for refinement cards, and
> `list_solution_keywords` / `solution_help` for solution keywords. SHELXT and
> SHELXC ship as tutorials rather than keyword lists, so they are not included as
> references (but they can still be *run* via `run_shelx` / `solve`).
### How SHELX is driven
SHELX programs are **console tools driven by a basename**: `shelxl name` reads
`name.ins` (and `name.hkl` when an `HKLF` instruction is present) and writes
`name.res`, `name.lst`, and optionally `name.cif` / `name.fcf`. This server writes
the input files to a (throw-away) working directory, runs the executable, and
returns the generated files as text.
---
## Requirements
- **Python** >= 3.10 (the project uses `uv`, which fetches its own interpreter
if needed).
- The **SHELX executables** installed locally (download from
https://shelx.uni-goettingen.de/ or your site licence). On the author's
machine they live in `C:/Users/kuzmi/AppData/Local/shelx64/`.
- [`uv`](https://docs.astral.sh/uv/) for dependency / environment management.
---
## Installation
```bash
git clone https://github.com/stge4code/shelx-mcp.git
cd shelx-mcp
uv sync # creates .venv and installs mcp + python-dotenv
uv sync --extra test # additionally installs pytest for the test suite
```
### Configure the SHELX location
Copy the template and point `SHELX_DIRECTORY` at the folder that contains the
`*.exe` files:
```bash
cp .env.example .env
# then edit .env:
# SHELX_DIRECTORY=C:/path/to/shelx64
```
> `.env` is git-ignored (it contains a machine-specific path). Only
> `.env.example` is committed.
### Run the server
The server speaks **MCP over stdio**, so you normally do not launch it by hand —
your agent does. To smoke-test it manually:
```bash
uv run shelx-mcp # runs the stdio server
uv run python -m shelx_mcp.server # equivalent
```
Verify the wiring (and that SHELX was found) before connecting an agent:
```bash
uv run python -c "import shelx_mcp.server as s; print(s.shelx_info())"
```
---
## Connecting an agent
All of the configurations below launch the server with:
```
uv --directory <repo> run shelx-mcp
```
Replace `<repo>` with the absolute path to this checkout (e.g.
`X:/DATA/Git/shelx-mcp`). The `.env` file inside the repo is picked up
automatically at startup. Every client uses the same `command` / `args` pair;
only the config file location and key names differ.
| Agent | Config file | Key | Example |
|-------|-------------|-----|---------|
| **Crush** | `crush.json` (project root or `~/.config/crush/`) | `mcp` (`"type": "stdio"`) | [`examples/crush.json`](examples/crush.json) |
| **OpenCode** | `~/.config/opencode/opencode.jsonc` | `mcpServers` | [`examples/opencode.jsonc`](examples/opencode.jsonc) |
| **Claude Desktop** | `claude_desktop_config.json` | `mcpServers` | [`examples/claude_desktop.json`](examples/claude_desktop.json) |
| **VS Code / Copilot** | `.vscode/mcp.json` | `servers` (`"type": "stdio"`) | [`examples/vscode_mcp.json`](examples/vscode_mcp.json) |
| **Zed** | `~/.config/zed/settings.json` | `context_servers` | [`examples/zed.json`](examples/zed.json) |
| **Cursor** | `.cursor/mcp.json` | `mcpServers` | [`examples/cursor.json`](examples/cursor.json) |
| **Cline** | `cline_mcp_settings.json` | `mcpServers` | [`examples/cline.json`](examples/cline.json) |
Copy the matching example into the agent's config location (or merge its server
block into an existing config) and restart the agent. The `shelx` server then
appears in the MCP list with its eleven tools available in any conversation.
For Crush specifically, the block looks like (note the `mcp` key and the
required `type` field, unlike the `mcpServers` key used by Claude/Cursor/Cline):
```json
{
"mcp": {
"shelx": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "X:/DATA/Git/shelx-mcp", "run", "shelx-mcp"]
}
}
}
```
> `uv` must be on the agent's `PATH`. If it is not, replace `"uv"` with the
> absolute path to `uv.exe` (e.g. `C:/Users/kuzmi/.local/bin/uv.exe`).
### Skills
A reusable agent **skill** that teaches the workflow (look up instructions,
validate, solve, refine, analyze) lives at
[`skills/shelx/SKILL.md`](skills/shelx/SKILL.md). To install it for Crush, copy
the folder into your skills directory:
```bash
mkdir -p ~/.config/crush/skills/shelx
cp -r skills/shelx/* ~/.config/crush/skills/shelx/
```
Other agents that support markdown skills can load the same file. The skill
keeps the SHELX card order and the solve→refine→analyze loop consistent across
sessions.
---
## Typical workflow
1. **Look up a command** — refinement card: `command_help("ANIS")` or
`search_commands("rigid group")`; solution keyword:
`list_solution_keywords("shelxs")` then `solution_help("shelxs", "TREF")`.
2. **Build the model** — paste or generate an `.ins` file, then
`validate_ins(...)` to confirm the header cards.
3. **Solve / refine** — call `solve(ins, hkl)` (SHELXS/SHELXT) or
`refine(ins, hkl)` (SHELXL).
4. **Read the outcome** — `analyze_results(lst=..., res=...)` returns the
R-factors, GooF and any warnings; the full `.lst` / `.res` are returned
inline by the run tools for deeper inspection.
---
## Tool reference
### `run_shelx(program, ins, hkl=None, basename="shelx_job", workdir=None, timeout=120, return_output=True)`
Run any allowed SHELX program on the supplied `.ins` (and optional `.hkl`)
text. Returns the program output, return code, and (when `return_output` is
true) the generated `.res` / `.lst` / `.cif` files. `program` must be one of
`shelxs`, `shelxl`, `shelxt`, `shelxc`, `shelxd`, `shelxe`, `ciftab`.
### `solve(ins, hkl=None, program="shelxs", basename="solve", timeout=120)`
Run structure solution. `program` is `shelxs` (direct methods) or `shelxt`
(dual-space). Requires an `.ins` set up for solution (`TITL`, `CELL`, `ZERR`,
`LATT`, `SYMM`, `SFAC`, `UNIT`, plus e.g. `TREF`/`FMAP`, and `HKLF`/`END`) and
the corresponding `.hkl` data.
### `refine(ins, hkl=None, program="shelxl", basename="refine", timeout=300)`
Run least-squares refinement. Requires a complete `.ins` (with `L.S.`/`CGLS` and
`HKLF`) and the `.hkl` data. Returns the `.lst` summary and refined `.res`.
### `list_commands()`
Return every SHELXL instruction name the server knows (alphabetical).
### `command_help(name)`
Return the signature and full description for one SHELXL instruction (e.g.
`command_help("ANIS")`, `command_help("L.S.")`).
### `search_commands(query, limit=10)`
Free-text search across instruction names, signatures, descriptions, related
cards and example lines.
### `list_solution_keywords(program)`
List the `.ins` cards (SHELXS/SHELXD) or command-line options (SHELXE) for
`program` ∈ {`shelxs`, `shelxd`, `shelxe`}.
### `solution_help(program, keyword)`
Return the signature and full description for one solution keyword. For
SHELXS/SHELXD use the card name (e.g. `solution_help("shelxs", "TREF")`); for
SHELXE use the option including its dash and placeholder (e.g.
`solution_help("shelxe", "-aN")`). SHELXE options are case-sensitive.
### `validate_ins(ins)`
Check an `.ins` file for the required SHELXL header cards (`TITL`, `CELL`,
`ZERR`, `LATT`, `SYMM`, `SFAC`, `UNIT`, `HKLF`, `END`) and a few common
mistakes (e.g. `SYMM` is not required for P1/P-1; `UNIT` must precede `HKLF`).
### `analyze_results(lst=None, res=None)`
Summarise refinement metrics: R1, wR2, GooF (S), max shift/esd, atom count and
any warning lines from the `.lst` / `.res` text.
### `shelx_info()`
Report the configured `SHELX_DIRECTORY` and which SHELX programs are present.
---
## Bundled reference data (offline)
The server does not call the web at runtime. It parses these bundled HTML files
(from https://shelx.uni-goettingen.de/) at startup:
| File | Programs covered |
|------|------------------|
| `data/shelxl_reference.html` | SHELXL refinement instructions |
| `data/shelxs_keywords.html` | SHELXS `.ins` solution cards |
| `data/shelxd_sm_keywords.html` | SHELXD `.ins` solution cards |
| `data/shelxe_keywords.html` | SHELXE command-line options |
To refresh the references, re-download the corresponding pages and replace the
files in `src/shelx_mcp/data/` (they are included in the wheel via
`force-include` in `pyproject.toml`).
---
## Project layout
```
shelx-mcp/
├── .env.example # template for SHELX_DIRECTORY
├── pyproject.toml # uv / hatchling project metadata
├── examples/
│ ├── crush.json # Crush MCP config
│ ├── opencode.jsonc # OpenCode MCP config
│ ├── claude_desktop.json # Claude Desktop config
│ ├── vscode_mcp.json # VS Code / Copilot (.vscode/mcp.json)
│ ├── zed.json # Zed context_servers
│ ├── cursor.json # Cursor .cursor/mcp.json
│ └── cline.json # Cline settings
├── skills/
│ └── shelx/
│ └── SKILL.md # agent skill: SHELX solve/refine workflow
├── src/shelx_mcp/
│ ├── server.py # FastMCP server + tools
│ ├── runner.py # safe subprocess execution of SHELX
│ ├── reference.py # parses the bundled SHELXL instruction HTML
│ ├── solution_ref.py # parses the bundled SHELXS/SHELXD/SHELXE references
│ ├── analysis.py # extracts R1/wR2/GooF/atoms/warnings
│ └── data/
│ ├── shelxl_reference.html # bundled, offline SHELXL manual
│ ├── shelxs_keywords.html # SHELXS .ins cards
│ ├── shelxd_sm_keywords.html # SHELXD .ins cards
│ └── shelxe_keywords.html # SHELXE command-line options
└── tests/
└── test_server.py
```
---
## Development
```bash
uv sync --extra test
uv run pytest -q # run the test suite (no real SHELX needed)
uvx ruff@0.11.0 check src tests # lint
```
Tests cover reference/solution parsing, output analysis, `validate_ins`, the
server tools, and (when `SHELX_DIRECTORY` is configured) an end-to-end SHELX run.
---
## Security notes
- Only the programs in `ALLOWED_PROGRAMS` (`shelxs`, `shelxl`, `shelxt`, `shelxc`,
`shelxd`, `shelxe`, `ciftab`) can be executed — the `program` argument cannot be
abused to run arbitrary commands.
- The `basename` is restricted to `[A-Za-z0-9_.-]` and the values `.` / `..` are
rejected, so it cannot escape the working directory.
- Extra input files must be bare names with no directory components.
- Input files are written to a temporary directory that is removed after the run
(unless you pass `workdir` explicitly), so the server never writes into your
repo.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available