scmcp-shared 0.3.6__py3-none-any.whl → 0.3.7__py3-none-any.whl
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.
- scmcp_shared/__init__.py +1 -1
- scmcp_shared/schema/io.py +2 -2
- scmcp_shared/schema/pl.py +42 -42
- scmcp_shared/schema/pp.py +10 -10
- scmcp_shared/schema/tl.py +18 -18
- scmcp_shared/schema/util.py +9 -9
- scmcp_shared/server/base.py +6 -11
- scmcp_shared/server/io.py +5 -4
- scmcp_shared/server/pl.py +33 -32
- scmcp_shared/server/pp.py +27 -23
- scmcp_shared/server/tl.py +35 -34
- scmcp_shared/server/util.py +19 -18
- {scmcp_shared-0.3.6.dist-info → scmcp_shared-0.3.7.dist-info}/METADATA +4 -2
- scmcp_shared-0.3.7.dist-info/RECORD +21 -0
- scmcp_shared-0.3.6.dist-info/RECORD +0 -21
- {scmcp_shared-0.3.6.dist-info → scmcp_shared-0.3.7.dist-info}/WHEEL +0 -0
- {scmcp_shared-0.3.6.dist-info → scmcp_shared-0.3.7.dist-info}/licenses/LICENSE +0 -0
scmcp_shared/server/pl.py
CHANGED
@@ -3,6 +3,7 @@ import inspect
|
|
3
3
|
from functools import partial
|
4
4
|
import scanpy as sc
|
5
5
|
from fastmcp import FastMCP, Context
|
6
|
+
from fastmcp.tools.tool import Tool
|
6
7
|
from fastmcp.exceptions import ToolError
|
7
8
|
from ..schema.pl import *
|
8
9
|
from ..schema import AdataInfo
|
@@ -24,7 +25,7 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
24
25
|
super().__init__("ScanpyMCP-PL-Server", include_tools, exclude_tools, AdataInfo)
|
25
26
|
|
26
27
|
def _tool_pca(self):
|
27
|
-
def _pca(request:
|
28
|
+
def _pca(request: PCAParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
28
29
|
"""Scatter plot in PCA coordinates. default figure for PCA plot"""
|
29
30
|
try:
|
30
31
|
if (res := forward_request("pl_pca", request, adinfo)) is not None:
|
@@ -39,10 +40,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
39
40
|
raise ToolError(e.__context__)
|
40
41
|
else:
|
41
42
|
raise ToolError(e)
|
42
|
-
return _pca
|
43
|
+
return Tool.from_function(_pca, name="pca")
|
43
44
|
|
44
45
|
def _tool_diffmap(self):
|
45
|
-
def _diffmap(request:
|
46
|
+
def _diffmap(request: DiffusionMapParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
46
47
|
"""Plot diffusion map embedding of cells."""
|
47
48
|
try:
|
48
49
|
if (res := forward_request("pl_diffmap", request, adinfo)) is not None:
|
@@ -57,10 +58,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
57
58
|
raise ToolError(e.__context__)
|
58
59
|
else:
|
59
60
|
raise ToolError(e)
|
60
|
-
return _diffmap
|
61
|
+
return Tool.from_function(_diffmap, name="diffmap")
|
61
62
|
|
62
63
|
def _tool_violin(self):
|
63
|
-
def _violin(request:
|
64
|
+
def _violin(request: ViolinParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
64
65
|
"""Plot violin plot of one or more variables."""
|
65
66
|
try:
|
66
67
|
if (res := forward_request("pl_violin", request, adinfo)) is not None:
|
@@ -77,10 +78,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
77
78
|
raise ToolError(e.__context__)
|
78
79
|
else:
|
79
80
|
raise ToolError(e)
|
80
|
-
return _violin
|
81
|
+
return Tool.from_function(_violin, name="violin")
|
81
82
|
|
82
83
|
def _tool_stacked_violin(self):
|
83
|
-
def _stacked_violin(request:
|
84
|
+
def _stacked_violin(request: StackedViolinParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
84
85
|
"""Plot stacked violin plots. Makes a compact image composed of individual violin plots stacked on top of each other."""
|
85
86
|
try:
|
86
87
|
if (res := forward_request("pl_stacked_violin", request, adinfo)) is not None:
|
@@ -95,10 +96,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
95
96
|
raise ToolError(e.__context__)
|
96
97
|
else:
|
97
98
|
raise ToolError(e)
|
98
|
-
return _stacked_violin
|
99
|
+
return Tool.from_function(_stacked_violin, name="stacked_violin")
|
99
100
|
|
100
101
|
def _tool_heatmap(self):
|
101
|
-
async def _heatmap(request:
|
102
|
+
async def _heatmap(request: HeatmapParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
102
103
|
"""Heatmap of the expression values of genes."""
|
103
104
|
try:
|
104
105
|
if (res := forward_request("pl_heatmap", request, adinfo)) is not None:
|
@@ -113,10 +114,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
113
114
|
raise ToolError(e.__context__)
|
114
115
|
else:
|
115
116
|
raise ToolError(e)
|
116
|
-
return _heatmap
|
117
|
+
return Tool.from_function(_heatmap, name="heatmap")
|
117
118
|
|
118
119
|
def _tool_dotplot(self):
|
119
|
-
def _dotplot(request:
|
120
|
+
def _dotplot(request: DotplotParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
120
121
|
"""Plot dot plot of expression values per gene for each group."""
|
121
122
|
try:
|
122
123
|
if (res := forward_request("pl_dotplot", request, adinfo)) is not None:
|
@@ -131,10 +132,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
131
132
|
raise ToolError(e.__context__)
|
132
133
|
else:
|
133
134
|
raise ToolError(e)
|
134
|
-
return _dotplot
|
135
|
+
return Tool.from_function(_dotplot, name="dotplot")
|
135
136
|
|
136
137
|
def _tool_matrixplot(self):
|
137
|
-
def _matrixplot(request:
|
138
|
+
def _matrixplot(request: MatrixplotParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
138
139
|
"""matrixplot, Create a heatmap of the mean expression values per group of each var_names."""
|
139
140
|
try:
|
140
141
|
if (res := forward_request("pl_matrixplot", request, adinfo)) is not None:
|
@@ -149,10 +150,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
149
150
|
raise ToolError(e.__context__)
|
150
151
|
else:
|
151
152
|
raise ToolError(e)
|
152
|
-
return _matrixplot
|
153
|
+
return Tool.from_function(_matrixplot, name="matrixplot")
|
153
154
|
|
154
155
|
def _tool_tracksplot(self):
|
155
|
-
def _tracksplot(request:
|
156
|
+
def _tracksplot(request: TracksplotParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
156
157
|
"""tracksplot, compact plot of expression of a list of genes."""
|
157
158
|
try:
|
158
159
|
if (res := forward_request("pl_tracksplot", request, adinfo)) is not None:
|
@@ -167,10 +168,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
167
168
|
raise ToolError(e.__context__)
|
168
169
|
else:
|
169
170
|
raise ToolError(e)
|
170
|
-
return _tracksplot
|
171
|
+
return Tool.from_function(_tracksplot, name="tracksplot")
|
171
172
|
|
172
173
|
def _tool_scatter(self):
|
173
|
-
def _scatter(request:
|
174
|
+
def _scatter(request: EnhancedScatterParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
174
175
|
"""Plot a scatter plot of two variables, Scatter plot along observations or variables axes."""
|
175
176
|
try:
|
176
177
|
if (res := forward_request("pl_scatter", request, adinfo)) is not None:
|
@@ -185,10 +186,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
185
186
|
raise ToolError(e.__context__)
|
186
187
|
else:
|
187
188
|
raise ToolError(e)
|
188
|
-
return _scatter
|
189
|
+
return Tool.from_function(_scatter, name="scatter")
|
189
190
|
|
190
191
|
def _tool_embedding(self):
|
191
|
-
def _embedding(request:
|
192
|
+
def _embedding(request: EmbeddingParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
192
193
|
"""Scatter plot for user specified embedding basis (e.g. umap, tsne, etc)."""
|
193
194
|
try:
|
194
195
|
if (res := forward_request("pl_embedding", request, adinfo)) is not None:
|
@@ -203,10 +204,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
203
204
|
raise ToolError(e.__context__)
|
204
205
|
else:
|
205
206
|
raise ToolError(e)
|
206
|
-
return _embedding
|
207
|
+
return Tool.from_function(_embedding, name="embedding")
|
207
208
|
|
208
209
|
def _tool_embedding_density(self):
|
209
|
-
def _embedding_density(request:
|
210
|
+
def _embedding_density(request: EmbeddingDensityParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
210
211
|
"""Plot the density of cells in an embedding."""
|
211
212
|
try:
|
212
213
|
if (res := forward_request("pl_embedding_density", request, adinfo)) is not None:
|
@@ -221,10 +222,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
221
222
|
raise ToolError(e.__context__)
|
222
223
|
else:
|
223
224
|
raise ToolError(e)
|
224
|
-
return _embedding_density
|
225
|
+
return Tool.from_function(_embedding_density, name="embedding_density")
|
225
226
|
|
226
227
|
def _tool_rank_genes_groups(self):
|
227
|
-
def _rank_genes_groups(request:
|
228
|
+
def _rank_genes_groups(request: RankGenesGroupsParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
228
229
|
"""Plot ranking of genes based on differential expression."""
|
229
230
|
try:
|
230
231
|
if (res := forward_request("pl_rank_genes_groups", request, adinfo)) is not None:
|
@@ -239,10 +240,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
239
240
|
raise ToolError(e.__context__)
|
240
241
|
else:
|
241
242
|
raise ToolError(e)
|
242
|
-
return _rank_genes_groups
|
243
|
+
return Tool.from_function(_rank_genes_groups, name="rank_genes_groups")
|
243
244
|
|
244
245
|
def _tool_rank_genes_groups_dotplot(self):
|
245
|
-
def _rank_genes_groups_dotplot(request:
|
246
|
+
def _rank_genes_groups_dotplot(request: RankGenesGroupsDotplotParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
246
247
|
"""Plot ranking of genes(DEGs) using dotplot visualization. Defualt plot DEGs for rank_genes_groups tool"""
|
247
248
|
try:
|
248
249
|
if (res := forward_request("pl_rank_genes_groups_dotplot", request, adinfo)) is not None:
|
@@ -257,10 +258,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
257
258
|
raise ToolError(e.__context__)
|
258
259
|
else:
|
259
260
|
raise ToolError(e)
|
260
|
-
return _rank_genes_groups_dotplot
|
261
|
+
return Tool.from_function(_rank_genes_groups_dotplot, name="rank_genes_groups_dotplot")
|
261
262
|
|
262
263
|
def _tool_clustermap(self):
|
263
|
-
def _clustermap(request:
|
264
|
+
def _clustermap(request: ClusterMapParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
264
265
|
"""Plot hierarchical clustering of cells and genes."""
|
265
266
|
try:
|
266
267
|
if (res := forward_request("pl_clustermap", request, adinfo)) is not None:
|
@@ -275,10 +276,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
275
276
|
raise ToolError(e.__context__)
|
276
277
|
else:
|
277
278
|
raise ToolError(e)
|
278
|
-
return _clustermap
|
279
|
+
return Tool.from_function(_clustermap, name="clustermap")
|
279
280
|
|
280
281
|
def _tool_highly_variable_genes(self):
|
281
|
-
def _highly_variable_genes(request:
|
282
|
+
def _highly_variable_genes(request: HighlyVariableGenesParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
282
283
|
"""plot highly variable genes; Plot dispersions or normalized variance versus means for genes."""
|
283
284
|
try:
|
284
285
|
if (res := forward_request("pl_highly_variable_genes", request, adinfo)) is not None:
|
@@ -293,10 +294,10 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
293
294
|
raise ToolError(e.__context__)
|
294
295
|
else:
|
295
296
|
raise ToolError(e)
|
296
|
-
return _highly_variable_genes
|
297
|
+
return Tool.from_function(_highly_variable_genes, name="highly_variable_genes")
|
297
298
|
|
298
299
|
def _tool_pca_variance_ratio(self):
|
299
|
-
def _pca_variance_ratio(request:
|
300
|
+
def _pca_variance_ratio(request: PCAVarianceRatioParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
300
301
|
"""Plot the PCA variance ratio to visualize explained variance."""
|
301
302
|
try:
|
302
303
|
if (res := forward_request("pl_pca_variance_ratio", request, adinfo)) is not None:
|
@@ -311,5 +312,5 @@ class ScanpyPlottingMCP(BaseMCP):
|
|
311
312
|
raise ToolError(e.__context__)
|
312
313
|
else:
|
313
314
|
raise ToolError(e)
|
314
|
-
return _pca_variance_ratio
|
315
|
+
return Tool.from_function(_pca_variance_ratio, name="pca_variance_ratio")
|
315
316
|
|
scmcp_shared/server/pp.py
CHANGED
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
import inspect
|
3
3
|
import scanpy as sc
|
4
4
|
from fastmcp import FastMCP, Context
|
5
|
+
from fastmcp.tools.tool import Tool
|
5
6
|
from fastmcp.exceptions import ToolError
|
6
7
|
from ..schema.pp import *
|
7
8
|
from ..schema import AdataInfo
|
@@ -22,7 +23,7 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
22
23
|
super().__init__("ScanpyMCP-PP-Server", include_tools, exclude_tools, AdataInfo)
|
23
24
|
|
24
25
|
def _tool_subset_cells(self):
|
25
|
-
def _subset_cells(request:
|
26
|
+
def _subset_cells(request: SubsetCellParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
26
27
|
"""filter or subset cells based on total genes expressed counts and numbers. or values in adata.obs[obs_key]"""
|
27
28
|
try:
|
28
29
|
result = forward_request("subset_cells", request, adinfo)
|
@@ -62,10 +63,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
62
63
|
raise ToolError(e.__context__)
|
63
64
|
else:
|
64
65
|
raise ToolError(e)
|
65
|
-
return _subset_cells
|
66
|
+
return Tool.from_function(_subset_cells, name="subset_cells")
|
66
67
|
|
67
68
|
def _tool_subset_genes(self):
|
68
|
-
def _subset_genes(request:
|
69
|
+
def _subset_genes(request: SubsetGeneParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
69
70
|
"""filter or subset genes based on number of cells or counts, or values in adata.var[var_key] or subset highly variable genes"""
|
70
71
|
try:
|
71
72
|
result = forward_request("pp_subset_genes", request, adinfo)
|
@@ -74,6 +75,11 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
74
75
|
func_kwargs = filter_args(request, sc.pp.filter_genes)
|
75
76
|
ads = get_ads()
|
76
77
|
adata = ads.get_adata(adinfo=adinfo).copy()
|
78
|
+
if request.highly_variable:
|
79
|
+
adata = adata[:, adata.var.highly_variable]
|
80
|
+
add_op_log(adata, "subset_genes",
|
81
|
+
{"hpv": "true"}, adinfo
|
82
|
+
)
|
77
83
|
if func_kwargs:
|
78
84
|
sc.pp.filter_genes(adata, **func_kwargs)
|
79
85
|
add_op_log(adata, sc.pp.filter_genes, func_kwargs, adinfo)
|
@@ -86,8 +92,6 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
86
92
|
if request.var_max is not None:
|
87
93
|
mask = mask & (adata.var[request.var_key] <= request.var_max)
|
88
94
|
adata = adata[:, mask]
|
89
|
-
if request.highly_variable:
|
90
|
-
adata = adata[:, mask & adata.var.highly_variable]
|
91
95
|
add_op_log(adata, "subset_genes",
|
92
96
|
{
|
93
97
|
"var_key": request.var_key, "var_min": request.var_min, "var_max": request.var_max,
|
@@ -103,7 +107,7 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
103
107
|
raise ToolError(e.__context__)
|
104
108
|
else:
|
105
109
|
raise ToolError(e)
|
106
|
-
return _subset_genes
|
110
|
+
return Tool.from_function(_subset_genes, name="subset_genes")
|
107
111
|
|
108
112
|
def _tool_calculate_qc_metrics(self):
|
109
113
|
def _calculate_qc_metrics(request: CalculateQCMetrics, adinfo: self.AdataInfo=self.AdataInfo()):
|
@@ -130,10 +134,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
130
134
|
raise ToolError(e.__context__)
|
131
135
|
else:
|
132
136
|
raise ToolError(e)
|
133
|
-
return _calculate_qc_metrics
|
137
|
+
return Tool.from_function(_calculate_qc_metrics, name="calculate_qc_metrics")
|
134
138
|
|
135
139
|
def _tool_log1p(self):
|
136
|
-
def _log1p(request:
|
140
|
+
def _log1p(request: Log1PParams=Log1PParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
137
141
|
"""Logarithmize the data matrix"""
|
138
142
|
try:
|
139
143
|
result = forward_request("pp_log1p", request, adinfo)
|
@@ -157,10 +161,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
157
161
|
raise ToolError(e.__context__)
|
158
162
|
else:
|
159
163
|
raise ToolError(e)
|
160
|
-
return _log1p
|
164
|
+
return Tool.from_function(_log1p, name="log1p")
|
161
165
|
|
162
166
|
def _tool_normalize_total(self):
|
163
|
-
def _normalize_total(request:
|
167
|
+
def _normalize_total(request: NormalizeTotalParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
164
168
|
"""Normalize counts per cell to the same total count"""
|
165
169
|
try:
|
166
170
|
result = forward_request("pp_normalize_total", request, adinfo)
|
@@ -180,10 +184,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
180
184
|
raise ToolError(e.__context__)
|
181
185
|
else:
|
182
186
|
raise ToolError(e)
|
183
|
-
return _normalize_total
|
187
|
+
return Tool.from_function(_normalize_total, name="normalize_total")
|
184
188
|
|
185
189
|
def _tool_highly_variable_genes(self):
|
186
|
-
def _highly_variable_genes(request:
|
190
|
+
def _highly_variable_genes(request: HighlyVariableGenesParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
187
191
|
"""Annotate highly variable genes"""
|
188
192
|
try:
|
189
193
|
result = forward_request("pp_highly_variable_genes", request, adinfo)
|
@@ -205,10 +209,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
205
209
|
raise ToolError(e.__context__)
|
206
210
|
else:
|
207
211
|
raise ToolError(e)
|
208
|
-
return _highly_variable_genes
|
212
|
+
return Tool.from_function(_highly_variable_genes, name="highly_variable_genes")
|
209
213
|
|
210
214
|
def _tool_regress_out(self):
|
211
|
-
def _regress_out(request:
|
215
|
+
def _regress_out(request: RegressOutParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
212
216
|
"""Regress out (mostly) unwanted sources of variation."""
|
213
217
|
try:
|
214
218
|
result = forward_request("pp_regress_out", request, adinfo)
|
@@ -228,10 +232,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
228
232
|
raise ToolError(e.__context__)
|
229
233
|
else:
|
230
234
|
raise ToolError(e)
|
231
|
-
return _regress_out
|
235
|
+
return Tool.from_function(_regress_out, name="regress_out")
|
232
236
|
|
233
237
|
def _tool_scale(self):
|
234
|
-
def _scale(request:
|
238
|
+
def _scale(request: ScaleParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
235
239
|
"""Scale data to unit variance and zero mean"""
|
236
240
|
try:
|
237
241
|
result = forward_request("pp_scale", request, adinfo)
|
@@ -253,10 +257,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
253
257
|
raise ToolError(e.__context__)
|
254
258
|
else:
|
255
259
|
raise ToolError(e)
|
256
|
-
return _scale
|
260
|
+
return Tool.from_function(_scale, name="scale")
|
257
261
|
|
258
262
|
def _tool_combat(self):
|
259
|
-
def _combat(request:
|
263
|
+
def _combat(request: CombatParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
260
264
|
"""ComBat function for batch effect correction"""
|
261
265
|
try:
|
262
266
|
result = forward_request("pp_combat", request, adinfo)
|
@@ -278,10 +282,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
278
282
|
raise ToolError(e.__context__)
|
279
283
|
else:
|
280
284
|
raise ToolError(e)
|
281
|
-
return _combat
|
285
|
+
return Tool.from_function(_combat, name="combat")
|
282
286
|
|
283
287
|
def _tool_scrublet(self):
|
284
|
-
def _scrublet(request:
|
288
|
+
def _scrublet(request: ScrubletParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
285
289
|
"""Predict doublets using Scrublet"""
|
286
290
|
try:
|
287
291
|
result = forward_request("pp_scrublet", request, adinfo)
|
@@ -300,10 +304,10 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
300
304
|
raise ToolError(e.__context__)
|
301
305
|
else:
|
302
306
|
raise ToolError(e)
|
303
|
-
return _scrublet
|
307
|
+
return Tool.from_function(_scrublet, name="scrublet")
|
304
308
|
|
305
309
|
def _tool_neighbors(self):
|
306
|
-
def _neighbors(request:
|
310
|
+
def _neighbors(request: NeighborsParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
307
311
|
"""Compute nearest neighbors distance matrix and neighborhood graph"""
|
308
312
|
try:
|
309
313
|
result = forward_request("pp_neighbors", request, adinfo)
|
@@ -322,4 +326,4 @@ class ScanpyPreprocessingMCP(BaseMCP):
|
|
322
326
|
raise ToolError(e.__context__)
|
323
327
|
else:
|
324
328
|
raise ToolError(e)
|
325
|
-
return _neighbors
|
329
|
+
return Tool.from_function(_neighbors, name="neighbors")
|
scmcp_shared/server/tl.py
CHANGED
@@ -4,6 +4,7 @@ import inspect
|
|
4
4
|
from fastmcp import FastMCP
|
5
5
|
import scanpy as sc
|
6
6
|
from fastmcp.exceptions import ToolError
|
7
|
+
from fastmcp.tools.tool import Tool
|
7
8
|
from ..schema.tl import *
|
8
9
|
from ..schema import AdataInfo
|
9
10
|
from scmcp_shared.util import filter_args, add_op_log, forward_request, get_ads, generate_msg
|
@@ -23,7 +24,7 @@ class ScanpyToolsMCP(BaseMCP):
|
|
23
24
|
super().__init__("ScanpyMCP-TL-Server", include_tools, exclude_tools, AdataInfo)
|
24
25
|
|
25
26
|
def _tool_tsne(self):
|
26
|
-
def _tsne(request:
|
27
|
+
def _tsne(request: TSNEParams=TSNEParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
27
28
|
"""t-distributed stochastic neighborhood embedding (t-SNE) for visualization"""
|
28
29
|
try:
|
29
30
|
result = forward_request("tl_tsne", request, adinfo)
|
@@ -42,10 +43,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
42
43
|
raise ToolError(e.__context__)
|
43
44
|
else:
|
44
45
|
raise ToolError(e)
|
45
|
-
return _tsne
|
46
|
+
return Tool.from_function(_tsne, name="tsne")
|
46
47
|
|
47
48
|
def _tool_umap(self):
|
48
|
-
def _umap(request:
|
49
|
+
def _umap(request: UMAPParams=UMAPParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
49
50
|
"""Uniform Manifold Approximation and Projection (UMAP) for visualization"""
|
50
51
|
try:
|
51
52
|
result = forward_request("tl_umap", request, adinfo)
|
@@ -64,10 +65,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
64
65
|
raise ToolError(e.__context__)
|
65
66
|
else:
|
66
67
|
raise ToolError(e)
|
67
|
-
return _umap
|
68
|
+
return Tool.from_function(_umap, name="umap")
|
68
69
|
|
69
70
|
def _tool_draw_graph(self):
|
70
|
-
def _draw_graph(request:
|
71
|
+
def _draw_graph(request: DrawGraphParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
71
72
|
"""Force-directed graph drawing"""
|
72
73
|
try:
|
73
74
|
result = forward_request("tl_draw_graph", request, adinfo)
|
@@ -86,10 +87,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
86
87
|
raise ToolError(e.__context__)
|
87
88
|
else:
|
88
89
|
raise ToolError(e)
|
89
|
-
return _draw_graph
|
90
|
+
return Tool.from_function(_draw_graph, name="draw_graph")
|
90
91
|
|
91
92
|
def _tool_diffmap(self):
|
92
|
-
def _diffmap(request:
|
93
|
+
def _diffmap(request: DiffMapParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
93
94
|
"""Diffusion Maps for dimensionality reduction"""
|
94
95
|
try:
|
95
96
|
result = forward_request("tl_diffmap", request, adinfo)
|
@@ -109,10 +110,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
109
110
|
raise ToolError(e.__context__)
|
110
111
|
else:
|
111
112
|
raise ToolError(e)
|
112
|
-
return _diffmap
|
113
|
+
return Tool.from_function(_diffmap, name="diffmap")
|
113
114
|
|
114
115
|
def _tool_embedding_density(self):
|
115
|
-
def _embedding_density(request:
|
116
|
+
def _embedding_density(request: EmbeddingDensityParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
116
117
|
"""Calculate the density of cells in an embedding"""
|
117
118
|
try:
|
118
119
|
result = forward_request("tl_embedding_density", request, adinfo)
|
@@ -131,10 +132,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
131
132
|
raise ToolError(e.__context__)
|
132
133
|
else:
|
133
134
|
raise ToolError(e)
|
134
|
-
return _embedding_density
|
135
|
+
return Tool.from_function(_embedding_density, name="embedding_density")
|
135
136
|
|
136
137
|
def _tool_leiden(self):
|
137
|
-
def _leiden(request:
|
138
|
+
def _leiden(request: LeidenParams=LeidenParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
138
139
|
"""Leiden clustering algorithm for community detection"""
|
139
140
|
try:
|
140
141
|
result = forward_request("tl_leiden", request, adinfo)
|
@@ -153,10 +154,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
153
154
|
raise ToolError(e.__context__)
|
154
155
|
else:
|
155
156
|
raise ToolError(e)
|
156
|
-
return _leiden
|
157
|
+
return Tool.from_function(_leiden, name="leiden")
|
157
158
|
|
158
159
|
def _tool_louvain(self):
|
159
|
-
def _louvain(request:
|
160
|
+
def _louvain(request: LouvainParams=LouvainParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
160
161
|
"""Louvain clustering algorithm for community detection"""
|
161
162
|
try:
|
162
163
|
result = forward_request("tl_louvain", request, adinfo)
|
@@ -175,10 +176,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
175
176
|
raise ToolError(e.__context__)
|
176
177
|
else:
|
177
178
|
raise ToolError(e)
|
178
|
-
return _louvain
|
179
|
+
return Tool.from_function(_louvain, name="louvain")
|
179
180
|
|
180
181
|
def _tool_dendrogram(self):
|
181
|
-
def _dendrogram(request:
|
182
|
+
def _dendrogram(request: DendrogramParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
182
183
|
"""Hierarchical clustering dendrogram"""
|
183
184
|
try:
|
184
185
|
result = forward_request("tl_dendrogram", request, adinfo)
|
@@ -197,10 +198,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
197
198
|
raise ToolError(e.__context__)
|
198
199
|
else:
|
199
200
|
raise ToolError(e)
|
200
|
-
return _dendrogram
|
201
|
+
return Tool.from_function(_dendrogram, name="dendrogram")
|
201
202
|
|
202
203
|
def _tool_dpt(self):
|
203
|
-
def _dpt(request:
|
204
|
+
def _dpt(request: DPTParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
204
205
|
"""Diffusion Pseudotime (DPT) analysis"""
|
205
206
|
try:
|
206
207
|
result = forward_request("tl_dpt", request, adinfo)
|
@@ -219,10 +220,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
219
220
|
raise ToolError(e.__context__)
|
220
221
|
else:
|
221
222
|
raise ToolError(e)
|
222
|
-
return _dpt
|
223
|
+
return Tool.from_function(_dpt, name="dpt")
|
223
224
|
|
224
225
|
def _tool_paga(self):
|
225
|
-
def _paga(request:
|
226
|
+
def _paga(request: PAGAParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
226
227
|
"""Partition-based graph abstraction"""
|
227
228
|
try:
|
228
229
|
result = forward_request("tl_paga", request, adinfo)
|
@@ -241,10 +242,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
241
242
|
raise ToolError(e.__context__)
|
242
243
|
else:
|
243
244
|
raise ToolError(e)
|
244
|
-
return _paga
|
245
|
+
return Tool.from_function(_paga, name="paga")
|
245
246
|
|
246
247
|
def _tool_ingest(self):
|
247
|
-
def _ingest(request:
|
248
|
+
def _ingest(request: IngestParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
248
249
|
"""Map labels and embeddings from reference data to new data"""
|
249
250
|
try:
|
250
251
|
result = forward_request("tl_ingest", request, adinfo)
|
@@ -263,10 +264,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
263
264
|
raise ToolError(e.__context__)
|
264
265
|
else:
|
265
266
|
raise ToolError(e)
|
266
|
-
return _ingest
|
267
|
+
return Tool.from_function(_ingest, name="ingest")
|
267
268
|
|
268
269
|
def _tool_rank_genes_groups(self):
|
269
|
-
def _rank_genes_groups(request:
|
270
|
+
def _rank_genes_groups(request: RankGenesGroupsParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
270
271
|
"""Rank genes for characterizing groups, for differentially expressison analysis"""
|
271
272
|
try:
|
272
273
|
result = forward_request("tl_rank_genes_groups", request, adinfo)
|
@@ -285,10 +286,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
285
286
|
raise ToolError(e.__context__)
|
286
287
|
else:
|
287
288
|
raise ToolError(e)
|
288
|
-
return _rank_genes_groups
|
289
|
+
return Tool.from_function(_rank_genes_groups, name="rank_genes_groups")
|
289
290
|
|
290
291
|
def _tool_filter_rank_genes_groups(self):
|
291
|
-
def _filter_rank_genes_groups(request:
|
292
|
+
def _filter_rank_genes_groups(request: FilterRankGenesGroupsParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
292
293
|
"""Filter out genes based on fold change and fraction of genes"""
|
293
294
|
try:
|
294
295
|
result = forward_request("tl_filter_rank_genes_groups", request, adinfo)
|
@@ -307,10 +308,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
307
308
|
raise ToolError(e.__context__)
|
308
309
|
else:
|
309
310
|
raise ToolError(e)
|
310
|
-
return _filter_rank_genes_groups
|
311
|
+
return Tool.from_function(_filter_rank_genes_groups, name="filter_rank_genes_groups")
|
311
312
|
|
312
313
|
def _tool_marker_gene_overlap(self):
|
313
|
-
def _marker_gene_overlap(request:
|
314
|
+
def _marker_gene_overlap(request: MarkerGeneOverlapParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
314
315
|
"""Calculate overlap between data-derived marker genes and reference markers"""
|
315
316
|
try:
|
316
317
|
result = forward_request("tl_marker_gene_overlap", request, adinfo)
|
@@ -329,10 +330,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
329
330
|
raise ToolError(e.__context__)
|
330
331
|
else:
|
331
332
|
raise ToolError(e)
|
332
|
-
return _marker_gene_overlap
|
333
|
+
return Tool.from_function(_marker_gene_overlap, name="marker_gene_overlap")
|
333
334
|
|
334
335
|
def _tool_score_genes(self):
|
335
|
-
def _score_genes(request:
|
336
|
+
def _score_genes(request: ScoreGenesParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
336
337
|
"""Score a set of genes based on their average expression"""
|
337
338
|
try:
|
338
339
|
result = forward_request("tl_score_genes", request, adinfo)
|
@@ -351,10 +352,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
351
352
|
raise ToolError(e.__context__)
|
352
353
|
else:
|
353
354
|
raise ToolError(e)
|
354
|
-
return _score_genes
|
355
|
+
return Tool.from_function(_score_genes, name="score_genes")
|
355
356
|
|
356
357
|
def _tool_score_genes_cell_cycle(self):
|
357
|
-
def _score_genes_cell_cycle(request:
|
358
|
+
def _score_genes_cell_cycle(request: ScoreGenesCellCycleParams, adinfo: self.AdataInfo=self.AdataInfo()):
|
358
359
|
"""Score cell cycle genes and assign cell cycle phases"""
|
359
360
|
try:
|
360
361
|
result = forward_request("tl_score_genes_cell_cycle", request, adinfo)
|
@@ -373,10 +374,10 @@ class ScanpyToolsMCP(BaseMCP):
|
|
373
374
|
raise ToolError(e.__context__)
|
374
375
|
else:
|
375
376
|
raise ToolError(e)
|
376
|
-
return _score_genes_cell_cycle
|
377
|
+
return Tool.from_function(_score_genes_cell_cycle, name="score_genes_cell_cycle")
|
377
378
|
|
378
379
|
def _tool_pca(self):
|
379
|
-
def _pca(request:
|
380
|
+
def _pca(request: PCAParams=PCAParams(), adinfo: self.AdataInfo=self.AdataInfo()):
|
380
381
|
"""Compute PCA (Principal Component Analysis)."""
|
381
382
|
try:
|
382
383
|
result = forward_request("tl_pca", request, adinfo)
|
@@ -395,4 +396,4 @@ class ScanpyToolsMCP(BaseMCP):
|
|
395
396
|
raise ToolError(e.__context__)
|
396
397
|
else:
|
397
398
|
raise ToolError(e)
|
398
|
-
return _pca
|
399
|
+
return Tool.from_function(_pca, name="pca")
|