shelx-mcp
# 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 (SM & MM) / SHELXT / SHELXC / SHELXE solution-program
references** bundled offline (from https://shelx.uni-goettingen.de/), so the
model can look up what any command or solution keyword does without leaving the
conversation.
---
## What it does
The server exposes **12 tools** split into two coverage areas:
- **Refinement (SHELXL)** — the cards that build and refine a model.
- **Structure solution & phasing (SHELXS / SHELXD / SHELXT / SHELXC / 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 intrinsic phasing). |
| `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/SHELXD-MM `.ins` cards, SHELXT/SHELXE command-line options, or SHELXC stdin keywords. |
| `solution_help` | solution | Full signature + description for one solution keyword (e.g. `TREF`, `-aN`, `SAD`). |
| `validate_ins` | refinement | Check an `.ins` file for the required SHELXL header cards. |
| `analyze_results` | refinement | Extract R1, wR2, GooF (S), max shift/esd, atom count and warnings. |
| `analyze_solution` | solution | Extract R1, alpha, space group, Flack x, CC and atom count from SHELXT output. |
| `shelx_info` | both | Report the configured SHELX directory and available programs. |
> **Refinement vs. solution coverage.** SHELXL refines a model you already
> have; SHELXS/SHELXD/SHELXT find the model from diffraction data and
> SHELXC/SHELXE handle experimental phasing of macromolecules. Use
> `list_commands` / `command_help` / `search_commands` for refinement cards, and
> `list_solution_keywords` / `solution_help` for solution keywords.
### 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 (register and download from
https://shelx.uni-goettingen.de/ or via your site licence).
- [`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.
`/absolute/path/to/shelx-mcp`, or `C:/path/to/shelx-mcp` on Windows). 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 twelve 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", "/absolute/path/to/shelx-mcp", "run", "shelx-mcp"]
}
}
}
```
> `uv` must be on the agent's `PATH`. If it is not, replace `"uv"` with the
> absolute path to the `uv` binary (e.g. `C:/Users/you/.local/bin/uv.exe` on
> Windows, `~/.local/bin/uv` on Linux/macOS).
### 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 / intrinsic phasing — usually the better first choice for small
molecules). 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. SHELXT writes its listing to `name.lxt` and its
solution(s) to `name_a.res`, `name_b.res`, ...; both are returned inline.
### `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 keywords for `program` ∈ {`shelxs`, `shelxd`, `shelxd_mm`, `shelxt`,
`shelxc`, `shelxe`}: `.ins` cards for SHELXS/SHELXD/SHELXD-MM, command-line
switches for SHELXT/SHELXE, and stdin keywords for SHELXC.
### `solution_help(program, keyword)`
Return the signature and full description for one solution keyword. For
SHELXS/SHELXD/SHELXD-MM use the card name (e.g. `solution_help("shelxs",
"TREF")`, `solution_help("shelxd_mm", "DSUL")`); for SHELXT/SHELXE use the
switch including its dash and placeholder (e.g. `solution_help("shelxt",
"-a")`, `solution_help("shelxe", "-aN")`); for SHELXC use the stdin keyword
(e.g. `solution_help("shelxc", "SAD")`). SHELXT/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.
### `analyze_solution(lxt=None, res=None)`
Summarise a SHELXT solution run from the `.lxt` listing and/or a `name_a.res`
solution model: R1, alpha (phase error), assigned space group, Flack x, CC,
Chem and atom count. Useful for judging whether an intrinsic-phasing run
produced an acceptable solution before starting refinement.
### `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` cards (small molecules) |
| `data/shelxd_mm_keywords.html` | SHELXD `.ins` cards (macromolecular substructure) |
| `data/shelxt_keywords.html` | SHELXT command-line switches |
| `data/shelxc_keywords.html` | SHELXC stdin keywords |
| `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
├── AI.md # technical summary for AI assistants
├── 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/D/T/C/E references
│ ├── analysis.py # extracts R1/wR2/GooF/atoms/warnings/solution metrics
│ └── data/
│ ├── shelxl_reference.html # bundled, offline SHELXL manual
│ ├── shelxs_keywords.html # SHELXS .ins cards
│ ├── shelxd_sm_keywords.html # SHELXD .ins cards (small molecules)
│ ├── shelxd_mm_keywords.html # SHELXD .ins cards (macromolecular)
│ ├── shelxt_keywords.html # SHELXT command-line switches
│ ├── shelxc_keywords.html # SHELXC stdin keywords
│ └── 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 (all six solution programs), output
analysis (SHELXL and SHELXT), `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.
---
## Disclaimer
> **AI-generated project.** This codebase was created with the assistance of
> large language models and is provided **as-is, without warranty of any
> kind**. While the SHELX executables themselves are the authoritative tools
> from the Sheldrick group, the wrappers, parsers and documentation here may
> contain errors. Always verify crystallographic results independently (e.g.
> with checkCIF and by inspecting the full `.lst` output), and report issues
> on the GitHub issue tracker.
>
> SHELX is © George M. Sheldrick; this project merely wraps the SHELX console
> programs and bundles excerpts of the official documentation pages from
> https://shelx.uni-goettingen.de/ for offline lookup. You must obtain your own
> licensed copy of the SHELX programs — they are **not** included here.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 12 tools
Most tools have distinct purposes: run_shelx is a generic runner, while solve and refine are specialized wrappers that could be confused with it but are clearly described. The pairs list_commands/list_solution_keywords and command_help/solution_help are differentiated by domain (refinement vs. solution), but agents might initially pick the wrong one.
Tool names are predominantly snake_case and verb-first (search_commands, validate_ins, analyze_results), but a few are noun-first (command_help, solution_help, shelx_info). The pattern is mostly consistent and readable, with minor deviations.
With 12 tools, the server is well-scoped for the SHELX crystallography domain. Each tool covers a distinct aspect (running programs, getting help, validating input, analyzing output), and the count is within the ideal 3-15 range without feeling excessive.
The tool surface covers the full workflow: searching/listing/help for commands and keywords, running any SHELX program, dedicated solve/refine wrappers, input validation, output analysis, and environment reporting. No obvious gaps that would block a typical crystallographic workflow.