scmcp-shared 0.1.0__py3-none-any.whl → 0.2.0__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.
@@ -0,0 +1,321 @@
1
+ import os
2
+ import inspect
3
+ from functools import partial
4
+ import scanpy as sc
5
+ from fastmcp import FastMCP, Context
6
+ from ..schema.pl import *
7
+ from pathlib import Path
8
+ from ..logging_config import setup_logger
9
+ from ..util import forward_request, sc_like_plot, get_ads
10
+
11
+
12
+ logger = setup_logger()
13
+
14
+ pl_mcp = FastMCP("ScanpyMCP-PL-Server")
15
+
16
+
17
+
18
+ @pl_mcp.tool()
19
+ async def pca(request: PCAModel = PCAModel()):
20
+ """Scatter plot in PCA coordinates. default figure for PCA plot"""
21
+ try:
22
+ result = await forward_request("pl_pca", request)
23
+ if result is not None:
24
+ return result
25
+ adata = get_ads().get_adata(request=request)
26
+ fig_path = sc_like_plot(sc.pl.pca, adata, request)
27
+ return {"figpath": fig_path}
28
+ except KeyError as e:
29
+ raise e
30
+ except Exception as e:
31
+ if hasattr(e, '__context__') and e.__context__:
32
+ raise Exception(f"{str(e.__context__)}")
33
+ else:
34
+ raise e
35
+
36
+ @pl_mcp.tool()
37
+ async def diffmap(request: DiffusionMapModel = DiffusionMapModel()):
38
+ """Plot diffusion map embedding of cells."""
39
+ try:
40
+ result = await forward_request("pl_diffmap", request)
41
+ if result is not None:
42
+ return result
43
+ adata = get_ads().get_adata(request=request)
44
+ fig_path = sc_like_plot(sc.pl.diffmap, adata, request)
45
+ return {"figpath": fig_path}
46
+ except KeyError as e:
47
+ raise e
48
+ except Exception as e:
49
+ if hasattr(e, '__context__') and e.__context__:
50
+ raise Exception(f"{str(e.__context__)}")
51
+ else:
52
+ raise e
53
+
54
+ @pl_mcp.tool()
55
+ async def violin(request: ViolinModel,):
56
+ """Plot violin plot of one or more variables."""
57
+ try:
58
+ result = await forward_request("pl_violin", request)
59
+ if result is not None:
60
+ return result
61
+ adata = get_ads().get_adata(request=request)
62
+ fig_path = sc_like_plot(sc.pl.violin, adata, request)
63
+ return {"figpath": fig_path}
64
+ except KeyError as e:
65
+ raise f"doest found {e} in current sampleid with adtype {request.adtype}"
66
+ except Exception as e:
67
+ if hasattr(e, '__context__') and e.__context__:
68
+ raise Exception(f"{str(e.__context__)}")
69
+ else:
70
+ raise e
71
+
72
+
73
+ @pl_mcp.tool()
74
+ async def stacked_violin(request: StackedViolinModel = StackedViolinModel()):
75
+ """Plot stacked violin plots. Makes a compact image composed of individual violin plots stacked on top of each other."""
76
+ try:
77
+ result = await forward_request("pl_stacked_violin", request)
78
+ if result is not None:
79
+ return result
80
+ adata = get_ads().get_adata(request=request)
81
+ fig_path = sc_like_plot(sc.pl.stacked_violin, adata, request)
82
+ return {"figpath": fig_path}
83
+ except KeyError as e:
84
+ raise e
85
+ except Exception as e:
86
+ if hasattr(e, '__context__') and e.__context__:
87
+ raise Exception(f"{str(e.__context__)}")
88
+ else:
89
+ raise e
90
+
91
+
92
+ @pl_mcp.tool()
93
+ async def heatmap(request: HeatmapModel):
94
+ """Heatmap of the expression values of genes."""
95
+ try:
96
+ result = await forward_request("pl_heatmap", request)
97
+ if result is not None:
98
+ return result
99
+ adata = get_ads().get_adata(request=request)
100
+ fig_path = sc_like_plot(sc.pl.heatmap, adata, request)
101
+ return {"figpath": fig_path}
102
+ except KeyError as e:
103
+ raise e
104
+ except Exception as e:
105
+ if hasattr(e, '__context__') and e.__context__:
106
+ raise Exception(f"{str(e.__context__)}")
107
+ else:
108
+ raise e
109
+
110
+
111
+ @pl_mcp.tool()
112
+ async def dotplot(request: DotplotModel):
113
+ """Plot dot plot of expression values per gene for each group."""
114
+ try:
115
+ result = await forward_request("pl_dotplot", request)
116
+ if result is not None:
117
+ return result
118
+ adata = get_ads().get_adata(request=request)
119
+ fig_path = sc_like_plot(sc.pl.dotplot, adata, request)
120
+ return {"figpath": fig_path}
121
+ except KeyError as e:
122
+ raise e
123
+ except Exception as e:
124
+ if hasattr(e, '__context__') and e.__context__:
125
+ raise Exception(f"{str(e.__context__)}")
126
+ else:
127
+ raise e
128
+
129
+ @pl_mcp.tool()
130
+ async def matrixplot(request: MatrixplotModel):
131
+ """matrixplot, Create a heatmap of the mean expression values per group of each var_names."""
132
+ try:
133
+ result = await forward_request("pl_matrixplot", request)
134
+ if result is not None:
135
+ return result
136
+ adata = get_ads().get_adata(request=request)
137
+ fig_path = sc_like_plot(sc.pl.matrixplot, adata, request)
138
+ return {"figpath": fig_path}
139
+ except KeyError as e:
140
+ raise e
141
+ except Exception as e:
142
+ if hasattr(e, '__context__') and e.__context__:
143
+ raise Exception(f"{str(e.__context__)}")
144
+ else:
145
+ raise e
146
+
147
+
148
+ @pl_mcp.tool()
149
+ async def tracksplot(request: TracksplotModel):
150
+ """tracksplot, compact plot of expression of a list of genes."""
151
+ try:
152
+ result = await forward_request("pl_tracksplot", request)
153
+ if result is not None:
154
+ return result
155
+ adata = get_ads().get_adata(request=request)
156
+ fig_path = sc_like_plot(sc.pl.tracksplot, adata, request)
157
+ return {"figpath": fig_path}
158
+ except KeyError as e:
159
+ raise e
160
+ except Exception as e:
161
+ if hasattr(e, '__context__') and e.__context__:
162
+ raise Exception(f"{str(e.__context__)}")
163
+ else:
164
+ raise e
165
+
166
+ @pl_mcp.tool()
167
+ async def scatter(request: EnhancedScatterModel = EnhancedScatterModel()):
168
+ """Plot a scatter plot of two variables, Scatter plot along observations or variables axes."""
169
+ try:
170
+ result = await forward_request("pl_scatter", request)
171
+ if result is not None:
172
+ return result
173
+ adata = get_ads().get_adata(request=request)
174
+ fig_path = sc_like_plot(sc.pl.scatter, adata, request)
175
+ return {"figpath": fig_path}
176
+ except KeyError as e:
177
+ raise e
178
+ except Exception as e:
179
+ if hasattr(e, '__context__') and e.__context__:
180
+ raise Exception(f"{str(e.__context__)}")
181
+ else:
182
+ raise e
183
+
184
+ @pl_mcp.tool()
185
+ async def embedding(request: EmbeddingModel):
186
+ """Scatter plot for user specified embedding basis (e.g. umap, tsne, etc)."""
187
+ try:
188
+ result = await forward_request("pl_embedding", request)
189
+ if result is not None:
190
+ return result
191
+ adata = get_ads().get_adata(request=request)
192
+ fig_path = sc_like_plot(sc.pl.embedding, adata, request)
193
+ return {"figpath": fig_path}
194
+ except KeyError as e:
195
+ from fastmcp.exceptions import ToolError
196
+ raise ToolError(f"doest found {e} in current sampleid with adtype {request.adtype}")
197
+ except Exception as e:
198
+ if hasattr(e, '__context__') and e.__context__:
199
+ raise Exception(f"{str(e.__context__)}")
200
+ else:
201
+ raise e
202
+
203
+
204
+ @pl_mcp.tool()
205
+ async def embedding_density(request: EmbeddingDensityModel):
206
+ """Plot the density of cells in an embedding."""
207
+ try:
208
+ result = await forward_request("pl_embedding_density", request)
209
+ if result is not None:
210
+ return result
211
+ adata = get_ads().get_adata(request=request)
212
+ fig_path = sc_like_plot(sc.pl.embedding_density, adata, request)
213
+ return {"figpath": fig_path}
214
+ except KeyError as e:
215
+ raise e
216
+ except Exception as e:
217
+ if hasattr(e, '__context__') and e.__context__:
218
+ raise Exception(f"{str(e.__context__)}")
219
+ else:
220
+ raise e
221
+
222
+ @pl_mcp.tool()
223
+ async def rank_genes_groups(request: RankGenesGroupsModel):
224
+ """Plot ranking of genes based on differential expression."""
225
+ try:
226
+ result = await forward_request("pl_rank_genes_groups", request)
227
+ if result is not None:
228
+ return result
229
+ adata = get_ads().get_adata(request=request)
230
+ fig_path = sc_like_plot(sc.pl.rank_genes_groups, adata, request)
231
+ return {"figpath": fig_path}
232
+ except KeyError as e:
233
+ raise e
234
+ except Exception as e:
235
+ if hasattr(e, '__context__') and e.__context__:
236
+ raise Exception(f"{str(e.__context__)}")
237
+ else:
238
+ raise e
239
+
240
+
241
+ @pl_mcp.tool()
242
+ async def rank_genes_groups_dotplot(
243
+ request: RankGenesGroupsDotplotModel,
244
+ ):
245
+ """Plot ranking of genes(DEGs) using dotplot visualization. Defualt plot DEGs for rank_genes_groups tool"""
246
+ try:
247
+ result = await forward_request("pl_rank_genes_groups_dotplot", request)
248
+ if result is not None:
249
+ return result
250
+ adata = get_ads().get_adata(request=request)
251
+ fig_path = sc_like_plot(sc.pl.rank_genes_groups_dotplot, adata, request)
252
+ return {"figpath": fig_path}
253
+ except KeyError as e:
254
+ raise e
255
+ except Exception as e:
256
+ if hasattr(e, '__context__') and e.__context__:
257
+ raise Exception(f"{str(e.__context__)}")
258
+ else:
259
+ raise e
260
+
261
+
262
+ @pl_mcp.tool()
263
+ async def clustermap(
264
+ request: ClusterMapModel = ClusterMapModel()
265
+ ):
266
+ """Plot hierarchical clustering of cells and genes."""
267
+ try:
268
+ result = await forward_request("pl_clustermap", request)
269
+ if result is not None:
270
+ return result
271
+ adata = get_ads().get_adata(request=request)
272
+ fig_path = sc_like_plot(sc.pl.clustermap, adata, request)
273
+ return {"figpath": fig_path}
274
+ except KeyError as e:
275
+ raise e
276
+ except Exception as e:
277
+ if hasattr(e, '__context__') and e.__context__:
278
+ raise Exception(f"{str(e.__context__)}")
279
+ else:
280
+ raise e
281
+
282
+ @pl_mcp.tool()
283
+ async def highly_variable_genes(
284
+ request: HighlyVariableGenesModel = HighlyVariableGenesModel()
285
+ ):
286
+ """plot highly variable genes; Plot dispersions or normalized variance versus means for genes."""
287
+ try:
288
+ result = await forward_request("pl_highly_variable_genes", request)
289
+ if result is not None:
290
+ return result
291
+ adata = get_ads().get_adata(request=request)
292
+ fig_path = sc_like_plot(sc.pl.highly_variable_genes, adata, request)
293
+ return {"figpath": fig_path}
294
+ except KeyError as e:
295
+ raise e
296
+ except Exception as e:
297
+ if hasattr(e, '__context__') and e.__context__:
298
+ raise Exception(f"{str(e.__context__)}")
299
+ else:
300
+ raise e
301
+
302
+
303
+ @pl_mcp.tool()
304
+ async def pca_variance_ratio(
305
+ request: PCAVarianceRatioModel = PCAVarianceRatioModel()
306
+ ):
307
+ """Plot the PCA variance ratio to visualize explained variance."""
308
+ try:
309
+ result = await forward_request("pl_pca_variance_ratio", request)
310
+ if result is not None:
311
+ return result
312
+ adata = get_ads().get_adata(request=request)
313
+ fig_path = sc_like_plot(sc.pl.pca_variance_ratio, adata, request)
314
+ return {"figpath": fig_path}
315
+ except KeyError as e:
316
+ raise e
317
+ except Exception as e:
318
+ if hasattr(e, '__context__') and e.__context__:
319
+ raise Exception(f"{str(e.__context__)}")
320
+ else:
321
+ raise e
@@ -0,0 +1,363 @@
1
+
2
+ import os
3
+ import inspect
4
+ import scanpy as sc
5
+ from fastmcp import FastMCP , Context
6
+ from ..schema.pp import *
7
+ from ..util import filter_args, add_op_log, forward_request, get_ads, generate_msg
8
+ from ..logging_config import setup_logger
9
+ logger = setup_logger()
10
+
11
+
12
+ pp_mcp = FastMCP("ScanpyMCP-PP-Server")
13
+
14
+
15
+ @pp_mcp.tool()
16
+ async def subset_cells(
17
+ request: SubsetCellModel = SubsetCellModel()
18
+ ):
19
+ """filter or subset cells based on total genes expressed counts and numbers. or values in adata.obs[obs_key]"""
20
+
21
+ try:
22
+ result = await forward_request("subset_cells", request)
23
+ if result is not None:
24
+ return result
25
+
26
+ ads = get_ads()
27
+ adata = ads.get_adata(request=request).copy()
28
+ func_kwargs = filter_args(request, sc.pp.filter_cells)
29
+ if func_kwargs:
30
+ sc.pp.filter_cells(adata, **func_kwargs)
31
+ add_op_log(adata, sc.pp.filter_cells, func_kwargs)
32
+ # Subset based on obs (cells) criteria
33
+ if request.obs_key is not None:
34
+ if request.obs_key not in adata.obs.columns:
35
+ raise ValueError(f"Key '{request.obs_key}' not found in adata.obs")
36
+ mask = True # Start with all cells selected
37
+ if request.obs_value is not None:
38
+ mask = mask & (adata.obs[request.obs_key] == request.obs_value)
39
+ if request.obs_min is not None:
40
+ mask = mask & (adata.obs[request.obs_key] >= request.obs_min)
41
+ if request.obs_max is not None:
42
+ mask = mask & (adata.obs[request.obs_key] <= request.obs_max)
43
+ adata = adata[mask, :]
44
+ add_op_log(adata, "subset_cells",
45
+ {
46
+ "obs_key": request.obs_key, "obs_value": request.obs_value,
47
+ "obs_min": request.obs_min, "obs_max": request.obs_max
48
+ }
49
+ )
50
+ ads.set_adata(adata, request=request)
51
+ return [
52
+ generate_msg(request, adata, ads)
53
+ ]
54
+ except KeyError as e:
55
+ raise e
56
+ except Exception as e:
57
+ if hasattr(e, '__context__') and e.__context__:
58
+ raise Exception(f"{str(e.__context__)}")
59
+ else:
60
+ raise e
61
+
62
+
63
+ @pp_mcp.tool()
64
+ async def subset_genes(
65
+ request: SubsetGeneModel = SubsetGeneModel()
66
+ ):
67
+ """filter or subset genes based on number of cells or counts, or values in adata.var[var_key] or subset highly variable genes"""
68
+ try:
69
+ result = await forward_request("pp_subset_genes", request)
70
+ if result is not None:
71
+ return result
72
+ func_kwargs = filter_args(request, sc.pp.filter_genes)
73
+ ads = get_ads()
74
+ adata = ads.get_adata(request=request).copy()
75
+ if func_kwargs:
76
+ sc.pp.filter_genes(adata, **func_kwargs)
77
+ add_op_log(adata, sc.pp.filter_genes, func_kwargs)
78
+ if request.var_key is not None:
79
+ if request.var_key not in adata.var.columns:
80
+ raise ValueError(f"Key '{request.var_key}' not found in adata.var")
81
+ mask = True # Start with all genes selected
82
+ if request.var_min is not None:
83
+ mask = mask & (adata.var[request.var_key] >= request.var_min)
84
+ if request.var_max is not None:
85
+ mask = mask & (adata.var[request.var_key] <= request.var_max)
86
+ adata = adata[:, mask]
87
+ if request.highly_variable is not None:
88
+ adata = adata[:, adata.var.highly_variable]
89
+ add_op_log(adata, "subset_genes",
90
+ {
91
+ "var_key": request.var_key, "var_value": request.var_value,
92
+ "var_min": request.var_min, "var_max": request.var_max, "hpv": request.highly_variable
93
+ }
94
+ )
95
+ ads.set_adata(adata, request=request)
96
+ return [
97
+ generate_msg(request, adata, ads)
98
+ ]
99
+ except KeyError as e:
100
+ raise e
101
+ except Exception as e:
102
+ if hasattr(e, '__context__') and e.__context__:
103
+ raise Exception(f"{str(e.__context__)}")
104
+ else:
105
+ raise e
106
+
107
+ @pp_mcp.tool()
108
+ async def calculate_qc_metrics(
109
+ request: CalculateQCMetrics = CalculateQCMetrics()
110
+ ):
111
+ """Calculate quality control metrics(common metrics: total counts, gene number, percentage of counts in ribosomal and mitochondrial) for AnnData."""
112
+
113
+ try:
114
+ result = await forward_request("pp_calculate_qc_metrics", request)
115
+ if result is not None:
116
+ return result
117
+ logger.info(f"calculate_qc_metrics {request.model_dump()}")
118
+ func_kwargs = filter_args(request, sc.pp.calculate_qc_metrics)
119
+ ads = get_ads()
120
+ adata = ads.get_adata(request=request)
121
+ func_kwargs["inplace"] = True
122
+ try:
123
+ sc.pp.calculate_qc_metrics(adata, **func_kwargs)
124
+ add_op_log(adata, sc.pp.calculate_qc_metrics, func_kwargs)
125
+ except KeyError as e:
126
+ raise KeyError(f"Cound find {e} in adata.var")
127
+ return [
128
+ generate_msg(request, adata, ads)
129
+ ]
130
+ except Exception as e:
131
+ if hasattr(e, '__context__') and e.__context__:
132
+ raise Exception(f"{str(e.__context__)}")
133
+ else:
134
+ raise e
135
+
136
+
137
+ @pp_mcp.tool()
138
+ async def log1p(
139
+ request: Log1PModel = Log1PModel()
140
+ ):
141
+ """Logarithmize the data matrix"""
142
+
143
+ try:
144
+ result = await forward_request("pp_log1p", request)
145
+ if result is not None:
146
+ return result
147
+ func_kwargs = filter_args(request, sc.pp.log1p)
148
+ ads = get_ads()
149
+ adata = ads.get_adata(request=request).copy()
150
+ try:
151
+ sc.pp.log1p(adata, **func_kwargs)
152
+ adata.raw = adata.copy()
153
+ add_op_log(adata, sc.pp.log1p, func_kwargs)
154
+ except Exception as e:
155
+ raise e
156
+ ads.set_adata(adata, request=request)
157
+ return [
158
+ generate_msg(request, adata, ads)
159
+ ]
160
+ except KeyError as e:
161
+ raise e
162
+ except Exception as e:
163
+ if hasattr(e, '__context__') and e.__context__:
164
+ raise Exception(f"{str(e.__context__)}")
165
+ else:
166
+ raise e
167
+
168
+
169
+ @pp_mcp.tool()
170
+ async def normalize_total(
171
+ request: NormalizeTotalModel = NormalizeTotalModel()
172
+ ):
173
+ """Normalize counts per cell to the same total count"""
174
+
175
+ try:
176
+ result = await forward_request("pp_normalize_total", request)
177
+ if result is not None:
178
+ return result
179
+ func_kwargs = filter_args(request, sc.pp.normalize_total)
180
+ ads = get_ads()
181
+ adata = ads.get_adata(request=request).copy()
182
+ sc.pp.normalize_total(adata, **func_kwargs)
183
+ add_op_log(adata, sc.pp.normalize_total, func_kwargs)
184
+ ads.set_adata(adata, request=request)
185
+ return [
186
+ generate_msg(request, adata, ads)
187
+ ]
188
+ except KeyError as e:
189
+ raise e
190
+ except Exception as e:
191
+ if hasattr(e, '__context__') and e.__context__:
192
+ raise Exception(f"{str(e.__context__)}")
193
+ else:
194
+ raise e
195
+
196
+
197
+
198
+ @pp_mcp.tool()
199
+ async def highly_variable_genes(
200
+ request: HighlyVariableGenesModel = HighlyVariableGenesModel()
201
+ ):
202
+ """Annotate highly variable genes"""
203
+
204
+ try:
205
+ result = await forward_request("pp_highly_variable_genes", request)
206
+ if result is not None:
207
+ return result
208
+ try:
209
+ func_kwargs = filter_args(request, sc.pp.highly_variable_genes)
210
+ ads = get_ads()
211
+ adata = ads.get_adata(request=request)
212
+ sc.pp.highly_variable_genes(adata, **func_kwargs)
213
+ add_op_log(adata, sc.pp.highly_variable_genes, func_kwargs)
214
+ except Exception as e:
215
+ logger.error(f"Error in pp_highly_variable_genes: {str(e)}")
216
+ raise e
217
+ return [
218
+ generate_msg(request, adata, ads)
219
+ ]
220
+ except KeyError as e:
221
+ raise e
222
+ except Exception as e:
223
+ if hasattr(e, '__context__') and e.__context__:
224
+ raise Exception(f"{str(e.__context__)}")
225
+ else:
226
+ raise e
227
+
228
+
229
+ @pp_mcp.tool()
230
+ async def regress_out(
231
+ request: RegressOutModel,
232
+
233
+ ):
234
+ """Regress out (mostly) unwanted sources of variation."""
235
+
236
+ try:
237
+ result = await forward_request("pp_regress_out", request)
238
+ if result is not None:
239
+ return result
240
+ func_kwargs = filter_args(request, sc.pp.regress_out)
241
+ ads = get_ads()
242
+ adata = ads.get_adata(request=request).copy()
243
+ sc.pp.regress_out(adata, **func_kwargs)
244
+ add_op_log(adata, sc.pp.regress_out, func_kwargs)
245
+ ads.set_adata(adata, request=request)
246
+ return [
247
+ generate_msg(request, adata, ads)
248
+ ]
249
+ except KeyError as e:
250
+ raise e
251
+ except Exception as e:
252
+ if hasattr(e, '__context__') and e.__context__:
253
+ raise Exception(f"{str(e.__context__)}")
254
+ else:
255
+ raise e
256
+
257
+ @pp_mcp.tool()
258
+ async def scale(
259
+ request: ScaleModel = ScaleModel()
260
+ ):
261
+ """Scale data to unit variance and zero mean"""
262
+
263
+ try:
264
+ result = await forward_request("pp_scale", request)
265
+ if result is not None:
266
+ return result
267
+ func_kwargs = filter_args(request, sc.pp.scale)
268
+ ads = get_ads()
269
+ adata = ads.get_adata(request=request).copy()
270
+
271
+ sc.pp.scale(adata, **func_kwargs)
272
+ add_op_log(adata, sc.pp.scale, func_kwargs)
273
+
274
+ ads.set_adata(adata, request=request)
275
+ return [
276
+ generate_msg(request, adata, ads)
277
+ ]
278
+ except KeyError as e:
279
+ raise e
280
+ except Exception as e:
281
+ if hasattr(e, '__context__') and e.__context__:
282
+ raise Exception(f"{str(e.__context__)}")
283
+ else:
284
+ raise e
285
+
286
+ @pp_mcp.tool()
287
+ async def combat(
288
+ request: CombatModel = CombatModel()
289
+ ):
290
+ """ComBat function for batch effect correction"""
291
+
292
+ try:
293
+ result = await forward_request("pp_combat", request)
294
+ if result is not None:
295
+ return result
296
+ func_kwargs = filter_args(request, sc.pp.combat)
297
+ ads = get_ads()
298
+ adata = ads.get_adata(request=request).copy()
299
+
300
+ sc.pp.combat(adata, **func_kwargs)
301
+ add_op_log(adata, sc.pp.combat, func_kwargs)
302
+
303
+ ads.set_adata(adata, request=request)
304
+ return [
305
+ generate_msg(request, adata, ads)
306
+ ]
307
+ except KeyError as e:
308
+ raise e
309
+ except Exception as e:
310
+ if hasattr(e, '__context__') and e.__context__:
311
+ raise Exception(f"{str(e.__context__)}")
312
+ else:
313
+ raise e
314
+
315
+ @pp_mcp.tool()
316
+ async def scrublet(
317
+ request: ScrubletModel = ScrubletModel()
318
+ ):
319
+ """Predict doublets using Scrublet"""
320
+
321
+ try:
322
+ result = await forward_request("pp_scrublet", request)
323
+ if result is not None:
324
+ return result
325
+ func_kwargs = filter_args(request, sc.pp.scrublet)
326
+ ads = get_ads()
327
+ adata = ads.get_adata(request=request)
328
+ sc.pp.scrublet(adata, **func_kwargs)
329
+ add_op_log(adata, sc.pp.scrublet, func_kwargs)
330
+ return [
331
+ generate_msg(request, adata, ads)
332
+ ]
333
+ except Exception as e:
334
+ if hasattr(e, '__context__') and e.__context__:
335
+ raise Exception(f"{str(e.__context__)}")
336
+ else:
337
+ raise e
338
+
339
+ @pp_mcp.tool()
340
+ async def neighbors(
341
+ request: NeighborsModel = NeighborsModel()
342
+ ):
343
+ """Compute nearest neighbors distance matrix and neighborhood graph"""
344
+
345
+ try:
346
+ result = await forward_request("pp_neighbors", request)
347
+ if result is not None:
348
+ return result
349
+ func_kwargs = filter_args(request, sc.pp.neighbors)
350
+ ads = get_ads()
351
+ adata = ads.get_adata(request=request)
352
+ sc.pp.neighbors(adata, **func_kwargs)
353
+ add_op_log(adata, sc.pp.neighbors, func_kwargs)
354
+ return [
355
+ generate_msg(request, adata, ads)
356
+ ]
357
+ except KeyError as e:
358
+ raise e
359
+ except Exception as e:
360
+ if hasattr(e, '__context__') and e.__context__:
361
+ raise Exception(f"{str(e.__context__)}")
362
+ else:
363
+ raise e