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/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(request=request)
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(request, adata, ads)
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)]
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(request=request)
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(request, adata, ads)
439
+ generate_msg(adinfo, adata, ads)
425
440
  ]
426
441
  except ToolError as e:
427
442
  raise ToolError(e)
@@ -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(request: QueryOpLogModel = QueryOpLogModel()):
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(request=request)
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(request=request)
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(request=request)
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(request=request)
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(request=request)
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: ConcatAdataModel = ConcatAdataModel()
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(request=request)
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(request=request)
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(request=request)
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",