hb-projectome-mcp
README.md
# hb-projectome-mcp
[](https://github.com/tanyabudhiraja/hb-projectome-mcp/actions/workflows/tests.yml)
An [MCP](https://modelcontextprotocol.io) server that lets AI agents answer questions about a single-cell atlas of the zebrafish dorsal habenula, using only the data, with guardrails against made-up or overstated results.
Ask an agent *"Is KCTD12.1 a marker for cluster 9?"* and it calls these tools, gets normalized statistics with effect sizes, FDR-corrected q-values, confidence intervals, caveats and provenance, and can plot the gene on the UMAP.
The data is the [Halpern Lab habenula projectome database](https://github.com/tanyabudhiraja/hb-projectome-db): scRNA-seq of ~4,300 adult zebrafish dHb neurons in 14 clusters, each assigned a classifier-predicted projection target in the interpeduncular nucleus (IPN).

## Tools
| Tool | Answers |
|---|---|
| `list_clusters` | The 14 clusters, cell counts, and most common predicted IPN target |
| `get_cluster_projections` | Predicted IPN target distribution for one cluster, with 95% CIs |
| `search_genes` | Gene symbols matching a query (max 20) |
| `get_gene_expression_by_cluster` | % of cells expressing a gene (95% CI) and mean normalized expression, per cluster |
| `get_gene_ipn_targets` | Where a gene's expressing neurons are predicted to project, and enrichment over the dHb baseline |
| `compare_gene_between_clusters` | Exploratory two-cluster test: p-value, rank-biserial effect size, detection difference with 95% CI |
| `test_candidate_markers` | Up to 10 candidate markers for a cluster vs all other cells, Benjamini-Hochberg FDR across the list |
| `plot_gene_umap` | UMAP colored by a gene's normalized expression (PNG) |
Resource **`dataset://card`**: what the data is, how it was made, and what it can't tell you. The server instructs agents to read it first.
## Guardrails
| Risk | What the server does |
|---|---|
| Hallucinated genes or clusters | Exact matching only. Unknown names return `[UNKNOWN_GENE]` with close matches (`GNG9` → "Did you mean: GNG8?"); nothing is guessed. |
| Predictions read as facts | IPN targets are XGBoost predictions with no saved confidence. Every projection answer says so and lists what it does *not* support. |
| Depth-confounded comparisons | A one-time index of per-cell UMI totals enables log1p(CP10K) normalization. Without it, answers fall back to raw counts and say so. |
| Significant but trivial results | Every test reports a rank-biserial effect size. "Significant but negligible" (\|r\| < 0.1) is called out as not a real difference, which matters with thousands of cells. |
| Multiple comparisons | Multi-gene tests use Benjamini-Hochberg FDR; the response notes that only the listed genes were tested. |
| Pseudoreplication | Cells from one dataset aren't independent replicates; every test is labeled exploratory. |
| Small samples | Projection summaries need ≥ 10 expressing cells; tests need ≥ 20 cells per group. Below that, the server refuses with `[TOO_FEW_CELLS]`. |
| Overreach | Each answer carries `supports` / `does_not_support` lists, e.g. expression ≠ protein, projection ≠ function, absence of detection ≠ absence. |
| Expensive or open-ended compute | Read-only; at most 10 genes per call; genome-wide scans refused; per-gene LRU cache. |
| Malformed or injected input | Pydantic schemas validate every argument (gene symbols must match `^[A-Za-z0-9._-]+$`) before any code runs. |
| Untraceable answers | Every answer includes provenance: data fingerprint (SHA-256 of the source tables), files read, normalization, parameters, server version, timestamp. Every call is audit-logged as a JSON line. |
## Setup
```bash
git clone https://github.com/tanyabudhiraja/hb-projectome-db # the data
git clone https://github.com/tanyabudhiraja/hb-projectome-mcp
cd hb-projectome-mcp
pip install -e .
# one-time, about 2 minutes: per-cell UMI totals for normalization
HB_DATA_DIR=../hb-projectome-db hb-projectome-mcp build-index
```
Add it to an MCP client. For Claude Desktop, in `claude_desktop_config.json`:
```json
{
"mcpServers": {
"hb-projectome": {
"command": "hb-projectome-mcp",
"env": { "HB_DATA_DIR": "/absolute/path/to/hb-projectome-db" }
}
}
}
```
| Variable | Purpose | Default |
|---|---|---|
| `HB_DATA_DIR` | Path to the hb-projectome-db clone | current folder |
| `HB_CACHE_DIR` | Where the normalization index is stored | `~/.cache/hb-projectome-mcp` |
| `HB_AUDIT_LOG` | JSON-lines audit log file | stderr |
## Tests
```bash
pip install -e ".[dev]"
pytest
```
33 tests on a 4-gene slice of the real data (`tests/fixtures/`), covering:
- statistics checked against published values (Wilson interval; Newcombe 1998 difference CI) and statsmodels (BH)
- every refusal path and error code
- provenance on every answer
- an end-to-end run that starts the server over stdio, calls it with an MCP client, and checks schema rejection of injected input and the audit log
## Layout
```
src/hb_mcp/data.py data access, normalization and guardrails (plain Python, no MCP)
src/hb_mcp/stats.py Wilson and Newcombe CIs, Mann-Whitney with rank-biserial r, BH FDR
src/hb_mcp/server.py MCP tools, input schemas, dataset card, audit log, CLI
tests/ unit, statistics and stdio end-to-end tests
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues