Cryosparc_mcp_Server
by yaswanth169
README.md
# Cryosparc_mcp_Server
Mode A MCP server for running the standard CryoSPARC W1->W2 pipeline from natural-language clients.
## What it does
- Exposes MCP tools to:
- start standard pipeline
- pause at interactive checkpoints
- resume after UI interaction
- check status
- generate report
- Uses a state file per run so disconnects/restarts can resume.
## Quick start
```bash
cd Cryosparc_mcp_Server
python3 -m pip install --user -r requirements.txt
cp config/profile.example.yaml config/profile.yaml
```
Update `config/profile.yaml` with your CryoSPARC details and dataset path.
## Run MCP server (stdio)
```bash
python3 -m src.cryosparc_mcp_server.server --config config/profile.yaml
```
Use this command as the MCP server command in Claude/Cursor MCP settings.
## Why the MCP “goes down” when Cursor or SSH drops
**The MCP process is tied to Cursor’s stdio pipe.** It is **not** a background daemon. If Cursor times out, closes the SSH session, or the jump host resets the connection, that Python process **exits**. That is normal.
**CryoSPARC jobs keep running** on the cluster/master — only the **bridge** (MCP + `cryosparc-tools`) stops. Your pipeline state is still in `runs/<run_id>.json` on the machine where the MCP ran. After reconnecting MCP, use **`cs_pipeline_status`** / **`cs_resume_pipeline`** / **`cs_continue_pipeline`** with the same `run_id`.
### Long workflows: “MCP should wait” — who actually times out?
**Your CryoSPARC MCP server *does* wait** for each tool until that tool’s Python code finishes (or errors). Nothing in this repo artificially cuts off a long `subprocess` or CryoSPARC call.
What stops “waiting” is almost always the **MCP client in Cursor** (and sometimes **SSH**), not the server:
- Cursor tracks each **tool call** and, after an internal limit, sends **`Request timed out`** (`-32001`) and tears down that stdio session.
- That closes stdin → the remote `run_server.py` hits **`BrokenResourceError`** even though CryoSPARC may still be busy on the cluster.
So “MCP should wait” = **you need the client (Cursor) to allow a longer tool call**, or **avoid one giant tool call** until Cursor supports it reliably.
#### Try a longer timeout in Cursor (version-dependent)
Open **Command Palette → “Preferences: Open User Settings (JSON)”** and try adding (values in **milliseconds**):
```json
"mcp.server.timeout": 600000,
"mcp.elicitation.timeout": 600000
```
Restart Cursor. **Names and support change by Cursor version**; if those keys are ignored, check [Cursor forum / release notes](https://forum.cursor.com/) for “MCP timeout”. There is no knob inside `Cryosparc_mcp_Server` that replaces this.
#### Reliable pattern when timeouts stay short
Until the client waits as long as your jobs:
1. **`cs_start_pipeline`** then repeated **`cs_continue_pipeline`** with **`steps=1`** — each HTTP-ish “step” returns sooner so a single MCP call is less likely to exceed Cursor’s limit.
2. Or run the heavy **`Tools_Cryosparc_Workflow`** stages in **`tmux`** on the server and use MCP only for **`cs_pipeline_status`** / **`cs_list_runs`** (short calls).
#### Future server-side option (design)
A **“start job, return immediately, poll status”** tool shape (async + `run_id`) would match how long HPC workflows work under strict client timeouts. Today, **`cs_start_pipeline` / `cs_continue_pipeline`** are the closest match to that pattern.
---
### Cursor “Request timed out” on long tools (summary)
Calls like **`cs_plan_and_run_standard_pipeline`** or **`cs_resume_pipeline`** can run **many minutes** (import, motion, CTF, etc.). Cursor’s MCP client often **times out around ~2–3 minutes** (varies by version) while the server is still working. After timeout you may see `BrokenResourceError` / `Connection closed` in logs — the remote Python then exits when stdin closes.
**Mitigation:** Increase Cursor timeouts if your build supports it (above), and/or prefer **`cs_start_pipeline`** + **`cs_continue_pipeline`** with `steps=1`. Or run heavy steps from **`tmux`** on the server with the CLI workflow, and use MCP only for short checks.
### SSH + `"declare -x" is not valid JSON` (red “Error” in Cursor)
MCP **must** get **only JSON-RPC on stdout**. Anything else breaks parsing → Cursor shows **Error** / **Show Output**.
**Causes:**
1. **`bash -lc` (login shell)** — can run `~/.bash_profile` and dump `declare -x` to stdout. **Do not use `-l`.**
2. **Even `bash -c`** — if `~/.bashrc` / system bashrc runs `export -p`, `set -x`, or similar, you can **still** get `declare -x` on stdout.
**Fix (strong):** avoid **bash** for the remote wrapper — use **`/bin/sh -c`** (usually **dash** on Ubuntu/Debian) so you do not hit bash’s `BASH_ENV`, login hooks, or `export -p` spam. Also add **`-T`** so SSH does not allocate a TTY (fewer surprises from interactive startup).
```json
"args": [
"-o", "ServerAliveInterval=60",
"-T",
"-J", "USER@jump:20022",
"-p", "20022",
"USER@10.0.1.2",
"/bin/sh", "-c",
"export CRYOSPARC_EMAIL='you@example.com' && export CRYOSPARC_PASSWORD='...' && cd /mnt/.../Cryosparc_mcp_Server && exec python3 -u run_server.py --config config/profile.yaml"
]
```
If you still see `declare -x` after this, your site may run **`bash -l`** (or similar) **before** your command. Then nothing on the client can fully fix stdout pollution — you need **`~/.bash_profile` / `~/.profile`** to **not** print or `export -p` to **stdout** (only stderr), or a dedicated account without noisy profile scripts.
**Fallback:** `bash --noprofile --norc -c '...'` is still worth trying if `/bin/sh` is linked to bash and behaves oddly.
Or the helper script (after `chmod +x` on the server):
```bash
chmod +x scripts/run_mcp_stdio.sh
```
```json
"bash", "--noprofile", "--norc", "-c",
"export CRYOSPARC_EMAIL='...' && export CRYOSPARC_PASSWORD='...' && exec /mnt/.../Cryosparc_mcp_Server/scripts/run_mcp_stdio.sh"
```
### Why it breaks **in the middle** of a run
Typical sequence in your logs:
1. **`cs_plan_and_run_standard_pipeline`** or **`cs_resume_pipeline`** runs **longer than Cursor’s MCP timeout** (~3 minutes) → `Request timed out`.
2. Cursor closes the request; the **SSH stdio pipe** dies → remote Python hits **`BrokenResourceError`** / **`Connection closed`**.
3. Cursor Settings shows **cryosparc-mode-a** as **Error** until you **toggle the server off and on** (starts a new `ssh`).
**CryoSPARC jobs often keep running**; your `runs/<run_id>.json` on the server still has the last state. After reconnecting MCP, use **`cs_pipeline_status`** with that `run_id`, then **`cs_continue_pipeline`** / **`cs_resume_pipeline`** — but use **short steps** (`steps=1`) so each tool call finishes **under ~3 minutes** when possible.
**Note:** Log lines like `INFO Processing request` may appear under **`[error]`** in Cursor’s MCP output; that is **Cursor’s log level**, not necessarily a Python exception.
### Keep SSH from idle-dropping
In `~/.ssh/config` on Windows:
```
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
```
## Tool flow (Mode A)
1. `cs_start_pipeline` -> starts run and executes one step immediately.
2. `cs_continue_pipeline` -> runs N additional steps (default 1).
3. `cs_resume_pipeline` -> continues after interactive checkpoint.
4. `cs_plan_and_run_standard_pipeline` -> convenience "run until next checkpoint/finish".
5. If checkpoint is returned, complete that interactive job in CryoSPARC UI.
6. `cs_pipeline_status` -> status anytime.
7. `cs_pipeline_report` -> final run report.
## Checkpoints vs the old CLI workflow
The `Tools_Cryosparc_Workflow` scripts use `input()` so the **terminal** waits until you press Enter after the CryoSPARC UI step.
The **MCP server cannot block on stdin** (stdio is used for the MCP protocol). So when `checkpoint_required` is true, the tool **returns immediately** with a message. **Cursor / the assistant** should:
1. Read `operator_instruction` and `checkpoint_message`.
2. **Stop** and tell you what to do in the CryoSPARC UI.
3. Wait until you say you are done (e.g. “continue”).
4. Call **`cs_resume_pipeline`** with the same `run_id` (not `cs_continue_pipeline` at interactive jobs).
Every summary now includes **`operator_instruction`**, **`next_suggested_tool`**, and **`next_suggested_args`** so the client knows exactly what to do next.
## Notes
- Interactive checkpoints: Curate Exposures, Inspect Picks (W1/W2), Select 2D (W1/W2).
- Pipeline definition is in `workflow_definitions/w1_w2_standard.yaml`.
- Run state is stored in `runs/`.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues