scmcp-shared 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. scmcp_shared-0.3.0/.github/release.yml +32 -0
  2. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/PKG-INFO +1 -1
  3. scmcp_shared-0.3.0/src/scmcp_shared/__init__.py +3 -0
  4. scmcp_shared-0.3.0/src/scmcp_shared/cli.py +96 -0
  5. scmcp_shared-0.3.0/src/scmcp_shared/schema/__init__.py +22 -0
  6. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/schema/io.py +4 -8
  7. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/schema/pl.py +2 -3
  8. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/schema/pp.py +15 -15
  9. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/schema/tl.py +18 -24
  10. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/schema/util.py +11 -11
  11. scmcp_shared-0.3.0/src/scmcp_shared/server/__init__.py +13 -0
  12. scmcp_shared-0.3.0/src/scmcp_shared/server/base.py +153 -0
  13. scmcp_shared-0.3.0/src/scmcp_shared/server/io.py +84 -0
  14. scmcp_shared-0.3.0/src/scmcp_shared/server/pl.py +315 -0
  15. scmcp_shared-0.3.0/src/scmcp_shared/server/pp.py +325 -0
  16. scmcp_shared-0.3.0/src/scmcp_shared/server/tl.py +398 -0
  17. scmcp_shared-0.3.0/src/scmcp_shared/server/util.py +251 -0
  18. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/util.py +102 -42
  19. scmcp_shared-0.2.1/src/scmcp_shared/__init__.py +0 -3
  20. scmcp_shared-0.2.1/src/scmcp_shared/schema/__init__.py +0 -1
  21. scmcp_shared-0.2.1/src/scmcp_shared/schema/base.py +0 -11
  22. scmcp_shared-0.2.1/src/scmcp_shared/server/__init__.py +0 -51
  23. scmcp_shared-0.2.1/src/scmcp_shared/server/io.py +0 -79
  24. scmcp_shared-0.2.1/src/scmcp_shared/server/pl.py +0 -324
  25. scmcp_shared-0.2.1/src/scmcp_shared/server/pp.py +0 -368
  26. scmcp_shared-0.2.1/src/scmcp_shared/server/tl.py +0 -432
  27. scmcp_shared-0.2.1/src/scmcp_shared/server/util.py +0 -251
  28. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/.github/workflows/publish.yml +0 -0
  29. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/LICENSE +0 -0
  30. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/README.md +0 -0
  31. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/pyproject.toml +0 -0
  32. {scmcp_shared-0.2.1 → scmcp_shared-0.3.0}/src/scmcp_shared/logging_config.py +0 -0
@@ -0,0 +1,32 @@
1
+ changelog:
2
+ exclude:
3
+ labels:
4
+ - ignore in release notes
5
+
6
+ categories:
7
+ - title: New Features 🎉
8
+ labels:
9
+ - feature
10
+ - enhancement
11
+ exclude:
12
+ labels:
13
+ - breaking change
14
+
15
+ - title: Fixes 🐞
16
+ labels:
17
+ - bug
18
+ exclude:
19
+ labels:
20
+ - breaking change
21
+
22
+ - title: Breaking Changes 🛫
23
+ labels:
24
+ - breaking change
25
+
26
+ - title: Docs 📚
27
+ labels:
28
+ - documentation
29
+
30
+ - title: Other Changes 🦾
31
+ labels:
32
+ - "*"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scmcp_shared
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: A shared function libray for scmcphub
5
5
  Author-email: shuang <hsh-me@outlook.com>
6
6
  License: BSD 3-Clause License
@@ -0,0 +1,3 @@
1
+
2
+ __version__ = "0.3.0"
3
+
@@ -0,0 +1,96 @@
1
+ import typer
2
+ from typing import Optional, Union, Type
3
+ from enum import Enum
4
+ from .util import add_figure_route, set_env,setup_mcp
5
+
6
+
7
+ class TransportEnum(str, Enum):
8
+ STDIO = "stdio"
9
+ SSE = "sse"
10
+ SHTTP = "shttp"
11
+
12
+ @property
13
+ def transport_value(self) -> str:
14
+ """Get the actual transport value to use."""
15
+ if self == TransportEnum.SHTTP:
16
+ return "streamable-http"
17
+ return self.value
18
+
19
+
20
+ class ModuleEnum(str, Enum):
21
+ """Base class for module types."""
22
+ ALL = "all"
23
+
24
+
25
+
26
+ class MCPCLI:
27
+ """Base class for CLI applications with support for dynamic modules and parameters."""
28
+
29
+ def __init__(self, name: str, help_text: str, manager=None, modules=ModuleEnum):
30
+ self.name = name
31
+ self.modules = modules
32
+ self.manager = manager
33
+ self.app = typer.Typer(
34
+ name=name,
35
+ help=help_text,
36
+ add_completion=False,
37
+ no_args_is_help=True,
38
+ )
39
+ self._setup_commands()
40
+
41
+ def _setup_commands(self):
42
+ """Setup the main commands for the CLI."""
43
+ self.app.command(name="run", help="Start the server with the specified configuration")(self.run_command())
44
+ self.app.callback()(self._callback)
45
+
46
+ def run_command(self):
47
+ def _run_command(
48
+ log_file: Optional[str] = typer.Option(None, "--log-file", help="log file path, use stdout if None"),
49
+ transport: TransportEnum = typer.Option(TransportEnum.STDIO, "-t", "--transport", help="specify transport type",
50
+ case_sensitive=False),
51
+ port: int = typer.Option(8000, "-p", "--port", help="transport port"),
52
+ host: str = typer.Option("127.0.0.1", "--host", help="transport host"),
53
+ forward: str = typer.Option(None, "-f", "--forward", help="forward request to another server"),
54
+ module: list[self.modules] = typer.Option(
55
+ [self.modules.ALL],
56
+ "-m",
57
+ "--module",
58
+ help="specify module to run"
59
+ ),
60
+ ):
61
+ """Start the server with the specified configuration."""
62
+ if "all" in module:
63
+ modules = None
64
+ elif isinstance(module, list) and bool(module):
65
+ modules = [m.value for m in module]
66
+ self.mcp = self.manager(self.name, include_modules=modules).mcp
67
+
68
+ self.run_mcp(log_file, forward, transport, host, port)
69
+ return _run_command
70
+
71
+
72
+ def _callback(self):
73
+ """Liana MCP CLI root command."""
74
+ pass
75
+
76
+ def run_mcp(self, log_file, forward, transport, host, port):
77
+ set_env(log_file, forward, transport, host, port)
78
+ from .logging_config import setup_logger
79
+ setup_logger(log_file)
80
+ if transport == "stdio":
81
+ self.mcp.run()
82
+ elif transport in ["sse", "shttp"]:
83
+ add_figure_route(self.mcp)
84
+ transport = transport.transport_value
85
+ self.mcp.run(
86
+ transport=transport,
87
+ host=host,
88
+ port=port,
89
+ log_level="info"
90
+ )
91
+
92
+ def run_cli(self, mcp=None, module_dic=None):
93
+ """Run the CLI application."""
94
+ self.mcp = mcp
95
+ self.module_dic = module_dic
96
+ self.app()
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import Field, BaseModel,ConfigDict
4
+
5
+
6
+ class AdataModel(BaseModel):
7
+ """Input schema for the adata tool."""
8
+ sampleid: str | None = Field(default=None, description="adata sampleid")
9
+ adtype: str = Field(default="exp", description="The input adata.X data type for preprocess/analysis/plotting")
10
+
11
+ model_config = ConfigDict(
12
+ extra="ignore"
13
+ )
14
+
15
+ class AdataInfo(BaseModel):
16
+ """Input schema for the adata tool."""
17
+ sampleid: str | None = Field(default=None, description="adata sampleid")
18
+ adtype: str = Field(default="exp", description="The input adata.X data type for preprocess/analysis/plotting")
19
+
20
+ model_config = ConfigDict(
21
+ extra="ignore"
22
+ )
@@ -1,13 +1,13 @@
1
1
  from pydantic import (
2
2
  Field,
3
3
  field_validator,
4
- model_validator,
4
+ model_validator, BaseModel
5
5
  )
6
6
  from typing import Optional, Literal
7
- from .base import AdataModel
8
7
 
9
8
 
10
- class ReadModel(AdataModel):
9
+
10
+ class ReadModel(BaseModel):
11
11
  """Input schema for the read tool."""
12
12
  filename: str = Field(
13
13
  ...,
@@ -22,10 +22,6 @@ class ReadModel(AdataModel):
22
22
  default=None,
23
23
  description="Name of sheet/table in hdf5 or Excel file."
24
24
  )
25
- ext: str = Field(
26
- default=None,
27
- description="Extension that indicates the file type. If None, uses extension of filename."
28
- )
29
25
  delimiter: str = Field(
30
26
  default=None,
31
27
  description="Delimiter that separates data within text file. If None, will split at arbitrary number of white spaces, which is different from enforcing splitting at any single white space."
@@ -86,7 +82,7 @@ class ReadModel(AdataModel):
86
82
  return v
87
83
 
88
84
 
89
- class WriteModel(AdataModel):
85
+ class WriteModel(BaseModel):
90
86
  """Input schema for the write tool."""
91
87
  filename: str = Field(
92
88
  description="Path to save the file. If no extension is provided, the default format will be used."
@@ -7,7 +7,6 @@ from pydantic import (
7
7
  model_validator,
8
8
  BaseModel
9
9
  )
10
- from .base import AdataModel
11
10
 
12
11
  # 创建 Mixin 类处理特定功能
13
12
  class LegendMixin:
@@ -73,7 +72,7 @@ class FigureSizeMixin:
73
72
 
74
73
 
75
74
  # 基础可视化模型,包含所有可视化工具共享的字段
76
- class BaseVisualizationModel(AdataModel, LegendMixin, ColorMappingMixin, FigureSizeMixin):
75
+ class BaseVisualizationModel(BaseModel, LegendMixin, ColorMappingMixin, FigureSizeMixin):
77
76
  """基础可视化模型,包含所有可视化工具共享的字段"""
78
77
  pass
79
78
 
@@ -632,7 +631,7 @@ class RankGenesGroupsModel(BaseVisualizationModel):
632
631
 
633
632
 
634
633
  # 重构 ClusterMapModel
635
- class ClusterMapModel(AdataModel):
634
+ class ClusterMapModel(BaseModel):
636
635
  """Input schema for the clustermap plotting tool."""
637
636
 
638
637
  obs_keys: Optional[str] = Field(
@@ -3,15 +3,15 @@ from pydantic import (
3
3
  ValidationInfo,
4
4
  computed_field,
5
5
  field_validator,
6
- model_validator,
6
+ model_validator,BaseModel
7
7
  )
8
- from .base import AdataModel
8
+
9
9
  from typing import Optional, Union, List, Dict, Any
10
10
  from typing import Literal
11
11
  import numpy as np
12
12
 
13
13
 
14
- class FilterCells(AdataModel):
14
+ class FilterCells(BaseModel):
15
15
  """Input schema for the filter_cells preprocessing tool."""
16
16
 
17
17
  min_counts: Optional[int] = Field(
@@ -42,7 +42,7 @@ class FilterCells(AdataModel):
42
42
  return v
43
43
 
44
44
 
45
- class FilterGenes(AdataModel):
45
+ class FilterGenes(BaseModel):
46
46
  """Input schema for the filter_genes preprocessing tool."""
47
47
 
48
48
  min_counts: Optional[int] = Field(
@@ -73,7 +73,7 @@ class FilterGenes(AdataModel):
73
73
  return v
74
74
 
75
75
 
76
- class SubsetCellModel(AdataModel):
76
+ class SubsetCellModel(BaseModel):
77
77
  """Input schema for subsetting AnnData objects based on various criteria."""
78
78
  obs_key: Optional[str] = Field(
79
79
  default=None,
@@ -109,7 +109,7 @@ class SubsetCellModel(AdataModel):
109
109
  )
110
110
 
111
111
 
112
- class SubsetGeneModel(AdataModel):
112
+ class SubsetGeneModel(BaseModel):
113
113
  """Input schema for subsetting AnnData objects based on various criteria."""
114
114
  min_counts: Optional[int] = Field(
115
115
  default=None,
@@ -145,7 +145,7 @@ class SubsetGeneModel(AdataModel):
145
145
  )
146
146
 
147
147
 
148
- class CalculateQCMetrics(AdataModel):
148
+ class CalculateQCMetrics(BaseModel):
149
149
  """Input schema for the calculate_qc_metrics preprocessing tool."""
150
150
 
151
151
  expr_type: str = Field(
@@ -196,7 +196,7 @@ class CalculateQCMetrics(AdataModel):
196
196
 
197
197
 
198
198
 
199
- class Log1PModel(AdataModel):
199
+ class Log1PModel(BaseModel):
200
200
  """Input schema for the log1p preprocessing tool."""
201
201
 
202
202
  base: Optional[Union[int, float]] = Field(
@@ -233,7 +233,7 @@ class Log1PModel(AdataModel):
233
233
 
234
234
 
235
235
 
236
- class HighlyVariableGenesModel(AdataModel):
236
+ class HighlyVariableGenesModel(BaseModel):
237
237
  """Input schema for the highly_variable_genes preprocessing tool."""
238
238
 
239
239
  layer: Optional[str] = Field(
@@ -307,7 +307,7 @@ class HighlyVariableGenesModel(AdataModel):
307
307
  return v
308
308
 
309
309
 
310
- class RegressOutModel(AdataModel):
310
+ class RegressOutModel(BaseModel):
311
311
  """Input schema for the regress_out preprocessing tool."""
312
312
 
313
313
  keys: Union[str, List[str]] = Field(
@@ -340,7 +340,7 @@ class RegressOutModel(AdataModel):
340
340
  raise ValueError("keys must be a string or list of strings")
341
341
 
342
342
 
343
- class ScaleModel(AdataModel):
343
+ class ScaleModel(BaseModel):
344
344
  """Input schema for the scale preprocessing tool."""
345
345
 
346
346
  zero_center: bool = Field(
@@ -376,7 +376,7 @@ class ScaleModel(AdataModel):
376
376
  return v
377
377
 
378
378
 
379
- class CombatModel(AdataModel):
379
+ class CombatModel(BaseModel):
380
380
  """Input schema for the combat batch effect correction tool."""
381
381
 
382
382
  key: str = Field(
@@ -405,7 +405,7 @@ class CombatModel(AdataModel):
405
405
  return v
406
406
 
407
407
 
408
- class ScrubletModel(AdataModel):
408
+ class ScrubletModel(BaseModel):
409
409
  """Input schema for the scrublet doublet prediction tool."""
410
410
 
411
411
  adata_sim: Optional[str] = Field(
@@ -511,7 +511,7 @@ class ScrubletModel(AdataModel):
511
511
  return v.lower()
512
512
 
513
513
 
514
- class NeighborsModel(AdataModel):
514
+ class NeighborsModel(BaseModel):
515
515
  """Input schema for the neighbors graph construction tool."""
516
516
 
517
517
  n_neighbors: int = Field(
@@ -589,7 +589,7 @@ class NeighborsModel(AdataModel):
589
589
  return v
590
590
 
591
591
 
592
- class NormalizeTotalModel(AdataModel):
592
+ class NormalizeTotalModel(BaseModel):
593
593
  """Input schema for the normalize_total preprocessing tool."""
594
594
 
595
595
  target_sum: Optional[float] = Field(
@@ -1,9 +1,8 @@
1
- from pydantic import Field, field_validator, ValidationInfo
1
+ from pydantic import Field, field_validator, ValidationInfo, BaseModel
2
2
  from typing import Optional, Union, List, Dict, Any, Tuple, Literal, Mapping
3
3
 
4
- from .base import AdataModel
5
4
 
6
- class TSNEModel(AdataModel):
5
+ class TSNEModel(BaseModel):
7
6
  """Input schema for the t-SNE dimensionality reduction tool."""
8
7
  n_pcs: Optional[int] = Field(
9
8
  default=None,
@@ -60,7 +59,7 @@ class TSNEModel(AdataModel):
60
59
  return v.lower()
61
60
 
62
61
 
63
- class UMAPModel(AdataModel):
62
+ class UMAPModel(BaseModel):
64
63
  """Input schema for the UMAP dimensionality reduction tool."""
65
64
 
66
65
  min_dist: Optional[float] = Field(
@@ -146,7 +145,7 @@ class UMAPModel(AdataModel):
146
145
  return v.lower()
147
146
 
148
147
 
149
- class DrawGraphModel(AdataModel):
148
+ class DrawGraphModel(BaseModel):
150
149
  """Input schema for the force-directed graph drawing tool."""
151
150
 
152
151
  layout: str = Field(
@@ -200,7 +199,7 @@ class DrawGraphModel(AdataModel):
200
199
  return v
201
200
 
202
201
 
203
- class DiffMapModel(AdataModel):
202
+ class DiffMapModel(BaseModel):
204
203
  """Input schema for the Diffusion Maps dimensionality reduction tool."""
205
204
 
206
205
  n_comps: int = Field(
@@ -230,7 +229,7 @@ class DiffMapModel(AdataModel):
230
229
  return v
231
230
 
232
231
 
233
- class EmbeddingDensityModel(AdataModel):
232
+ class EmbeddingDensityModel(BaseModel):
234
233
  """Input schema for the embedding density calculation tool."""
235
234
 
236
235
  basis: str = Field(
@@ -258,7 +257,7 @@ class EmbeddingDensityModel(AdataModel):
258
257
  return v
259
258
 
260
259
 
261
- class LeidenModel(AdataModel):
260
+ class LeidenModel(BaseModel):
262
261
  """Input schema for the Leiden clustering algorithm."""
263
262
 
264
263
  resolution: Optional[float] = Field(
@@ -301,11 +300,6 @@ class LeidenModel(AdataModel):
301
300
  description="Which package's implementation to use."
302
301
  )
303
302
 
304
- clustering_args: Optional[Dict[str, Any]] = Field(
305
- default=None,
306
- description="Any further arguments to pass to the clustering algorithm."
307
- )
308
-
309
303
  @field_validator('resolution')
310
304
  def validate_resolution(cls, v: float) -> float:
311
305
  """Validate resolution is positive"""
@@ -330,7 +324,7 @@ class LeidenModel(AdataModel):
330
324
  return v
331
325
 
332
326
 
333
- class LouvainModel(AdataModel):
327
+ class LouvainModel(BaseModel):
334
328
  """Input schema for the Louvain clustering algorithm."""
335
329
 
336
330
  resolution: Optional[float] = Field(
@@ -402,7 +396,7 @@ class LouvainModel(AdataModel):
402
396
  return v
403
397
 
404
398
 
405
- class DendrogramModel(AdataModel):
399
+ class DendrogramModel(BaseModel):
406
400
  """Input schema for the hierarchical clustering dendrogram tool."""
407
401
 
408
402
  groupby: str = Field(
@@ -467,7 +461,7 @@ class DendrogramModel(AdataModel):
467
461
  return v
468
462
 
469
463
 
470
- class DPTModel(AdataModel):
464
+ class DPTModel(BaseModel):
471
465
  """Input schema for the Diffusion Pseudotime (DPT) tool."""
472
466
 
473
467
  n_dcs: int = Field(
@@ -516,7 +510,7 @@ class DPTModel(AdataModel):
516
510
  raise ValueError("min_group_size must be between 0 and 1")
517
511
  return v
518
512
 
519
- class PAGAModel(AdataModel):
513
+ class PAGAModel(BaseModel):
520
514
  """Input schema for the Partition-based Graph Abstraction (PAGA) tool."""
521
515
 
522
516
  groups: Optional[str] = Field(
@@ -544,7 +538,7 @@ class PAGAModel(AdataModel):
544
538
  return v
545
539
 
546
540
 
547
- class IngestModel(AdataModel):
541
+ class IngestModel(BaseModel):
548
542
  """Input schema for the ingest tool that maps labels and embeddings from reference data to new data."""
549
543
 
550
544
  obs: Optional[Union[str, List[str]]] = Field(
@@ -593,7 +587,7 @@ class IngestModel(AdataModel):
593
587
  return v.lower()
594
588
 
595
589
 
596
- class RankGenesGroupsModel(AdataModel):
590
+ class RankGenesGroupsModel(BaseModel):
597
591
  """Input schema for the rank_genes_groups tool."""
598
592
 
599
593
  groupby: str = Field(
@@ -675,7 +669,7 @@ class RankGenesGroupsModel(AdataModel):
675
669
  return v
676
670
 
677
671
 
678
- class FilterRankGenesGroupsModel(AdataModel):
672
+ class FilterRankGenesGroupsModel(BaseModel):
679
673
  """Input schema for filtering ranked genes groups."""
680
674
 
681
675
  key: Optional[str] = Field(
@@ -738,7 +732,7 @@ class FilterRankGenesGroupsModel(AdataModel):
738
732
  return v
739
733
 
740
734
 
741
- class MarkerGeneOverlapModel(AdataModel):
735
+ class MarkerGeneOverlapModel(BaseModel):
742
736
  """Input schema for the marker gene overlap tool."""
743
737
 
744
738
  key: str = Field(
@@ -809,7 +803,7 @@ class MarkerGeneOverlapModel(AdataModel):
809
803
  return v
810
804
 
811
805
 
812
- class ScoreGenesModel(AdataModel):
806
+ class ScoreGenesModel(BaseModel):
813
807
  """Input schema for the score_genes tool that calculates gene scores based on average expression."""
814
808
 
815
809
  ctrl_size: int = Field(
@@ -852,7 +846,7 @@ class ScoreGenesModel(AdataModel):
852
846
  return v
853
847
 
854
848
 
855
- class ScoreGenesCellCycleModel(AdataModel):
849
+ class ScoreGenesCellCycleModel(BaseModel):
856
850
  """Input schema for the score_genes_cell_cycle tool that scores cell cycle genes."""
857
851
 
858
852
  s_genes: List[str] = Field(
@@ -902,7 +896,7 @@ class ScoreGenesCellCycleModel(AdataModel):
902
896
 
903
897
 
904
898
 
905
- class PCAModel(AdataModel):
899
+ class PCAModel(BaseModel):
906
900
  """Input schema for the PCA preprocessing tool."""
907
901
 
908
902
  n_comps: Optional[int] = Field(
@@ -4,13 +4,13 @@ from pydantic import (
4
4
  ValidationInfo,
5
5
  computed_field,
6
6
  field_validator,
7
- model_validator,
7
+ model_validator,BaseModel
8
8
  )
9
9
  from typing import Optional, Union, List, Dict, Any, Callable, Collection, Literal
10
- from .base import AdataModel
11
10
 
12
11
 
13
- class MarkVarModel(AdataModel):
12
+
13
+ class MarkVarModel(BaseModel):
14
14
  """Determine or mark if each gene meets specific conditions and store results in adata.var as boolean values"""
15
15
 
16
16
  var_name: str = Field(
@@ -32,15 +32,15 @@ class MarkVarModel(AdataModel):
32
32
  )
33
33
 
34
34
 
35
- class ListVarModel(AdataModel):
35
+ class ListVarModel(BaseModel):
36
36
  """ListVarModel"""
37
37
  pass
38
38
 
39
- class ListObsModel(AdataModel):
39
+ class ListObsModel(BaseModel):
40
40
  """ListObsModel"""
41
41
  pass
42
42
 
43
- class VarNamesModel(AdataModel):
43
+ class VarNamesModel(BaseModel):
44
44
  """ListObsModel"""
45
45
  var_names: List[str] = Field(
46
46
  default=None,
@@ -48,7 +48,7 @@ class VarNamesModel(AdataModel):
48
48
  )
49
49
 
50
50
 
51
- class ConcatAdataModel(AdataModel):
51
+ class ConcatBaseModel(BaseModel):
52
52
  """Model for concatenating AnnData objects"""
53
53
 
54
54
  axis: Literal['obs', 0, 'var', 1] = Field(
@@ -89,7 +89,7 @@ class ConcatAdataModel(AdataModel):
89
89
  )
90
90
 
91
91
 
92
- class DPTIROOTModel(AdataModel):
92
+ class DPTIROOTModel(BaseModel):
93
93
  """Input schema for setting the root cell for diffusion pseudotime."""
94
94
  diffmap_key: str = Field(
95
95
  default="X_diffmap",
@@ -103,7 +103,7 @@ class DPTIROOTModel(AdataModel):
103
103
  )
104
104
 
105
105
 
106
- class CelltypeMapCellTypeModel(AdataModel):
106
+ class CelltypeMapCellTypeModel(BaseModel):
107
107
  """Input schema for mapping cluster IDs to cell type names."""
108
108
  cluster_key: str = Field(
109
109
  description="Key in adata.obs containing cluster IDs."
@@ -122,14 +122,14 @@ class CelltypeMapCellTypeModel(AdataModel):
122
122
 
123
123
 
124
124
 
125
- class AddLayerModel(AdataModel):
125
+ class AddLayerModel(BaseModel):
126
126
  """Input schema for adding a layer to AnnData object."""
127
127
  layer_name: str = Field(
128
128
  description="Name of the layer to add to adata.layers."
129
129
  )
130
130
 
131
131
 
132
- class QueryOpLogModel(AdataModel):
132
+ class QueryOpLogModel(BaseModel):
133
133
  """QueryOpLogModel"""
134
134
  n: int = Field(
135
135
  default=10,
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable, AsyncIterator
4
+ from typing import Any, Dict, List, Optional
5
+ from contextlib import asynccontextmanager
6
+ import asyncio
7
+
8
+ from .base import BaseMCP,AdataState,BaseMCPManager
9
+ from .io import ScanpyIOMCP, io_mcp
10
+ from .util import ScanpyUtilMCP
11
+ from .pl import ScanpyPlottingMCP
12
+ from .pp import ScanpyPreprocessingMCP
13
+ from .tl import ScanpyToolsMCP