louvain
Detect communities in single-cell RNA sequencing data using the Louvain clustering algorithm to identify cell types and functional groups.
Instructions
Louvain clustering algorithm for community detection
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| resolution | No | For the default flavor ('vtraag') or for 'RAPIDS', you can provide a resolution (higher resolution means finding more and smaller clusters), which defaults to 1.0. | |
| random_state | No | Change the initialization of the optimization. | |
| key_added | No | Key under which to add the cluster labels. | louvain |
| flavor | No | Package for computing the clustering: 'vtraag' (default, more powerful), 'igraph' (built-in method), or 'rapids' (GPU accelerated). | vtraag |
| directed | No | Interpret the adjacency matrix as directed graph. | |
| use_weights | No | Use weights from knn graph. | |
| partition_kwargs | No | Key word arguments to pass to partitioning, if 'vtraag' method is being used. | |
| neighbors_key | No | Use neighbors connectivities as adjacency. If specified, louvain looks .obsp[.uns[neighbors_key]['connectivities_key']] for connectivities. | |
| obsp | No | Use .obsp[obsp] as adjacency. You can't specify both `obsp` and `neighbors_key` at the same time. |
Implementation Reference
- src/scmcp/tool/tl.py:164-177 (handler)Generic handler function that dispatches to sc.tl.louvain for the 'louvain' tool by inspecting signature and calling with provided arguments.def run_tl_func(ads, func, arguments): adata = ads.adata_dic[ads.active] if func not in tl_func: raise ValueError(f"Unsupported function: {func}") run_func = tl_func[func] parameters = inspect.signature(run_func).parameters kwargs = {k: arguments.get(k) for k in parameters if k in arguments} try: res = run_func(adata, **kwargs) add_op_log(adata, run_func, kwargs) except Exception as e: logger.error(f"Error running function {func}: {e}") raise return
- src/scmcp/schema/tl.py:344-414 (schema)Pydantic input schema model for the louvain tool, defining parameters like resolution, flavor, etc., with validators.class LouvainModel(JSONParsingModel): """Input schema for the Louvain clustering algorithm.""" resolution: Optional[float] = Field( default=None, description="For the default flavor ('vtraag') or for 'RAPIDS', you can provide a resolution (higher resolution means finding more and smaller clusters), which defaults to 1.0." ) random_state: int = Field( default=0, description="Change the initialization of the optimization." ) key_added: str = Field( default='louvain', description="Key under which to add the cluster labels." ) flavor: Literal['vtraag', 'igraph', 'rapids'] = Field( default='vtraag', description="Package for computing the clustering: 'vtraag' (default, more powerful), 'igraph' (built-in method), or 'rapids' (GPU accelerated)." ) directed: bool = Field( default=True, description="Interpret the adjacency matrix as directed graph." ) use_weights: bool = Field( default=False, description="Use weights from knn graph." ) partition_kwargs: Optional[Dict[str, Any]] = Field( default=None, description="Key word arguments to pass to partitioning, if 'vtraag' method is being used." ) neighbors_key: Optional[str] = Field( default=None, description="Use neighbors connectivities as adjacency. If specified, louvain looks .obsp[.uns[neighbors_key]['connectivities_key']] for connectivities." ) obsp: Optional[str] = Field( default=None, description="Use .obsp[obsp] as adjacency. You can't specify both `obsp` and `neighbors_key` at the same time." ) @field_validator('resolution') def validate_resolution(cls, v: Optional[float]) -> Optional[float]: """Validate resolution is positive if provided""" if v is not None and v <= 0: raise ValueError("resolution must be a positive number") return v @field_validator('obsp', 'neighbors_key') def validate_graph_source(cls, v: Optional[str], info: ValidationInfo) -> Optional[str]: """Validate that obsp and neighbors_key are not both specified""" values = info.data if v is not None and 'obsp' in values and 'neighbors_key' in values: if values['obsp'] is not None and values['neighbors_key'] is not None: raise ValueError("Cannot specify both obsp and neighbors_key") return v @field_validator('flavor') def validate_flavor(cls, v: str) -> str: """Validate flavor is supported""" if v not in ['vtraag', 'igraph', 'rapids']: raise ValueError("flavor must be one of 'vtraag', 'igraph', or 'rapids'") return v
- src/scmcp/tool/tl.py:54-59 (registration)Creates the MCP Tool instance for 'louvain' with name, description, and schema reference.# Add louvain tool louvain_tool = types.Tool( name="louvain", description="Louvain clustering algorithm for community detection", inputSchema=LouvainModel.model_json_schema(), )
- src/scmcp/tool/tl.py:145-162 (registration)Registers the louvain_tool in the tl_tools dictionary, which is provided to the MCP server.tl_tools = { "tsne": tsne_tool, "umap": umap_tool, "draw_graph": draw_graph_tool, "diffmap": diffmap_tool, "embedding_density": embedding_density_tool, "leiden": leiden_tool, "louvain": louvain_tool, "dendrogram": dendrogram_tool, "dpt": dpt_tool, "paga": paga_tool, "ingest": ingest_tool, "rank_genes_groups": rank_genes_groups_tool, "filter_rank_genes_groups": filter_rank_genes_groups_tool, "marker_gene_overlap": marker_gene_overlap_tool, "score_genes": score_genes_tool, "score_genes_cell_cycle": score_genes_cell_cycle_tool, }
- src/scmcp/server.py:35-56 (registration)MCP server.list_tools() decorator that includes tl_tools.values(), exposing the louvain tool to clients.@server.list_tools() async def list_tools() -> list[types.Tool]: if MODULE == "io": tools = io_tools.values() elif MODULE == "pp": tools = pp_tools.values() elif MODULE == "tl": tools = tl_tools.values() elif MODULE == "pl": tools = pl_tools.values() elif MODULE == "util": tools = util_tools.values() else: tools = [ *io_tools.values(), *pp_tools.values(), *tl_tools.values(), *pl_tools.values(), *util_tools.values(), *ccc_tools.values(), ] return tools