CLI (cellucid)#
The cellucid command-line interface is a single entry point for common workflows.
Its commands:
serve, which serves any file or folder Cellucid readsmcp, a local MCP server that lets an agent app on this computer (Claude Code, Claude Desktop, Cursor) work in a viewer (Local agents (cellucid mcp))
If you prefer Python APIs, see Server (browser tab + local HTTP server) and Jupyter (notebook embedding + hooks).
Fast path (beginner-friendly)#
# Serve an AnnData file or store (format detected)
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset
cellucid serve /path/to/data.zarr --dataset-name "My dataset" --dataset-id my-dataset
cellucid serve /path/to/data.zarr.zip --dataset-name "My dataset" --dataset-id my-dataset
Command reference#
cellucid --version#
cellucid --version
cellucid serve#
cellucid serve <data_path> [--port 8765] [--host 127.0.0.1] [--no-browser] [--quiet] [--verbose] [dataset and launch options]
<data_path> can be:
.h5adfileZarr store folder (Zarr v2 with
.zgroup, or v3 withzarr.json), AnnData or MuDatazipped Zarr store (
.zip, for exampledata.zarr.zip).h5mufile.loomfile10x Cell Ranger
.h5file, or a Cell Rangerouts/folderMatrixMarket folder (a
.mtx[.gz]file, whatever its name:matrix.mtx.gz, GEO’sGSM4567_matrix.mtx.gzorGSE158055_covid19_counts.mtx.gz, with itsbarcodesandfeaturesorgeneslists)R object:
.rds,.rda,.RData, gzipped or not (.rds.gz)
Cellucid’s own files are not datasets, and are refused before anything is
served with the sentence the web app says for them (the registry’s
cellucidFiles): a .cellucid-session file is the history of one dataset
(open its dataset, then load it in the web app, or read it with
cellucid.read_session()), and a .cellucid-library file holds datasets of
the web app’s My datasets (import it there).
An h5Seurat file is not opened, so it is refused before anything is served,
with the route that converts it: in R, read it with
SeuratDisk::LoadH5Seurat() and save it with saveRDS(), then serve the
.rds (or show the object with cellucid::cellucid_view()). A gzip-compressed
copy of a file read in parts (.h5ad.gz, .loom.gz, .h5.gz, .h5mu.gz, as
GEO ships many) is refused too, with how to decompress it (gunzip): a served
file is read in parts, which a gzip stream has none of. The web app itself
opens such a copy from a link or from this computer, reading it whole once.
A file written by cellucid.prepare() declares its own name and id, so
cellucid serve pbmc_cellucid.h5ad needs no --dataset-name/--dataset-id;
any other dataset is named after its file (its id the file’s name without the
extension) unless they name it. A file
prepare() wrote is served as it is on disk, and the viewer reads it itself
with HTTP range requests; any other AnnData, MuData, Loom or 10x file (or
outs/ folder) is served as a Zarr store in priority order, made from the file
as the viewer reads it and holding what the viewer presents from the file
itself; an R object or a MatrixMarket folder is served as it is and read
once from the start.
Options#
Option |
Meaning |
Notes |
|---|---|---|
|
Port to bind |
Use a different port if 8765 is busy; |
|
Host to bind to |
Default |
|
Extra exact |
Needed only behind a reverse proxy; one bare DNS name or IP address, no port, scheme, or wildcard |
|
Bytes a store served in priority order keeps of what it makes (renumbered genes), least recently used first out |
Default a 64th of the machine’s memory; |
|
Don’t auto-open a browser tab |
Helpful on remote servers |
|
Minimal output |
Mutually exclusive with |
|
Debug logging |
Useful for troubleshooting; also prints the traceback for any failure |
|
Serve the data endpoints without establishing the web application |
For machines with no outbound network access |
|
Exact origin publishing |
|
|
Directory for the active verified web build |
|
|
The |
never a 2D/3D layout; without it the file’s own |
|
The fewest cells a category needs for the outlier filter to measure it |
without it the file’s own |
|
Set the display name |
Defaults to the store’s |
|
Set the stable dataset identifier |
Defaults to the store’s |
|
Show this |
every column is shown when omitted |
|
The vector field the overlay opens on: |
|
|
The modality a multimodal dataset (a MuData) opens on |
without it the file’s own |
|
The |
|
|
Scale every embedding into the viewer’s coordinate range, or show the coordinates the store holds |
without either, the file’s own |
|
The |
read only when a view draws it |
|
The |
the var index when omitted |
|
Where gene expression is read |
Every AnnData launch option is checked against the store’s metadata before the
server binds — a named obsm, obsp, layer, or column must exist — and handed
to the viewer as a launch default. An option left out is taken from the store’s
own uns['cellucid']['defaults'], else the viewer applies its default rule
(Data mapping). For a format only the
browser reads (an R object, Loom, a 10x file or folder, a MatrixMarket folder),
the server checks each option’s form only (an embedding’s columns against its
dimension), and the viewer checks it against the data when it opens it.
A flag overrides the store’s own uns['cellucid']['defaults'] for this server
only; the file is not changed.
cellucid mcp#
cellucid mcp [--open PATH | --viewer NAME] [--port N] [--no-browser] [--web-source-url URL] [--web-cache-dir PATH] [--verbose]
A local MCP server over stdio for an agent app on this computer (Claude
Code, Claude Desktop, Cursor); the app starts it (Local agents (cellucid mcp),
Local agents (MCP): Claude Code, Claude Desktop, Cursor). It needs
pip install 'cellucid[mcp]'.
no option: drive the newest viewer open on this computer (
cellucid serve, a notebook’sshow_anndata, a script’sAnnDataViewer, R’scellucid_view()orcellucid_serve()), found again whenever the one it drives stops or its page closes while another viewer’s is open;--viewer NAME: drive the viewer with this dataset id, dataset name or viewer id;--open PATH: serve this file or folder in a viewer of its own and open it in the browser (--no-browser: print its address on stderr;--port: its port); the viewer stops with the server.
stdout is the MCP channel; everything the command says goes to stderr.
Startup output#
Startup runs four numbered steps: [1/4] Detecting format,
[2/4] Reading metadata, [3/4] Checking launch choices, [4/4] Starting server. The transcript is in Server Mode (CLI + Python).
Steps 1 to 3 read metadata only, never an array, so they take about the same
time on an atlas as on a small file. Step 2 prints the cell and gene counts and
the obsm, obs, obsp, and layers entries of an AnnData or MuData (for a
format only the browser reads it prints Contents: read by the browser when it opens the data); step 3 prints each launch choice it accepted.
Embeddings: which obsm key each dimension came from#
--embedding-1d, --embedding-2d, and --embedding-3d name the array of a slot,
optionally with 0-based columns (--embedding-2d X_pca:0,1). Every other slot
follows the default rule: X_umap_1d, X_umap_2d, and X_umap_3d each fill
their own slot, and when an object declares none of them, the first 1- to
3-column UMAP under a conventional name — X_umap (what sc.tl.umap() writes),
X_UMAP (a SingleCellExperiment’s), X_wnn.umap or X_ref.umap (a Seurat
object’s) — fills the slot of its own column count. When neither
fills any slot, the viewer makes a deterministic best guess from the other
obsm arrays and reports it as guessed. Nothing is renamed and the object is
not modified. When nothing can be plotted, the viewer reports Cellucid found nothing to plot…, listing every obsm entry and why it was not used.
--knn KEY#
Names the obsp matrix the viewer draws as the neighbour graph. Without it the
viewer draws obsp['connectivities'] when the store has one. Either way the
graph is read the first time a view draws edges, never at startup.
Naming a matrix the store does not carry is an error before the bind, not
silence — compute it with sc.pp.neighbors(adata) first, or drop the flag. A
matrix that is not symmetric, such as obsp['distances'], is refused by the
viewer with that reason, never symmetrized.
The Python equivalent is the keyword-only knn_key="connectivities" on
serve_anndata(), show_anndata(),
AnnDataViewer, and AnnDataServer; see
Server (browser tab + local HTTP server). prepare() stores one graph,
obsp['connectivities'], from its own connectivities= argument; the viewer’s
default rule finds it without --knn.
Practical workflows#
Remote server (HPC) via SSH tunnel#
On the remote machine:
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset --no-browser --host 127.0.0.1 --port 8765
On your laptop:
ssh -L 8765:127.0.0.1:8765 user@remote
Then open the Viewer URL the banner printed:
http://127.0.0.1:8765/?viewer=true&viewerId=…&viewerToken=…&anndata=true
Always copy the exact Viewer URL from the server banner: its viewerId and
viewerToken open the page’s viewer channel, which cellucid mcp drives
(Local agents (cellucid mcp)). http://127.0.0.1:8765/?anndata=true opens the same dataset
without it.
Serving from an HPC compute node#
A tunnel to a cluster terminates on the login node, and the login node opens the second connection to the compute node. The server therefore has to accept a connection that does not come from its own machine:
# on the compute node
cellucid serve /path/to/data.h5ad --dataset-name "My dataset" --dataset-id my-dataset \
--host 0.0.0.0 --no-browser
# on your laptop
ssh -N -L 8765:<compute-node>:8765 <username>@<login-node>
Open the loopback Viewer URL the banner printed. Full treatment — Slurm
batch jobs, tmux, port choice, large objects on shared storage, and the tunnel
failure table: HPC clusters: Slurm, compute nodes, and --host 0.0.0.0.
Tunnels in general, including firewalls, JupyterHub, and cloud VMs:
Remote servers, SSH tunneling, and cloud.
Exit codes#
0: success (or help text)1: error (e.g., path not found, format not detected)130: interrupted by user (Ctrl+C)
What a failure prints#
The last line always begins Error: and carries the whole message — what
failed and what to change. Conditions you can correct (a missing flag, a taken
port, an unbindable host, an input that is not a dataset, an optional package
that is not installed) print that line and nothing else.
A Python traceback above the Error: line means Cellucid itself failed; that
line then names the issue tracker and asks you to include the traceback. Add
--verbose to see the traceback for any failure.
A missing import names the condition it actually is#
An import that fails is not one condition, and the advice differs:
Condition |
What the |
|---|---|
The distribution is not installed in this environment |
|
The module imported, but a name Cellucid needs is not in it |
|
A standard-library module this interpreter cannot provide |
The running Python version, the interpreter’s own path, and the Python range this Cellucid declares. No |
Troubleshooting#
Symptom: “Unable to detect format”#
Fix:
Provide a path that is:
a file whose name a format names (
.h5ad,.zip,.h5mu,.loom,.h5,.rds[.gz],.rda[.gz],.RData[.gz]), ora Zarr store folder (with
.zgrouporzarr.jsonat its root), ora Cell Ranger
outs/or MatrixMarket folder.
Symptom: “This is an h5Seurat file (SeuratDisk), which Cellucid does not open”#
Fix:
In R:
seu <- SeuratDisk::LoadH5Seurat(path), thensaveRDS(seu, "object.rds"), and serve the.rds.
Symptom: “Permission denied” / “Address already in use”#
Fix:
Choose a different port:
--port 9000.
Symptom: “zsh: command not found: cellucid”#
Fix:
Ensure the package is installed in the current environment:
pip install cellucidIf you’re inside a virtualenv/conda env, activate it before running
cellucid.The equivalent module invocation is
python -m cellucid.cli --version.
Symptom: “The server is reachable but the browser didn’t open”#
Fix:
Remove
--no-browser.Or open the printed URL manually.
Symptom: “The browser cannot open http://0.0.0.0:8765”#
Fix:
0.0.0.0is a bind address, not a destination. Open the Viewer URL the banner printed — loopback for a tunnel, theMachine URLblock from another machine on the same network.
Symptom: “The graph is not drawn”#
Fix:
Read the
obsp:line of step 2. A graph stored under a key other thanconnectivitiesis drawn when you name it with--knn KEY.No
obspentry at all means the object has no graph: compute one withsc.pp.neighbors(adata)before serving.
See also#
Server (browser tab + local HTTP server) for the Python server APIs (
serve_anndata,AnnDataServer)