Contributing to ChatSpatial
May 1, 2026 · View on GitHub
Contributions are welcome — bug reports, new analysis methods, documentation improvements, and feature requests.
Getting Started
# Fork and clone
git clone https://github.com/YOUR_USERNAME/ChatSpatial.git
cd ChatSpatial
# Create environment and install
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
# Verify
pytest tests/unit/ -x
Prerequisites: Python 3.11-3.13, Git. For R-based methods (RCTD, CellChat, SPARK-X, etc.): R 4.4+ and rpy2.
Project Structure
chatspatial/
├── server.py # MCP tool definitions (entry point)
├── spatial_mcp_adapter.py # ToolContext and data manager
├── config.py # Runtime configuration
├── tools/ # Analysis implementations
│ ├── spatial_genes.py # SpatialDE, SPARK-X, FlashS
│ ├── spatial_domains.py # SpaGCN, STAGATE, GraphST, BANKSY, Leiden
│ ├── cell_communication.py # FastCCC, LIANA, CellPhoneDB, CellChat (`cellchat_r`)
│ ├── deconvolution/ # FlashDeconv, Cell2location, RCTD, etc.
│ ├── visualization/ # 11 plot types
│ └── ...
├── models/
│ ├── data.py # Pydantic parameter models
│ └── analysis.py # Pydantic result models
└── utils/
├── mcp_utils.py # @mcp_tool_error_handler decorator
├── exceptions.py # Custom exception classes
├── adata_utils.py # AnnData validation helpers
└── dependency_manager.py # Optional dependency checking
Adding a New Analysis Method
This is the most common contribution. Follow the existing pattern:
1. Parameter model (models/data.py)
class YourMethodParameters(BaseModel):
method: Literal["method_a", "method_b"] = Field(
default="method_a",
description="Which algorithm to use.",
)
n_top_genes: Optional[int] = Field(
default=None, description="Number of top genes to return."
)
2. Result model (models/analysis.py)
class YourMethodResult(BaseModel):
data_id: str
method: str
n_genes_analyzed: int
results_key: Optional[str] = None
3. Tool implementation (tools/your_tool.py)
from ..utils.exceptions import DataError, ProcessingError
from ..utils.dependency_manager import require
async def your_method(
data_id: str,
ctx: "ToolContext",
params: YourMethodParameters,
) -> YourMethodResult:
"""Implement your analysis."""
require("optional_package") # Checks at runtime, clear error if missing
adata = await ctx.get_adata(data_id)
# ... analysis logic ...
return YourMethodResult(...)
4. Register in server.py
@mcp.tool()
@mcp_tool_error_handler()
async def your_tool(
data_id: str,
params: Optional[YourMethodParameters] = None,
context: Optional[Context] = None,
) -> YourMethodResult:
"""Brief description for LLM tool selection."""
ctx = ToolContext(_data_manager=data_manager, _mcp_context=context)
p = _resolve_params(params, YourMethodParameters)
return await your_method(data_id, ctx, p)
5. Add tests
# tests/unit/test_your_tool.py
@pytest.mark.asyncio
async def test_your_method_basic(minimal_spatial_adata, monkeypatch):
# Mock external dependencies, test logic
...
Checklist
- Parameter model with Pydantic validation
- Result model following existing patterns
- Implementation using
ToolContext(not raw data_store dict) - Optional dependencies handled via
require() - MCP tool registered with
@mcp_tool_error_handler() - Unit tests with mocked dependencies
- Docstrings on public functions
Code Style
# Format and lint
black chatspatial/
isort chatspatial/
ruff check chatspatial/ --fix
# Type check
mypy chatspatial/
- Max line length: 88 (Black default)
- Type hints on all public functions
- Imports: stdlib, third-party, local (isort handles this)
Testing
pytest tests/unit/ # Fast, no external deps
pytest tests/integration/ # Multi-component workflows
pytest tests/e2e/ # Full MCP tool calls
# Pre-PR quality gate
make test-gates
- Unit tests: mock external packages, test logic in isolation
- Integration tests: test tool dispatch and result storage
- Keep test data small (<1000 spots, <500 genes)
- Set random seeds for reproducibility
Submitting Changes
- Create a branch:
git checkout -b feature/your-feature - Make changes, run tests and linting
- Commit with clear messages:
feat: add X method for Y analysis - Open a PR against
main
Commit style
feat: add new spatial analysis method
fix: handle edge case in deconvolution
docs: update methods reference
test: add integration test for trajectory
Reporting Issues
- Bugs: include a minimal reproducible example, error traceback, and
pip show chatspatialoutput - Feature requests: describe the use case and suggest which tool category it fits
Questions?
Open a GitHub Discussion or check the docs.