skill example

AI Agent Skill for cryoSPARC

A StructAgent example that gives Claude, Codex, or any tool-using agent three capabilities against a live cryoSPARC instance: consult on workflow and parameter questions, inspect completed-job results, and launch new jobs only with explicit confirmation.

Part of StructAgent Real skill folder: skills/annika/cryosparc Paper: bioRxiv 2026 Runtime-agnostic 44-file skill folder 35 on-demand Markdown references cryosparc-tools helper included

What the skill does

Consult

Answer cryoSPARC questions using a lean SKILL.md router plus 35 on-demand Markdown references covering parameters, workflows, masks, symmetry, orientations, troubleshooting, and RELION interop.

Inspect results

Pull a finished job’s assets through cryosparc-tools — FSC, cFSC, viewing-direction, B-factor, scale-factor — and summarise what they mean.

Launch jobs safely

Build, launch, and chain new jobs against a live cryoSPARC instance, with explicit user confirmation of project / workspace / lane / params before anything starts.

The skill is one folder of plain Markdown plus a Python helper. There is no hidden server component and no model-specific glue. It is independent and unofficial; authoritative details remain in the upstream cryoSPARC documentation and license terms.

Install

This unofficial skill ships as part of StructAgent — see the accompanying paper, StructAgent: Orchestrating Cryo-EM Model Building and Refinement with a Multi-Agent LLM System (bioRxiv 2026), for the broader system. The AI agent skill for cryoSPARC itself is self-contained and can be dropped into any coding-agent runtime that can load an instruction file and run shell or Python tools.

Actual skill files

The installable skill lives here: github.com/bhgtiger/StructAgent/tree/main/skills/annika/cryosparc. The folder now includes its own README.md with install, porting, site-config, and safety instructions.

Claude Code

$ git clone https://github.com/bhgtiger/StructAgent.git
$ mkdir -p ~/.claude/skills
$ cp -R StructAgent/skills/annika/cryosparc ~/.claude/skills/

Codex CLI / other agent runtimes

Point the runtime at the same folder; the only requirement is that it can read the files on demand and run an approved Python script.

$ cp -R StructAgent/skills/annika/cryosparc <your-agent-skills-dir>/

Resulting layout

cryosparc/
├── README.md                   # install, porting, site-config, safety instructions
├── SKILL.md                    # entrypoint, routing, safety rules
├── lessons.md                  # site/user lessons placeholder
├── references/                 # 32 markdown reference files
└── scripts/
    └── cryosparc_harness.py    # read-only by default; --commit to queue
ConceptMapping in any agent runtime
README.mdHuman-facing install, porting, local configuration, and safety instructions.
SKILL.mdSystem / tool instruction module loaded when the user asks about cryoSPARC.
references/On-demand reference files loaded only when relevant.
lessons.mdWritable memory for confirmed local lessons.
scripts/cryosparc_harness.pyTool wrapper. Read-only by default; queueing requires explicit flags.

Configure for your site

The skill becomes useful only after it learns the local deployment. Create a local, non-public site_config.md next to SKILL.md. Do not commit it.

# ~/.claude/skills/cryosparc/site_config.md
# Local file. Do not commit credentials.

## cryoSPARC instance
- master_url:           http://xxx:xxx
- master_version:       v5.x
- license_id:           xxx
- cryosparc_tools_env:  ~/.venvs/cs-tools-v5/bin/python
- default_lane:         xxx

## Access rules
- Read-only inspection first.
- Queue only after explicit user confirmation of project_uid,
  workspace_uid, lane, and params.
- Never store passwords or session tokens here.

Credentials are passed via environment variables at run time, never written into the skill folder:

export CRYOSPARC_HOST='xxx'
export CRYOSPARC_BASE_PORT='xxx'
export CRYOSPARC_EMAIL='xxx@xxx'
read -s CRYOSPARC_PASSWORD
export CRYOSPARC_PASSWORD

Example session

A short happy-path session against project Pxxx, showing the three capabilities back-to-back. The baseline is an existing local-refinement job, J{baseline}. A second scenario below shows the same skill running an entire cryoSPARC → RELION → cryoSPARC classification round-trip from a single instruction.

1. Consult — what parameters did this job change?

User
Check J{baseline} — what parameters did it change, and what could we try?
Agent

Pulled J{baseline}’s params via cryosparc-tools and diffed against defaults. 5 of 78 parameters were non-default:

ParamThis jobDefault
use_alignment_priorTrueFalse
sigma_prior_r10°15°
sigma_prior_s10 Å7 Å
refine_res_init3.6 Å12 Å
refine_scale_minTrueFalse

Reference 09_local_refinement.md gives a documented “small ROI” recipe of 3° / 1 Å priors + 9° / 3 Å explicit search extent. Three variants worth trying: tight (recipe values), medium (between recipe and current), reseed (same params, different random seed).

2. Check results — read the existing plots

User
What do J{baseline}’s plots say?
Agent

Downloaded the iteration-2 plots via job.download_asset() and read them:

  • GSFSC 3.22 Å with the auto-tightened mask (iter-0 3.05 Å with the input mask, no oscillation between halves).
  • cFAR 0.72, cone resolutions 2.95 Å (best) → 3.39 Å (worst). The 3.22 Å headline is a geometric average over an anisotropic map.
  • B-factor 106.5 Å2 from the Guinier plot — healthy for a ~3.2 Å map.
  • Per-particle scale factors are a clean near-Gaussian around 1.0 (mean 1.008), so refine_scale_min was safe.
  • Viewing-direction distribution has a hot spot and a sparse zone — the anisotropy is driven by preferred orientation, not by the local-refine settings.

The headline number is honest at the average; the limiting feature is the orientation distribution upstream, not the local-refine dialog. Two options: (a) accept the anisotropy and tune locally with the 3-variant sweep above, or (b) rebalance the orientation distribution upstream and re-run with the same params, to isolate the effect.

3. Run — rebalance, then redo the local refinement

User
Do option (b): rebalance, then redo the local refinement with the same params.
Agent

Confirming before queueing:

  • Step 1rebalance_3D on J{baseline}’s input particles, default params.
  • Step 2new_local_refine on step-1 output + same volume / mask as J{baseline}, params verbatim.
  • Workspace Wxxx, lane xxx (matches J{baseline}’s resources_allocated.lane).
  • Sequential with wait_for_done() between steps. ETA ~20 min.

OK to queue?

User
Yes.
Agent

Queued. Status:

JobTitleStatusNotes
J{baseline}baseline local refinecompletedGSFSC 3.22 Å, cFAR 0.72
J{rebalance}Rebalance Orientations✓ completed55,715 / 80,164 particles kept (69.5%)
J{redo}post-rebalance local refinecompletedfinished at 3.30 Å
cryoSPARC web UI tile view showing the rebalance and post-rebalance local-refine jobs completing, with FSC curves and density thumbnails.
cryoSPARC web UI — the rebalance and post-rebalance local-refine tiles after the protocol completed.

What this shows

One short conversation covered all three capabilities: pulling parameters and recommending alternatives (consult), downloading and interpreting job assets (check), and queueing a multi-step protocol after explicit confirmation (run). The agent never touched anything destructive without asking.

CryoSPARC - RELION full round trip in one prompt

The session above was three separate questions. This one is the opposite shape: a single instruction, after which the agent runs an entire cryoSPARC → RELION focused 3D classification → cryoSPARC loop on its own — export, classify, map the classes back, rebuild one refinement per class — and pauses exactly once, at the single step that starts compute on the instance. The starting point is a consensus refinement J{src} (~258k particles, box 400 @ xxx Å/px). The question: is one small region of the map fully ordered, or only present in a subset of particles?

User
Starting from my consensus refinement J{src}: I think one small region of the map is only ordered in part of the dataset. Using the focus mask xxx_mask.mrc, run a focused 3D classification on just that region in RELION with the cryoSPARC poses held fixed, then split the particles by class and bring each class back into cryoSPARC as its own whole-molecule refinement. Keep the native poses and CTF — don’t re-import the raw stacks.
Agent

Loaded the cryosparc skill (27_relion_interop.md for the metadata bridge, 23_external_jobs.md for carrying particle subsets back) plus the companion relion skill for the RELION binaries. This is a no-re-import round-trip: the classes get re-joined to the original particles by uid, so cryoSPARC poses + CTF stay native the whole way. Running the export, classification and build legs now; I’ll stop before anything queues on the instance.

1 · cryoSPARC → RELION. Exported J{src}’s particles with csparc2star.py (--boxsize 400), built a .mrcs symlink farm with absolute paths (the STAR says .mrcs while the data is .mrc on disk — the documented extension gotcha), and downsampled box 400 → 128 for classification (relion_image_handler). Re-softened the hard cryoSPARC mask for RELION (relion_mask_create --width_soft_edge 6).

2 · Focused Class3D, poses fixed. relion_refine --K 4 --skip_align over the masked region. First pass at low --tau2_fudge (T) collapsed — every particle fell into one class, the known small-mask failure mode — so I raised T; T=300 gave a balanced, reproducible 4-way split (a tiny mask needs a far higher T than a large one). RELION 5 runs --skip_align on CPU, so this leg used mpirun, ~2–3 min/iter × 25 iters.

ClassState at the siteFraction
c3both sub-features ordered38.5%
c4both sub-features weak54.6%
c2sub-feature 2 missing3.9%
c1sub-feature 1 weak3.0%

In-mask CC 0.54–0.78 across classes.

3 · Map the classes back to uid. Joined the RELION run_it025_data.star rows onto the cryoSPARC particles by (stack file, in-stack index) → uid. Every particle matched (the join aborts on any unmatched row), so each class is a clean subset of the originals — no stack re-imported, poses + CTF untouched.

4 · Built on cryoSPARC (no compute yet). One External Job wrote a particle subset per class by uid; the harness then built one nonuniform_refine_new per subset in dry-run — whole molecule, no focus mask, initial model = the J{src} volume, refinement params read back from J{src} (inspect-job --print-specs). Everything is in building status; nothing is running.

Confirm before compute

Ready to queue 4 subsets + 4 whole-molecule refinements into Pxxx / Wxxx, lane xxx (the lane J{src} used). ~15 min each. This is the only step that starts jobs on the instance — queue them?

User
Yes, queue them.
Agent

Queued (--commit --queue --queue-confirm QUEUE) and waited. The per-class refinements were run without a focus mask, so they are an independent check on the focused classification — and they reproduce the split:

Class → refineStateSite density (σ over local background)
J{src}consensus (all particles)both sub-features, averaged over states
c3 → J{r3}both ordered (38.5%)sub-feature 1 +2.1 / sub-feature 2 +2.8
c4 → J{r4}both weak (54.6%)sub-feature 1 −0.2 / sub-feature 2 −0.6

(The two minor classes, c1/c2 at 3.0% / 3.9%, refined too but are too small to read confidently.)

Conclusion: the region is not uniformly ordered — it is fully present in only ~38% of the particles, while in the majority class the density is essentially gone. Because the confirming refinements carried no focus mask, the effect is real, not a classification-mask artifact. All jobs are in Pxxx / Wxxx; the class–uid assignment is saved for reuse.

What this shows

One instruction drove a full cryoSPARC → RELION → cryoSPARC heterogeneity round-trip. The agent owned the multi-tool plumbing across two skills (STAR/MRCS conversion, downsample, mask soften, focused --skip_align classification, the uid re-join, the per-class rebuild), recovered from the low-T collapse on its own, and — the point of the whole exercise — kept the cryoSPARC poses native by mapping classes back by uid instead of re-importing stacks, stopping for confirmation only at the single step that starts compute.

Sanitized cryoSPARC project view showing completed External and Local Refine jobs for RELION class particle subsets, with density thumbnails, FSC plots, and completed-state cards.
cryoSPARC web UI — completed per-class External and Local Refine jobs after the RELION focused-classification round-trip.

Snippets

Connect from Python

import os
from cryosparc.tools import CryoSPARC

cs = CryoSPARC(
    license  = os.environ["CRYOSPARC_LICENSE"],  # xxx-xxx-xxx
    host     = os.environ["CRYOSPARC_HOST"],     # xxx
    base_port= int(os.environ["CRYOSPARC_BASE_PORT"]),  # xxx
    email    = os.environ["CRYOSPARC_EMAIL"],
    password = os.environ["CRYOSPARC_PASSWORD"],
)
assert cs.test_connection()

Inspect a job’s params and assets

project = cs.find_project("Pxxx")
job     = project.find_job("Jxxx")

# Parameters (full dict, including defaults)
params = job.doc["params_spec"]

# Asset listing (PNG / PDF plots, logs, ...)
for asset in job.list_assets():
    print(asset["filename"], asset["_id"])

# Download one asset
job.download_asset(asset_id, "/tmp/out.png")

Queue safely via the harness

# default: dry-run JSON only; no job is queued
$ python scripts/cryosparc_harness.py create-job \
    --project-uid Pxxx --workspace-uid Wxxx \
    --job-type new_local_refine \
    --params-json '{"use_alignment_prior": true}'

# real queue only with explicit commit + queue + lane + confirmation word
$ python scripts/cryosparc_harness.py create-job \
    --project-uid Pxxx --workspace-uid Wxxx \
    --job-type new_local_refine \
    --lane xxx --commit --queue --queue-confirm QUEUE