scmcp-shared 0.2.1__py3-none-any.whl → 0.2.5__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/__init__.py +13 -1
- scmcp_shared/schema/io.py +4 -4
- scmcp_shared/schema/pl.py +2 -3
- scmcp_shared/schema/pp.py +15 -15
- scmcp_shared/schema/tl.py +18 -19
- scmcp_shared/schema/util.py +11 -11
- scmcp_shared/server/__init__.py +6 -6
- scmcp_shared/server/io.py +17 -12
- scmcp_shared/server/pl.py +107 -65
- scmcp_shared/server/pp.py +75 -64
- scmcp_shared/server/tl.py +99 -84
- scmcp_shared/server/util.py +39 -29
- scmcp_shared/util.py +23 -28
- {scmcp_shared-0.2.1.dist-info → scmcp_shared-0.2.5.dist-info}/METADATA +1 -1
- scmcp_shared-0.2.5.dist-info/RECORD +19 -0
- scmcp_shared/schema/base.py +0 -11
- scmcp_shared-0.2.1.dist-info/RECORD +0 -20
- {scmcp_shared-0.2.1.dist-info → scmcp_shared-0.2.5.dist-info}/WHEEL +0 -0
- {scmcp_shared-0.2.1.dist-info → scmcp_shared-0.2.5.dist-info}/licenses/LICENSE +0 -0
scmcp_shared/server/tl.py
CHANGED
@@ -3,6 +3,7 @@ import os
|
|
3
3
|
import scanpy as sc
|
4
4
|
from fastmcp.exceptions import ToolError
|
5
5
|
from ..schema.tl import *
|
6
|
+
from ..schema import AdataModel
|
6
7
|
from scmcp_shared.util import filter_args, add_op_log, forward_request, get_ads, generate_msg
|
7
8
|
from scmcp_shared.logging_config import setup_logger
|
8
9
|
logger = setup_logger()
|
@@ -12,20 +13,21 @@ tl_mcp = FastMCP("ScanpyMCP-TL-Server")
|
|
12
13
|
|
13
14
|
@tl_mcp.tool()
|
14
15
|
async def tsne(
|
15
|
-
request: TSNEModel = TSNEModel()
|
16
|
+
request: TSNEModel = TSNEModel(),
|
17
|
+
adinfo: AdataModel = AdataModel()
|
16
18
|
):
|
17
19
|
"""t-distributed stochastic neighborhood embedding (t-SNE) for visualization"""
|
18
20
|
|
19
21
|
try:
|
20
|
-
result = await forward_request("tl_tsne", request)
|
22
|
+
result = await forward_request("tl_tsne", request, adinfo)
|
21
23
|
if result is not None:
|
22
24
|
return result
|
23
25
|
func_kwargs = filter_args(request, sc.tl.tsne)
|
24
26
|
ads = get_ads()
|
25
|
-
adata = ads.get_adata(
|
27
|
+
adata = ads.get_adata(adinfo=adinfo)
|
26
28
|
sc.tl.tsne(adata, **func_kwargs)
|
27
|
-
add_op_log(adata, sc.tl.tsne, func_kwargs)
|
28
|
-
return generate_msg(
|
29
|
+
add_op_log(adata, sc.tl.tsne, func_kwargs, adinfo)
|
30
|
+
return generate_msg(adinfo, adata, ads)
|
29
31
|
except ToolError as e:
|
30
32
|
raise ToolError(e)
|
31
33
|
except Exception as e:
|
@@ -37,20 +39,21 @@ async def tsne(
|
|
37
39
|
|
38
40
|
@tl_mcp.tool()
|
39
41
|
async def umap(
|
40
|
-
request: UMAPModel = UMAPModel()
|
42
|
+
request: UMAPModel = UMAPModel(),
|
43
|
+
adinfo: AdataModel = AdataModel()
|
41
44
|
):
|
42
45
|
"""Uniform Manifold Approximation and Projection (UMAP) for visualization"""
|
43
46
|
|
44
47
|
try:
|
45
|
-
result = await forward_request("tl_umap", request)
|
48
|
+
result = await forward_request("tl_umap", request, adinfo)
|
46
49
|
if result is not None:
|
47
50
|
return result
|
48
51
|
func_kwargs = filter_args(request, sc.tl.umap)
|
49
52
|
ads = get_ads()
|
50
|
-
adata = ads.get_adata(
|
53
|
+
adata = ads.get_adata(adinfo=adinfo)
|
51
54
|
sc.tl.umap(adata, **func_kwargs)
|
52
|
-
add_op_log(adata, sc.tl.umap, func_kwargs)
|
53
|
-
return [generate_msg(
|
55
|
+
add_op_log(adata, sc.tl.umap, func_kwargs, adinfo)
|
56
|
+
return [generate_msg(adinfo, adata, ads)]
|
54
57
|
except ToolError as e:
|
55
58
|
raise ToolError(e)
|
56
59
|
except Exception as e:
|
@@ -61,20 +64,21 @@ async def umap(
|
|
61
64
|
|
62
65
|
@tl_mcp.tool()
|
63
66
|
async def draw_graph(
|
64
|
-
request: DrawGraphModel = DrawGraphModel()
|
67
|
+
request: DrawGraphModel = DrawGraphModel(),
|
68
|
+
adinfo: AdataModel = AdataModel()
|
65
69
|
):
|
66
70
|
"""Force-directed graph drawing"""
|
67
71
|
|
68
72
|
try:
|
69
|
-
result = await forward_request("tl_draw_graph", request)
|
73
|
+
result = await forward_request("tl_draw_graph", request, adinfo)
|
70
74
|
if result is not None:
|
71
75
|
return result
|
72
76
|
func_kwargs = filter_args(request, sc.tl.draw_graph)
|
73
77
|
ads = get_ads()
|
74
|
-
adata = ads.get_adata(
|
78
|
+
adata = ads.get_adata(adinfo=adinfo)
|
75
79
|
sc.tl.draw_graph(adata, **func_kwargs)
|
76
|
-
add_op_log(adata, sc.tl.draw_graph, func_kwargs)
|
77
|
-
return [generate_msg(
|
80
|
+
add_op_log(adata, sc.tl.draw_graph, func_kwargs, adinfo)
|
81
|
+
return [generate_msg(adinfo, adata, ads)]
|
78
82
|
except ToolError as e:
|
79
83
|
raise ToolError(e)
|
80
84
|
except Exception as e:
|
@@ -85,21 +89,22 @@ async def draw_graph(
|
|
85
89
|
|
86
90
|
@tl_mcp.tool()
|
87
91
|
async def diffmap(
|
88
|
-
request: DiffMapModel = DiffMapModel()
|
92
|
+
request: DiffMapModel = DiffMapModel(),
|
93
|
+
adinfo: AdataModel = AdataModel()
|
89
94
|
):
|
90
95
|
"""Diffusion Maps for dimensionality reduction"""
|
91
96
|
|
92
97
|
try:
|
93
|
-
result = await forward_request("tl_diffmap", request)
|
98
|
+
result = await forward_request("tl_diffmap", request, adinfo)
|
94
99
|
if result is not None:
|
95
100
|
return result
|
96
101
|
func_kwargs = filter_args(request, sc.tl.diffmap)
|
97
102
|
ads = get_ads()
|
98
|
-
adata = ads.get_adata(
|
103
|
+
adata = ads.get_adata(adinfo=adinfo)
|
99
104
|
sc.tl.diffmap(adata, **func_kwargs)
|
100
105
|
adata.obsm["X_diffmap"] = adata.obsm["X_diffmap"][:,1:]
|
101
|
-
add_op_log(adata, sc.tl.diffmap, func_kwargs)
|
102
|
-
return [generate_msg(
|
106
|
+
add_op_log(adata, sc.tl.diffmap, func_kwargs, adinfo)
|
107
|
+
return [generate_msg(adinfo, adata, ads)]
|
103
108
|
except ToolError as e:
|
104
109
|
raise ToolError(e)
|
105
110
|
except Exception as e:
|
@@ -110,20 +115,21 @@ async def diffmap(
|
|
110
115
|
|
111
116
|
@tl_mcp.tool()
|
112
117
|
async def embedding_density(
|
113
|
-
request: EmbeddingDensityModel = EmbeddingDensityModel()
|
118
|
+
request: EmbeddingDensityModel = EmbeddingDensityModel(),
|
119
|
+
adinfo: AdataModel = AdataModel()
|
114
120
|
):
|
115
121
|
"""Calculate the density of cells in an embedding"""
|
116
122
|
|
117
123
|
try:
|
118
|
-
result = await forward_request("tl_embedding_density", request)
|
124
|
+
result = await forward_request("tl_embedding_density", request, adinfo)
|
119
125
|
if result is not None:
|
120
126
|
return result
|
121
127
|
func_kwargs = filter_args(request, sc.tl.embedding_density)
|
122
128
|
ads = get_ads()
|
123
|
-
adata = ads.get_adata(
|
129
|
+
adata = ads.get_adata(adinfo=adinfo)
|
124
130
|
sc.tl.embedding_density(adata, **func_kwargs)
|
125
|
-
add_op_log(adata, sc.tl.embedding_density, func_kwargs)
|
126
|
-
return [generate_msg(
|
131
|
+
add_op_log(adata, sc.tl.embedding_density, func_kwargs, adinfo)
|
132
|
+
return [generate_msg(adinfo, adata, ads)]
|
127
133
|
except ToolError as e:
|
128
134
|
raise ToolError(e)
|
129
135
|
except Exception as e:
|
@@ -135,20 +141,21 @@ async def embedding_density(
|
|
135
141
|
|
136
142
|
@tl_mcp.tool()
|
137
143
|
async def leiden(
|
138
|
-
request: LeidenModel = LeidenModel()
|
144
|
+
request: LeidenModel = LeidenModel(),
|
145
|
+
adinfo: AdataModel = AdataModel()
|
139
146
|
):
|
140
147
|
"""Leiden clustering algorithm for community detection"""
|
141
148
|
|
142
149
|
try:
|
143
|
-
result = await forward_request("tl_leiden", request)
|
150
|
+
result = await forward_request("tl_leiden", request, adinfo)
|
144
151
|
if result is not None:
|
145
152
|
return result
|
146
153
|
func_kwargs = filter_args(request, sc.tl.leiden)
|
147
154
|
ads = get_ads()
|
148
|
-
adata = ads.get_adata(
|
155
|
+
adata = ads.get_adata(adinfo=adinfo)
|
149
156
|
sc.tl.leiden(adata, **func_kwargs)
|
150
|
-
add_op_log(adata, sc.tl.leiden, func_kwargs)
|
151
|
-
return [generate_msg(
|
157
|
+
add_op_log(adata, sc.tl.leiden, func_kwargs, adinfo)
|
158
|
+
return [generate_msg(adinfo, adata, ads)]
|
152
159
|
except ToolError as e:
|
153
160
|
raise ToolError(e)
|
154
161
|
except Exception as e:
|
@@ -160,20 +167,21 @@ async def leiden(
|
|
160
167
|
|
161
168
|
@tl_mcp.tool()
|
162
169
|
async def louvain(
|
163
|
-
request: LouvainModel = LouvainModel()
|
170
|
+
request: LouvainModel = LouvainModel(),
|
171
|
+
adinfo: AdataModel = AdataModel()
|
164
172
|
):
|
165
173
|
"""Louvain clustering algorithm for community detection"""
|
166
174
|
|
167
175
|
try:
|
168
|
-
result = await forward_request("tl_louvain", request)
|
176
|
+
result = await forward_request("tl_louvain", request, adinfo)
|
169
177
|
if result is not None:
|
170
178
|
return result
|
171
179
|
func_kwargs = filter_args(request, sc.tl.louvain)
|
172
180
|
ads = get_ads()
|
173
|
-
adata = ads.get_adata(
|
181
|
+
adata = ads.get_adata(adinfo=adinfo)
|
174
182
|
sc.tl.louvain(adata, **func_kwargs)
|
175
|
-
add_op_log(adata, sc.tl.louvain, func_kwargs)
|
176
|
-
return [generate_msg(
|
183
|
+
add_op_log(adata, sc.tl.louvain, func_kwargs, adinfo)
|
184
|
+
return [generate_msg(adinfo, adata, ads)]
|
177
185
|
except ToolError as e:
|
178
186
|
raise ToolError(e)
|
179
187
|
except Exception as e:
|
@@ -185,19 +193,20 @@ async def louvain(
|
|
185
193
|
@tl_mcp.tool()
|
186
194
|
async def dendrogram(
|
187
195
|
request: DendrogramModel,
|
196
|
+
adinfo: AdataModel = AdataModel()
|
188
197
|
):
|
189
198
|
"""Hierarchical clustering dendrogram"""
|
190
199
|
|
191
200
|
try:
|
192
|
-
result = await forward_request("tl_dendrogram", request)
|
201
|
+
result = await forward_request("tl_dendrogram", request, adinfo)
|
193
202
|
if result is not None:
|
194
203
|
return result
|
195
204
|
func_kwargs = filter_args(request, sc.tl.dendrogram)
|
196
205
|
ads = get_ads()
|
197
|
-
adata = ads.get_adata(
|
206
|
+
adata = ads.get_adata(adinfo=adinfo)
|
198
207
|
sc.tl.dendrogram(adata, **func_kwargs)
|
199
|
-
add_op_log(adata, sc.tl.dendrogram, func_kwargs)
|
200
|
-
return [generate_msg(
|
208
|
+
add_op_log(adata, sc.tl.dendrogram, func_kwargs, adinfo)
|
209
|
+
return [generate_msg(adinfo, adata, ads)]
|
201
210
|
except ToolError as e:
|
202
211
|
raise ToolError(e)
|
203
212
|
except Exception as e:
|
@@ -208,20 +217,21 @@ async def dendrogram(
|
|
208
217
|
|
209
218
|
@tl_mcp.tool()
|
210
219
|
async def dpt(
|
211
|
-
request: DPTModel = DPTModel()
|
220
|
+
request: DPTModel = DPTModel(),
|
221
|
+
adinfo: AdataModel = AdataModel()
|
212
222
|
):
|
213
223
|
"""Diffusion Pseudotime (DPT) analysis"""
|
214
224
|
|
215
225
|
try:
|
216
|
-
result = await forward_request("tl_dpt", request)
|
226
|
+
result = await forward_request("tl_dpt", request, adinfo)
|
217
227
|
if result is not None:
|
218
228
|
return result
|
219
229
|
func_kwargs = filter_args(request, sc.tl.dpt)
|
220
230
|
ads = get_ads()
|
221
|
-
adata = ads.get_adata(
|
231
|
+
adata = ads.get_adata(adinfo=adinfo)
|
222
232
|
sc.tl.dpt(adata, **func_kwargs)
|
223
|
-
add_op_log(adata, sc.tl.dpt, func_kwargs)
|
224
|
-
return [generate_msg(
|
233
|
+
add_op_log(adata, sc.tl.dpt, func_kwargs, adinfo)
|
234
|
+
return [generate_msg(adinfo, adata, ads)]
|
225
235
|
except ToolError as e:
|
226
236
|
raise ToolError(e)
|
227
237
|
except Exception as e:
|
@@ -233,20 +243,21 @@ async def dpt(
|
|
233
243
|
|
234
244
|
@tl_mcp.tool()
|
235
245
|
async def paga(
|
236
|
-
request: PAGAModel = PAGAModel()
|
246
|
+
request: PAGAModel = PAGAModel(),
|
247
|
+
adinfo: AdataModel = AdataModel()
|
237
248
|
):
|
238
249
|
"""Partition-based graph abstraction"""
|
239
250
|
|
240
251
|
try:
|
241
|
-
result = await forward_request("tl_paga", request)
|
252
|
+
result = await forward_request("tl_paga", request, adinfo)
|
242
253
|
if result is not None:
|
243
254
|
return result
|
244
255
|
func_kwargs = filter_args(request, sc.tl.paga)
|
245
256
|
ads = get_ads()
|
246
|
-
adata = ads.get_adata(
|
257
|
+
adata = ads.get_adata(adinfo=adinfo)
|
247
258
|
sc.tl.paga(adata, **func_kwargs)
|
248
|
-
add_op_log(adata, sc.tl.paga, func_kwargs)
|
249
|
-
return [generate_msg(
|
259
|
+
add_op_log(adata, sc.tl.paga, func_kwargs, adinfo)
|
260
|
+
return [generate_msg(adinfo, adata, ads)]
|
250
261
|
except ToolError as e:
|
251
262
|
raise ToolError(e)
|
252
263
|
except Exception as e:
|
@@ -258,20 +269,21 @@ async def paga(
|
|
258
269
|
|
259
270
|
@tl_mcp.tool()
|
260
271
|
async def ingest(
|
261
|
-
request: IngestModel = IngestModel()
|
272
|
+
request: IngestModel = IngestModel(),
|
273
|
+
adinfo: AdataModel = AdataModel()
|
262
274
|
):
|
263
275
|
"""Map labels and embeddings from reference data to new data"""
|
264
276
|
|
265
277
|
try:
|
266
|
-
result = await forward_request("tl_ingest", request)
|
278
|
+
result = await forward_request("tl_ingest", request, adinfo)
|
267
279
|
if result is not None:
|
268
280
|
return result
|
269
281
|
func_kwargs = filter_args(request, sc.tl.ingest)
|
270
282
|
ads = get_ads()
|
271
|
-
adata = ads.get_adata(
|
283
|
+
adata = ads.get_adata(adinfo=adinfo)
|
272
284
|
sc.tl.ingest(adata, **func_kwargs)
|
273
|
-
add_op_log(adata, sc.tl.ingest, func_kwargs)
|
274
|
-
return [generate_msg(
|
285
|
+
add_op_log(adata, sc.tl.ingest, func_kwargs, adinfo)
|
286
|
+
return [generate_msg(adinfo, adata, ads)]
|
275
287
|
except ToolError as e:
|
276
288
|
raise ToolError(e)
|
277
289
|
except Exception as e:
|
@@ -283,20 +295,20 @@ async def ingest(
|
|
283
295
|
@tl_mcp.tool()
|
284
296
|
async def rank_genes_groups(
|
285
297
|
request: RankGenesGroupsModel,
|
286
|
-
|
298
|
+
adinfo: AdataModel = AdataModel()
|
287
299
|
):
|
288
300
|
"""Rank genes for characterizing groups, for differentially expressison analysis"""
|
289
301
|
|
290
302
|
try:
|
291
|
-
result = await forward_request("tl_rank_genes_groups", request)
|
303
|
+
result = await forward_request("tl_rank_genes_groups", request, adinfo)
|
292
304
|
if result is not None:
|
293
305
|
return result
|
294
306
|
func_kwargs = filter_args(request, sc.tl.rank_genes_groups)
|
295
307
|
ads = get_ads()
|
296
|
-
adata = ads.get_adata(
|
308
|
+
adata = ads.get_adata(adinfo=adinfo)
|
297
309
|
sc.tl.rank_genes_groups(adata, **func_kwargs)
|
298
|
-
add_op_log(adata, sc.tl.rank_genes_groups, func_kwargs)
|
299
|
-
return [generate_msg(
|
310
|
+
add_op_log(adata, sc.tl.rank_genes_groups, func_kwargs, adinfo)
|
311
|
+
return [generate_msg(adinfo, adata, ads)]
|
300
312
|
except ToolError as e:
|
301
313
|
raise ToolError(e)
|
302
314
|
except Exception as e:
|
@@ -308,20 +320,21 @@ async def rank_genes_groups(
|
|
308
320
|
|
309
321
|
@tl_mcp.tool()
|
310
322
|
async def filter_rank_genes_groups(
|
311
|
-
request: FilterRankGenesGroupsModel = FilterRankGenesGroupsModel()
|
323
|
+
request: FilterRankGenesGroupsModel = FilterRankGenesGroupsModel(),
|
324
|
+
adinfo: AdataModel = AdataModel()
|
312
325
|
):
|
313
326
|
"""Filter out genes based on fold change and fraction of genes"""
|
314
327
|
|
315
328
|
try:
|
316
|
-
result = await forward_request("tl_filter_rank_genes_groups", request)
|
329
|
+
result = await forward_request("tl_filter_rank_genes_groups", request, adinfo)
|
317
330
|
if result is not None:
|
318
331
|
return result
|
319
332
|
func_kwargs = filter_args(request, sc.tl.filter_rank_genes_groups)
|
320
333
|
ads = get_ads()
|
321
|
-
adata = ads.get_adata(
|
334
|
+
adata = ads.get_adata(adinfo=adinfo)
|
322
335
|
sc.tl.filter_rank_genes_groups(adata, **func_kwargs)
|
323
|
-
add_op_log(adata, sc.tl.filter_rank_genes_groups, func_kwargs)
|
324
|
-
return [generate_msg(
|
336
|
+
add_op_log(adata, sc.tl.filter_rank_genes_groups, func_kwargs, adinfo)
|
337
|
+
return [generate_msg(adinfo, adata, ads)]
|
325
338
|
except ToolError as e:
|
326
339
|
raise ToolError(e)
|
327
340
|
except Exception as e:
|
@@ -333,20 +346,21 @@ async def filter_rank_genes_groups(
|
|
333
346
|
|
334
347
|
@tl_mcp.tool()
|
335
348
|
async def marker_gene_overlap(
|
336
|
-
request: MarkerGeneOverlapModel = MarkerGeneOverlapModel()
|
349
|
+
request: MarkerGeneOverlapModel = MarkerGeneOverlapModel(),
|
350
|
+
adinfo: AdataModel = AdataModel()
|
337
351
|
):
|
338
352
|
"""Calculate overlap between data-derived marker genes and reference markers"""
|
339
353
|
|
340
354
|
try:
|
341
|
-
result = await forward_request("tl_marker_gene_overlap", request)
|
355
|
+
result = await forward_request("tl_marker_gene_overlap", request, adinfo)
|
342
356
|
if result is not None:
|
343
357
|
return result
|
344
358
|
func_kwargs = filter_args(request, sc.tl.marker_gene_overlap)
|
345
359
|
ads = get_ads()
|
346
|
-
adata = ads.get_adata(
|
360
|
+
adata = ads.get_adata(adinfo=adinfo)
|
347
361
|
sc.tl.marker_gene_overlap(adata, **func_kwargs)
|
348
|
-
add_op_log(adata, sc.tl.marker_gene_overlap, func_kwargs)
|
349
|
-
return [generate_msg(
|
362
|
+
add_op_log(adata, sc.tl.marker_gene_overlap, func_kwargs, adinfo)
|
363
|
+
return [generate_msg(adinfo, adata, ads)]
|
350
364
|
except ToolError as e:
|
351
365
|
raise ToolError(e)
|
352
366
|
except Exception as e:
|
@@ -358,19 +372,19 @@ async def marker_gene_overlap(
|
|
358
372
|
@tl_mcp.tool()
|
359
373
|
async def score_genes(
|
360
374
|
request: ScoreGenesModel,
|
361
|
-
|
375
|
+
adinfo: AdataModel = AdataModel()
|
362
376
|
):
|
363
377
|
"""Score a set of genes based on their average expression"""
|
364
378
|
try:
|
365
|
-
result = await forward_request("tl_score_genes", request)
|
379
|
+
result = await forward_request("tl_score_genes", request, adinfo)
|
366
380
|
if result is not None:
|
367
381
|
return result
|
368
382
|
func_kwargs = filter_args(request, sc.tl.score_genes)
|
369
383
|
ads = get_ads()
|
370
|
-
adata = ads.get_adata(
|
384
|
+
adata = ads.get_adata(adinfo=adinfo)
|
371
385
|
sc.tl.score_genes(adata, **func_kwargs)
|
372
|
-
add_op_log(adata, sc.tl.score_genes, func_kwargs)
|
373
|
-
return [generate_msg(
|
386
|
+
add_op_log(adata, sc.tl.score_genes, func_kwargs, adinfo)
|
387
|
+
return [generate_msg(adinfo, adata, ads)]
|
374
388
|
except ToolError as e:
|
375
389
|
raise ToolError(e)
|
376
390
|
except Exception as e:
|
@@ -382,20 +396,20 @@ async def score_genes(
|
|
382
396
|
@tl_mcp.tool()
|
383
397
|
async def score_genes_cell_cycle(
|
384
398
|
request: ScoreGenesCellCycleModel,
|
385
|
-
|
399
|
+
adinfo: AdataModel = AdataModel()
|
386
400
|
):
|
387
401
|
"""Score cell cycle genes and assign cell cycle phases"""
|
388
402
|
|
389
403
|
try:
|
390
|
-
result = await forward_request("tl_score_genes_cell_cycle", request)
|
404
|
+
result = await forward_request("tl_score_genes_cell_cycle", request, adinfo)
|
391
405
|
if result is not None:
|
392
406
|
return result
|
393
407
|
func_kwargs = filter_args(request, sc.tl.score_genes_cell_cycle)
|
394
408
|
ads = get_ads()
|
395
|
-
adata = ads.get_adata(
|
409
|
+
adata = ads.get_adata(adinfo=adinfo)
|
396
410
|
sc.tl.score_genes_cell_cycle(adata, **func_kwargs)
|
397
|
-
add_op_log(adata, sc.tl.score_genes_cell_cycle, func_kwargs)
|
398
|
-
return [generate_msg(
|
411
|
+
add_op_log(adata, sc.tl.score_genes_cell_cycle, func_kwargs, adinfo)
|
412
|
+
return [generate_msg(adinfo, adata, ads)]
|
399
413
|
except ToolError as e:
|
400
414
|
raise ToolError(e)
|
401
415
|
except Exception as e:
|
@@ -407,21 +421,22 @@ async def score_genes_cell_cycle(
|
|
407
421
|
|
408
422
|
@tl_mcp.tool()
|
409
423
|
async def pca(
|
410
|
-
request: PCAModel = PCAModel()
|
424
|
+
request: PCAModel = PCAModel(),
|
425
|
+
adinfo: AdataModel = AdataModel()
|
411
426
|
):
|
412
427
|
"""Principal component analysis"""
|
413
428
|
|
414
429
|
try:
|
415
|
-
result = await forward_request("tl_pca", request)
|
430
|
+
result = await forward_request("tl_pca", request, adinfo)
|
416
431
|
if result is not None:
|
417
432
|
return result
|
418
433
|
func_kwargs = filter_args(request, sc.pp.pca)
|
419
434
|
ads = get_ads()
|
420
|
-
adata = ads.get_adata(
|
435
|
+
adata = ads.get_adata(adinfo=adinfo)
|
421
436
|
sc.pp.pca(adata, **func_kwargs)
|
422
|
-
add_op_log(adata, sc.pp.pca, func_kwargs)
|
437
|
+
add_op_log(adata, sc.pp.pca, func_kwargs, adinfo)
|
423
438
|
return [
|
424
|
-
generate_msg(
|
439
|
+
generate_msg(adinfo, adata, ads)
|
425
440
|
]
|
426
441
|
except ToolError as e:
|
427
442
|
raise ToolError(e)
|
scmcp_shared/server/util.py
CHANGED
@@ -5,6 +5,7 @@ import scanpy as sc
|
|
5
5
|
from fastmcp import FastMCP , Context
|
6
6
|
from fastmcp.exceptions import ToolError
|
7
7
|
from ..schema.util import *
|
8
|
+
from ..schema import AdataModel
|
8
9
|
from ..util import filter_args, forward_request, get_ads, generate_msg,add_op_log
|
9
10
|
|
10
11
|
|
@@ -12,9 +13,12 @@ ul_mcp = FastMCP("SCMCP-Util-Server")
|
|
12
13
|
|
13
14
|
|
14
15
|
@ul_mcp.tool()
|
15
|
-
async def query_op_log(
|
16
|
+
async def query_op_log(
|
17
|
+
request: QueryOpLogModel = QueryOpLogModel(),
|
18
|
+
adinfo: AdataModel = AdataModel()
|
19
|
+
):
|
16
20
|
"""Query the adata operation log"""
|
17
|
-
adata = get_ads().get_adata(
|
21
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
18
22
|
op_dic = adata.uns["operation"]["op"]
|
19
23
|
opids = adata.uns["operation"]["opid"][-n:]
|
20
24
|
op_list = []
|
@@ -25,7 +29,8 @@ async def query_op_log(request: QueryOpLogModel = QueryOpLogModel()):
|
|
25
29
|
|
26
30
|
@ul_mcp.tool()
|
27
31
|
async def mark_var(
|
28
|
-
request: MarkVarModel = MarkVarModel()
|
32
|
+
request: MarkVarModel = MarkVarModel(),
|
33
|
+
adinfo: AdataModel = AdataModel()
|
29
34
|
):
|
30
35
|
"""
|
31
36
|
Determine if each gene meets specific conditions and store results in adata.var as boolean values.
|
@@ -33,10 +38,10 @@ async def mark_var(
|
|
33
38
|
The tool should be called first when calculate quality control metrics for mitochondrion, ribosomal, harhemoglobin genes, or other qc_vars.
|
34
39
|
"""
|
35
40
|
try:
|
36
|
-
result = await forward_request("ul_mark_var", request)
|
41
|
+
result = await forward_request("ul_mark_var", request, adinfo)
|
37
42
|
if result is not None:
|
38
43
|
return result
|
39
|
-
adata = get_ads().get_adata(
|
44
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
40
45
|
var_name = request.var_name
|
41
46
|
gene_class = request.gene_class
|
42
47
|
pattern_type = request.pattern_type
|
@@ -65,7 +70,7 @@ async def mark_var(
|
|
65
70
|
|
66
71
|
res = {var_name: adata.var[var_name].value_counts().to_dict(), "msg": f"add '{var_name}' column in adata.var"}
|
67
72
|
func_kwargs = {"var_name": var_name, "gene_class": gene_class, "pattern_type": pattern_type, "patterns": patterns}
|
68
|
-
add_op_log(adata, "mark_var", func_kwargs)
|
73
|
+
add_op_log(adata, "mark_var", func_kwargs, adinfo)
|
69
74
|
return res
|
70
75
|
except ToolError as e:
|
71
76
|
raise ToolError(e)
|
@@ -78,16 +83,17 @@ async def mark_var(
|
|
78
83
|
|
79
84
|
@ul_mcp.tool()
|
80
85
|
async def list_var(
|
81
|
-
request: ListVarModel = ListVarModel()
|
86
|
+
request: ListVarModel = ListVarModel(),
|
87
|
+
adinfo: AdataModel = AdataModel()
|
82
88
|
):
|
83
89
|
"""List key columns in adata.var. It should be called for checking when other tools need var key column names as input."""
|
84
90
|
try:
|
85
|
-
result = await forward_request("ul_list_var", request)
|
91
|
+
result = await forward_request("ul_list_var", request, adinfo)
|
86
92
|
if result is not None:
|
87
93
|
return result
|
88
|
-
adata = get_ads().get_adata(
|
94
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
89
95
|
columns = list(adata.var.columns)
|
90
|
-
add_op_log(adata, list_var, {})
|
96
|
+
add_op_log(adata, list_var, {}, adinfo)
|
91
97
|
return columns
|
92
98
|
except ToolError as e:
|
93
99
|
raise ToolError(e)
|
@@ -99,16 +105,17 @@ async def list_var(
|
|
99
105
|
|
100
106
|
@ul_mcp.tool()
|
101
107
|
async def list_obs(
|
102
|
-
request: ListObsModel = ListObsModel()
|
108
|
+
request: ListObsModel = ListObsModel(),
|
109
|
+
adinfo: AdataModel = AdataModel()
|
103
110
|
):
|
104
111
|
"""List key columns in adata.obs. It should be called before other tools need obs key column names input."""
|
105
112
|
try:
|
106
|
-
result = await forward_request("ul_list_obs", request)
|
113
|
+
result = await forward_request("ul_list_obs", request, adinfo)
|
107
114
|
if result is not None:
|
108
115
|
return result
|
109
|
-
adata = get_ads().get_adata(
|
116
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
110
117
|
columns = list(adata.obs.columns)
|
111
|
-
add_op_log(adata, list_obs, {})
|
118
|
+
add_op_log(adata, list_obs, {}, adinfo)
|
112
119
|
return columns
|
113
120
|
except ToolError as e:
|
114
121
|
raise ToolError(e)
|
@@ -120,17 +127,18 @@ async def list_obs(
|
|
120
127
|
|
121
128
|
@ul_mcp.tool()
|
122
129
|
async def check_var(
|
123
|
-
request: VarNamesModel = VarNamesModel()
|
130
|
+
request: VarNamesModel = VarNamesModel(),
|
131
|
+
adinfo: AdataModel = AdataModel()
|
124
132
|
):
|
125
133
|
"""Check if genes/variables exist in adata.var_names. This tool should be called before gene expression visualizations or color by genes."""
|
126
134
|
try:
|
127
|
-
result = await forward_request("ul_check_var", request)
|
135
|
+
result = await forward_request("ul_check_var", request, adinfo)
|
128
136
|
if result is not None:
|
129
137
|
return result
|
130
|
-
adata = get_ads().get_adata(
|
138
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
131
139
|
var_names = request.var_names
|
132
140
|
result = {v: v in adata.var_names for v in var_names}
|
133
|
-
add_op_log(adata, check_var, {"var_names": var_names})
|
141
|
+
add_op_log(adata, check_var, {"var_names": var_names}, adinfo)
|
134
142
|
return result
|
135
143
|
except ToolError as e:
|
136
144
|
raise ToolError(e)
|
@@ -142,21 +150,22 @@ async def check_var(
|
|
142
150
|
|
143
151
|
@ul_mcp.tool()
|
144
152
|
async def merge_adata(
|
145
|
-
request:
|
153
|
+
request: ConcatBaseModel = ConcatBaseModel(),
|
154
|
+
adinfo: AdataModel = AdataModel()
|
146
155
|
):
|
147
156
|
"""Merge multiple adata objects."""
|
148
157
|
|
149
158
|
try:
|
150
|
-
result = await forward_request("ul_merge_adata", request)
|
159
|
+
result = await forward_request("ul_merge_adata", request, adinfo)
|
151
160
|
if result is not None:
|
152
161
|
return result
|
153
162
|
ads = get_ads()
|
154
|
-
adata = ads.get_adata(
|
163
|
+
adata = ads.get_adata(adinfo=adinfo)
|
155
164
|
kwargs = {k: v for k, v in request.model_dump().items() if v is not None}
|
156
165
|
merged_adata = adata.concat(list(ads.adata_dic[dtype].values()), **kwargs)
|
157
166
|
ads.adata_dic[dtype] = {}
|
158
167
|
ads.active_id = "merged_adata"
|
159
|
-
add_op_log(merged_adata, ad.concat, kwargs)
|
168
|
+
add_op_log(merged_adata, ad.concat, kwargs, adinfo)
|
160
169
|
ads.adata_dic[ads.active_id] = merged_adata
|
161
170
|
return {"status": "success", "message": "Successfully merged all AnnData objects"}
|
162
171
|
except ToolError as e:
|
@@ -171,14 +180,14 @@ async def merge_adata(
|
|
171
180
|
@ul_mcp.tool()
|
172
181
|
async def set_dpt_iroot(
|
173
182
|
request: DPTIROOTModel,
|
174
|
-
|
183
|
+
adinfo: AdataModel = AdataModel()
|
175
184
|
):
|
176
185
|
"""Set the iroot cell"""
|
177
186
|
try:
|
178
|
-
result = await forward_request("ul_set_dpt_iroot", request)
|
187
|
+
result = await forward_request("ul_set_dpt_iroot", request, adinfo)
|
179
188
|
if result is not None:
|
180
189
|
return result
|
181
|
-
adata = get_ads().get_adata(
|
190
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
182
191
|
diffmap_key = request.diffmap_key
|
183
192
|
dimension = request.dimension
|
184
193
|
direction = request.direction
|
@@ -190,7 +199,7 @@ async def set_dpt_iroot(
|
|
190
199
|
adata.uns["iroot"] = adata.obsm[diffmap_key][:, dimension].argmax()
|
191
200
|
|
192
201
|
func_kwargs = {"diffmap_key": diffmap_key, "dimension": dimension, "direction": direction}
|
193
|
-
add_op_log(adata, "set_dpt_iroot", func_kwargs)
|
202
|
+
add_op_log(adata, "set_dpt_iroot", func_kwargs, adinfo)
|
194
203
|
|
195
204
|
return {"status": "success", "message": f"Successfully set root cell for DPT using {direction} of dimension {dimension}"}
|
196
205
|
except ToolError as e:
|
@@ -204,14 +213,15 @@ async def set_dpt_iroot(
|
|
204
213
|
@ul_mcp.tool()
|
205
214
|
async def add_layer(
|
206
215
|
request: AddLayerModel,
|
216
|
+
adinfo: AdataModel = AdataModel()
|
207
217
|
):
|
208
218
|
"""Add a layer to the AnnData object.
|
209
219
|
"""
|
210
220
|
try:
|
211
|
-
result = await forward_request("ul_add_layer", request)
|
221
|
+
result = await forward_request("ul_add_layer", request, adinfo)
|
212
222
|
if result is not None:
|
213
223
|
return result
|
214
|
-
adata = get_ads().get_adata(
|
224
|
+
adata = get_ads().get_adata(adinfo=adinfo)
|
215
225
|
layer_name = request.layer_name
|
216
226
|
|
217
227
|
# Check if layer already exists
|
@@ -221,7 +231,7 @@ async def add_layer(
|
|
221
231
|
adata.layers[layer_name] = adata.X.copy()
|
222
232
|
|
223
233
|
func_kwargs = {"layer_name": layer_name}
|
224
|
-
add_op_log(adata, "add_layer", func_kwargs)
|
234
|
+
add_op_log(adata, "add_layer", func_kwargs, adinfo)
|
225
235
|
|
226
236
|
return {
|
227
237
|
"status": "success",
|