czty commited on
Commit
b6fb194
·
verified ·
1 Parent(s): 61e1468

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Biomni/mcp_generated/mcp_ariba/Dockerfile +40 -0
  2. Biomni/mcp_generated/mcp_ariba/app/ariba_server.py +509 -0
  3. Biomni/mcp_generated/mcp_ariba/app/ariba_shim_server.py +55 -0
  4. Biomni/mcp_generated/mcp_ariba/app/requirements.txt +1 -0
  5. Biomni/mcp_generated/mcp_ariba/docker-compose.yml +22 -0
  6. Biomni/mcp_generated/mcp_ariba/environment.yaml +10 -0
  7. Biomni/mcp_generated/mcp_ariba/requirements.txt +2 -0
  8. Biomni/mcp_generated/mcp_arvados-python-client/Dockerfile +40 -0
  9. Biomni/mcp_generated/mcp_arvados-python-client/app/arvados-python-client_server.py +306 -0
  10. Biomni/mcp_generated/mcp_arvados-python-client/app/arvados-python-client_shim_server.py +55 -0
  11. Biomni/mcp_generated/mcp_arvados-python-client/app/requirements.txt +1 -0
  12. Biomni/mcp_generated/mcp_arvados-python-client/docker-compose.yml +22 -0
  13. Biomni/mcp_generated/mcp_arvados-python-client/environment.yaml +10 -0
  14. Biomni/mcp_generated/mcp_arvados-python-client/requirements.txt +2 -0
  15. Biomni/mcp_generated/mcp_bbmap/docker-compose.yml +22 -0
  16. Biomni/mcp_generated/mcp_bbmap/requirements.txt +2 -0
  17. Biomni/mcp_generated/mcp_bioconductor-affy/Dockerfile +40 -0
  18. Biomni/mcp_generated/mcp_bioconductor-affy/app/bioconductor-affy_server.py +196 -0
  19. Biomni/mcp_generated/mcp_bioconductor-affy/app/bioconductor-affy_shim_server.py +55 -0
  20. Biomni/mcp_generated/mcp_bioconductor-affy/environment.yaml +10 -0
  21. Biomni/mcp_generated/mcp_bioconductor-affy/requirements.txt +2 -0
  22. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/Dockerfile +40 -0
  23. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_server.py +221 -0
  24. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_shim_server.py +55 -0
  25. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/requirements.txt +1 -0
  26. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/docker-compose.yml +22 -0
  27. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/environment.yaml +10 -0
  28. Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/requirements.txt +2 -0
  29. Biomni/mcp_generated/mcp_bioconductor-biocneighbors/environment.yaml +10 -0
  30. Biomni/mcp_generated/mcp_bioconductor-biocneighbors/requirements.txt +2 -0
  31. Biomni/mcp_generated/mcp_bioconductor-biocsingular/Dockerfile +40 -0
  32. Biomni/mcp_generated/mcp_bioconductor-biocsingular/app/bioconductor-biocsingular_server.py +224 -0
  33. Biomni/mcp_generated/mcp_bioconductor-biocsingular/app/bioconductor-biocsingular_shim_server.py +55 -0
  34. Biomni/mcp_generated/mcp_bioconductor-biocsingular/docker-compose.yml +22 -0
  35. Biomni/mcp_generated/mcp_bioconductor-biocsingular/environment.yaml +10 -0
  36. Biomni/mcp_generated/mcp_bioconductor-biocsingular/requirements.txt +2 -0
  37. Biomni/mcp_generated/mcp_bioconductor-bulksignalr/app/bioconductor-bulksignalr_server.py +473 -0
  38. Biomni/mcp_generated/mcp_bioconductor-clusterprofiler/app/bioconductor-clusterprofiler_shim_server.py +55 -0
  39. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/Dockerfile +40 -0
  40. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py +325 -0
  41. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_shim_server.py +55 -0
  42. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/requirements.txt +1 -0
  43. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/docker-compose.yml +22 -0
  44. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/environment.yaml +10 -0
  45. Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/requirements.txt +2 -0
  46. Biomni/mcp_generated/mcp_bioconductor-ggtree/Dockerfile +40 -0
  47. Biomni/mcp_generated/mcp_bioconductor-ggtree/app/bioconductor-ggtree_server.py +216 -0
  48. Biomni/mcp_generated/mcp_bioconductor-ggtree/app/bioconductor-ggtree_shim_server.py +55 -0
  49. Biomni/mcp_generated/mcp_bioconductor-ggtree/app/requirements.txt +1 -0
  50. Biomni/mcp_generated/mcp_bioconductor-ggtree/docker-compose.yml +22 -0
Biomni/mcp_generated/mcp_ariba/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install ariba via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda ariba -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/ariba_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/ariba_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/ariba_server.py"]
40
+
Biomni/mcp_generated/mcp_ariba/app/ariba_server.py ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, List, Literal
6
+
7
+ # MCP decorator is not needed for the final code, as per instructions.
8
+ # Assuming it's provided by the MCP framework.
9
+ # from mcp import tool as mcp_tool
10
+ # For linting purposes, we can create a dummy decorator.
11
+ class mcp:
12
+ def tool(func):
13
+ return func
14
+
15
+ @mcp.tool
16
+ def pubmlstget(
17
+ pubmlst_db: str,
18
+ outdir: Path,
19
+ force: bool = False,
20
+ noverify: bool = False,
21
+ ):
22
+ """
23
+ Download data from a pubmlst database.
24
+
25
+ Args:
26
+ pubmlst_db: Name of the pubmlst database to download.
27
+ outdir: Output directory to save the downloaded data.
28
+ force: Overwrite existing files if they exist.
29
+ noverify: Do not verify the SSL certificate.
30
+ """
31
+ # Input validation
32
+ if not pubmlst_db:
33
+ raise ValueError("pubmlst_db must be provided.")
34
+
35
+ try:
36
+ outdir.mkdir(parents=True, exist_ok=True)
37
+ except Exception as e:
38
+ raise IOError(f"Could not create output directory {outdir}: {e}")
39
+
40
+ # Command construction
41
+ cmd = ["ariba", "pubmlstget"]
42
+ if force:
43
+ cmd.append("--force")
44
+ if noverify:
45
+ cmd.append("--noverify")
46
+ cmd.extend([pubmlst_db, str(outdir)])
47
+
48
+ # Subprocess execution
49
+ try:
50
+ result = subprocess.run(
51
+ cmd,
52
+ check=True,
53
+ capture_output=True,
54
+ text=True,
55
+ )
56
+ output_files = [str(p) for p in outdir.rglob("*") if p.is_file()]
57
+ return {
58
+ "command_executed": " ".join(cmd),
59
+ "stdout": result.stdout,
60
+ "stderr": result.stderr,
61
+ "output_files": output_files,
62
+ }
63
+ except FileNotFoundError:
64
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
65
+ except subprocess.CalledProcessError as e:
66
+ return {
67
+ "command_executed": " ".join(cmd),
68
+ "stdout": e.stdout,
69
+ "stderr": e.stderr,
70
+ "error": "ARIBA pubmlstget failed",
71
+ "return_code": e.returncode,
72
+ }
73
+
74
+ @mcp.tool
75
+ def getref(
76
+ ref_name: Literal[
77
+ "argannot", "card", "megares", "plasmidfinder", "resfinder",
78
+ "srst2_argannot", "vfdb_core", "vfdb_full", "virfinder"
79
+ ],
80
+ outdir: Path,
81
+ force: bool = False,
82
+ metadata_only: bool = False,
83
+ ):
84
+ """
85
+ Download reference data from NCBI/RefSeq or other sources.
86
+
87
+ Args:
88
+ ref_name: Name of reference data to download.
89
+ outdir: Output directory to save the reference data.
90
+ force: Overwrite existing files if they exist.
91
+ metadata_only: Only download metadata, not the sequences.
92
+ """
93
+ # Input validation
94
+ try:
95
+ outdir.mkdir(parents=True, exist_ok=True)
96
+ except Exception as e:
97
+ raise IOError(f"Could not create output directory {outdir}: {e}")
98
+
99
+ # Command construction
100
+ cmd = ["ariba", "getref"]
101
+ if force:
102
+ cmd.append("--force")
103
+ if metadata_only:
104
+ cmd.append("--metadata_only")
105
+ cmd.extend([ref_name, str(outdir)])
106
+
107
+ # Subprocess execution
108
+ try:
109
+ result = subprocess.run(
110
+ cmd,
111
+ check=True,
112
+ capture_output=True,
113
+ text=True,
114
+ )
115
+ output_files = [str(p) for p in outdir.rglob("*") if p.is_file()]
116
+ return {
117
+ "command_executed": " ".join(cmd),
118
+ "stdout": result.stdout,
119
+ "stderr": result.stderr,
120
+ "output_files": output_files,
121
+ }
122
+ except FileNotFoundError:
123
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
124
+ except subprocess.CalledProcessError as e:
125
+ return {
126
+ "command_executed": " ".join(cmd),
127
+ "stdout": e.stdout,
128
+ "stderr": e.stderr,
129
+ "error": "ARIBA getref failed",
130
+ "return_code": e.returncode,
131
+ }
132
+
133
+ @mcp.tool
134
+ def prepareref(
135
+ fasta_in: Path,
136
+ metadata_in: Path,
137
+ outdir: Path,
138
+ min_length: int = 1,
139
+ max_length: int = 10000,
140
+ max_n: int = 5,
141
+ no_cdhit: bool = False,
142
+ cdhit_clusters: Optional[Path] = None,
143
+ cdhit_threads: int = 1,
144
+ cdhit_mem: int = 400,
145
+ force: bool = False,
146
+ tmp_dir: Optional[Path] = None,
147
+ verbose: bool = False,
148
+ debug: bool = False,
149
+ ):
150
+ """
151
+ Prepare reference data for use with ARIBA.
152
+
153
+ Args:
154
+ fasta_in: Input FASTA file of sequences.
155
+ metadata_in: Input TSV file of metadata.
156
+ outdir: Output directory for prepared reference.
157
+ min_length: Minimum sequence length.
158
+ max_length: Maximum sequence length.
159
+ max_n: Maximum number of Ns allowed in a sequence.
160
+ no_cdhit: Do not run cd-hit.
161
+ cdhit_clusters: File of cd-hit clusters. Implies --no_cdhit.
162
+ cdhit_threads: Threads to use for cd-hit.
163
+ cdhit_mem: Memory for cd-hit in MB.
164
+ force: Overwrite existing files.
165
+ tmp_dir: Use this directory for temporary files.
166
+ verbose: Print STDOUT and STDERR from external programs.
167
+ debug: Print debugging messages.
168
+ """
169
+ # Input validation
170
+ if not fasta_in.is_file():
171
+ raise FileNotFoundError(f"Input FASTA file not found: {fasta_in}")
172
+ if not metadata_in.is_file():
173
+ raise FileNotFoundError(f"Input metadata file not found: {metadata_in}")
174
+ if cdhit_clusters and not cdhit_clusters.is_file():
175
+ raise FileNotFoundError(f"cd-hit clusters file not found: {cdhit_clusters}")
176
+ if min_length <= 0:
177
+ raise ValueError("min_length must be a positive integer.")
178
+ if cdhit_threads <= 0:
179
+ raise ValueError("cdhit_threads must be a positive integer.")
180
+
181
+ try:
182
+ outdir.mkdir(parents=True, exist_ok=True)
183
+ except Exception as e:
184
+ raise IOError(f"Could not create output directory {outdir}: {e}")
185
+
186
+ # Command construction
187
+ cmd = ["ariba", "prepareref",
188
+ "--fasta_in", str(fasta_in),
189
+ "--metadata_in", str(metadata_in),
190
+ "--outdir", str(outdir),
191
+ "--min_length", str(min_length),
192
+ "--max_length", str(max_length),
193
+ "--max_N", str(max_n),
194
+ "--cdhit_threads", str(cdhit_threads),
195
+ "--cdhit_mem", str(cdhit_mem)]
196
+
197
+ if no_cdhit:
198
+ cmd.append("--no_cdhit")
199
+ if cdhit_clusters:
200
+ cmd.extend(["--cdhit_clusters", str(cdhit_clusters)])
201
+ if force:
202
+ cmd.append("--force")
203
+ if tmp_dir:
204
+ cmd.extend(["--tmp_dir", str(tmp_dir)])
205
+ if verbose:
206
+ cmd.append("--verbose")
207
+ if debug:
208
+ cmd.append("--debug")
209
+
210
+ # Subprocess execution
211
+ try:
212
+ result = subprocess.run(
213
+ cmd,
214
+ check=True,
215
+ capture_output=True,
216
+ text=True,
217
+ )
218
+ output_files = [str(p) for p in outdir.rglob("*") if p.is_file()]
219
+ return {
220
+ "command_executed": " ".join(cmd),
221
+ "stdout": result.stdout,
222
+ "stderr": result.stderr,
223
+ "output_files": output_files,
224
+ }
225
+ except FileNotFoundError:
226
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
227
+ except subprocess.CalledProcessError as e:
228
+ return {
229
+ "command_executed": " ".join(cmd),
230
+ "stdout": e.stdout,
231
+ "stderr": e.stderr,
232
+ "error": "ARIBA prepareref failed",
233
+ "return_code": e.returncode,
234
+ }
235
+
236
+ @mcp.tool
237
+ def run(
238
+ ref_dir: Path,
239
+ reads1: Path,
240
+ reads2: Path,
241
+ outdir: Path,
242
+ assembly_cov: int = 10,
243
+ gene_cov_cutoff: float = 0.95,
244
+ gene_nt_extend: int = 10,
245
+ local_th: float = 0.9,
246
+ min_gene_cov: int = 1,
247
+ assembled_threshold: float = 0.95,
248
+ nucmer_min_id: int = 90,
249
+ nucmer_min_len: int = 50,
250
+ nucmer_breaklen: int = 10,
251
+ nucmer_max_gap: int = 20,
252
+ assembler: Literal["fermilite", "spades"] = "fermilite",
253
+ spades_options: str = "--careful",
254
+ threads: int = 1,
255
+ force: bool = False,
256
+ tmp_dir: Optional[Path] = None,
257
+ verbose: bool = False,
258
+ debug: bool = False,
259
+ noclean: bool = False,
260
+ unique_map_only: bool = False,
261
+ other_ref_fasta: Optional[Path] = None,
262
+ filestem: Optional[str] = None,
263
+ ):
264
+ """
265
+ Run the main ARIBA pipeline to identify genes from paired sequencing reads.
266
+
267
+ Args:
268
+ ref_dir: Reference directory, created by 'prepareref'.
269
+ reads1: First paired-end reads file.
270
+ reads2: Second paired-end reads file.
271
+ outdir: Output directory for results.
272
+ assembly_cov: Minimum coverage of internal contig flank.
273
+ gene_cov_cutoff: Minimum proportion of gene length covered by reads.
274
+ gene_nt_extend: Max nucleotides to extend assembly past gene ends.
275
+ local_th: Minimum proportion of bases in local assembly identical to reference.
276
+ min_gene_cov: Minimum read coverage depth on any base of a gene.
277
+ assembled_threshold: Minimum proportion of bases in a gene covered by local assembly.
278
+ nucmer_min_id: nucmer --minid option.
279
+ nucmer_min_len: nucmer --mincluster option.
280
+ nucmer_breaklen: nucmer --breaklen option.
281
+ nucmer_max_gap: nucmer --maxgap option.
282
+ assembler: Assembler to use.
283
+ spades_options: Options to pass to spades.
284
+ threads: Number of threads to use.
285
+ force: Overwrite existing output files.
286
+ tmp_dir: Use this directory for temporary files.
287
+ verbose: Print STDOUT and STDERR from external programs.
288
+ debug: Print debugging messages.
289
+ noclean: Do not clean up intermediate files.
290
+ unique_map_only: Only use uniquely mapping reads.
291
+ other_ref_fasta: FASTA file of other reference sequences (e.g., host genome).
292
+ filestem: Filestem to use for output files.
293
+ """
294
+ # Input validation
295
+ if not ref_dir.is_dir():
296
+ raise FileNotFoundError(f"Reference directory not found: {ref_dir}")
297
+ if not reads1.is_file():
298
+ raise FileNotFoundError(f"Reads file 1 not found: {reads1}")
299
+ if not reads2.is_file():
300
+ raise FileNotFoundError(f"Reads file 2 not found: {reads2}")
301
+ if other_ref_fasta and not other_ref_fasta.is_file():
302
+ raise FileNotFoundError(f"Other reference FASTA not found: {other_ref_fasta}")
303
+ if threads <= 0:
304
+ raise ValueError("threads must be a positive integer.")
305
+
306
+ try:
307
+ outdir.mkdir(parents=True, exist_ok=True)
308
+ except Exception as e:
309
+ raise IOError(f"Could not create output directory {outdir}: {e}")
310
+
311
+ # Command construction
312
+ cmd = ["ariba", "run",
313
+ "--assembly_cov", str(assembly_cov),
314
+ "--gene_cov_cutoff", str(gene_cov_cutoff),
315
+ "--gene_nt_extend", str(gene_nt_extend),
316
+ "--local_th", str(local_th),
317
+ "--min_gene_cov", str(min_gene_cov),
318
+ "--assembled_threshold", str(assembled_threshold),
319
+ "--nucmer_min_id", str(nucmer_min_id),
320
+ "--nucmer_min_len", str(nucmer_min_len),
321
+ "--nucmer_breaklen", str(nucmer_breaklen),
322
+ "--nucmer_max_gap", str(nucmer_max_gap),
323
+ "--assembler", assembler,
324
+ "--spades_options", spades_options,
325
+ "--threads", str(threads)]
326
+
327
+ if force: cmd.append("--force")
328
+ if verbose: cmd.append("--verbose")
329
+ if debug: cmd.append("--debug")
330
+ if noclean: cmd.append("--noclean")
331
+ if unique_map_only: cmd.append("--unique_map_only")
332
+ if tmp_dir: cmd.extend(["--tmp_dir", str(tmp_dir)])
333
+ if other_ref_fasta: cmd.extend(["--other_ref_fasta", str(other_ref_fasta)])
334
+ if filestem: cmd.extend(["--filestem", filestem])
335
+
336
+ cmd.extend([str(ref_dir), str(reads1), str(reads2), str(outdir)])
337
+
338
+ # Subprocess execution
339
+ try:
340
+ result = subprocess.run(
341
+ cmd,
342
+ check=True,
343
+ capture_output=True,
344
+ text=True,
345
+ )
346
+ output_files = [str(p) for p in outdir.rglob("*") if p.is_file()]
347
+ return {
348
+ "command_executed": " ".join(cmd),
349
+ "stdout": result.stdout,
350
+ "stderr": result.stderr,
351
+ "output_files": output_files,
352
+ }
353
+ except FileNotFoundError:
354
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
355
+ except subprocess.CalledProcessError as e:
356
+ return {
357
+ "command_executed": " ".join(cmd),
358
+ "stdout": e.stdout,
359
+ "stderr": e.stderr,
360
+ "error": "ARIBA run failed",
361
+ "return_code": e.returncode,
362
+ }
363
+
364
+ @mcp.tool
365
+ def summary(
366
+ out_prefix: Path,
367
+ in_dirs: List[Path],
368
+ preset: Literal["all", "assembled", "flags", "variants"] = "all",
369
+ cluster_cols: Optional[str] = None,
370
+ col_sep: str = ".",
371
+ row_sep: str = ".",
372
+ force: bool = False,
373
+ verbose: bool = False,
374
+ debug: bool = False,
375
+ ):
376
+ """
377
+ Summarise multiple ARIBA reports into a single file.
378
+
379
+ Args:
380
+ out_prefix: Output file prefix.
381
+ in_dirs: List of directories containing ARIBA results.
382
+ preset: Output preset.
383
+ cluster_cols: Columns to use for clustering.
384
+ col_sep: Separator between column names in output file.
385
+ row_sep: Separator between sample name and cluster name in output file.
386
+ force: Overwrite existing files.
387
+ verbose: Print STDOUT and STDERR from external programs.
388
+ debug: Print debugging messages.
389
+ """
390
+ # Input validation
391
+ if not in_dirs:
392
+ raise ValueError("At least one input directory must be provided in in_dirs.")
393
+ for d in in_dirs:
394
+ if not d.is_dir():
395
+ raise FileNotFoundError(f"Input directory not found: {d}")
396
+ report_path = d / "report.tsv"
397
+ if not report_path.is_file():
398
+ raise FileNotFoundError(f"Expected report file not found in {d}: {report_path}")
399
+
400
+ try:
401
+ out_prefix.parent.mkdir(parents=True, exist_ok=True)
402
+ except Exception as e:
403
+ raise IOError(f"Could not create output directory {out_prefix.parent}: {e}")
404
+
405
+ # Command construction
406
+ cmd = ["ariba", "summary",
407
+ "--preset", preset,
408
+ "--col_sep", col_sep,
409
+ "--row_sep", row_sep]
410
+
411
+ if cluster_cols:
412
+ cmd.extend(["--cluster_cols", cluster_cols])
413
+ if force:
414
+ cmd.append("--force")
415
+ if verbose:
416
+ cmd.append("--verbose")
417
+ if debug:
418
+ cmd.append("--debug")
419
+
420
+ cmd.append(str(out_prefix))
421
+ cmd.extend([str(d) for d in in_dirs])
422
+
423
+ # Subprocess execution
424
+ try:
425
+ result = subprocess.run(
426
+ cmd,
427
+ check=True,
428
+ capture_output=True,
429
+ text=True,
430
+ )
431
+ output_files = [str(p) for p in out_prefix.parent.glob(f"{out_prefix.name}*")]
432
+ return {
433
+ "command_executed": " ".join(cmd),
434
+ "stdout": result.stdout,
435
+ "stderr": result.stderr,
436
+ "output_files": output_files,
437
+ }
438
+ except FileNotFoundError:
439
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
440
+ except subprocess.CalledProcessError as e:
441
+ return {
442
+ "command_executed": " ".join(cmd),
443
+ "stdout": e.stdout,
444
+ "stderr": e.stderr,
445
+ "error": "ARIBA summary failed",
446
+ "return_code": e.returncode,
447
+ }
448
+
449
+ @mcp.tool
450
+ def version():
451
+ """
452
+ Print the ARIBA version number and exit.
453
+ """
454
+ cmd = ["ariba", "version"]
455
+ try:
456
+ result = subprocess.run(
457
+ cmd,
458
+ check=True,
459
+ capture_output=True,
460
+ text=True,
461
+ )
462
+ return {
463
+ "command_executed": " ".join(cmd),
464
+ "stdout": result.stdout,
465
+ "stderr": result.stderr,
466
+ "version": result.stdout.strip(),
467
+ }
468
+ except FileNotFoundError:
469
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
470
+ except subprocess.CalledProcessError as e:
471
+ return {
472
+ "command_executed": " ".join(cmd),
473
+ "stdout": e.stdout,
474
+ "stderr": e.stderr,
475
+ "error": "ARIBA version command failed",
476
+ "return_code": e.returncode,
477
+ }
478
+
479
+ @mcp.tool
480
+ def test():
481
+ """
482
+ Run the ARIBA test suite to verify installation and functionality.
483
+ """
484
+ cmd = ["ariba", "test"]
485
+ try:
486
+ # The test command can be long-running and produce a lot of output.
487
+ # We capture it as usual.
488
+ result = subprocess.run(
489
+ cmd,
490
+ check=True,
491
+ capture_output=True,
492
+ text=True,
493
+ )
494
+ return {
495
+ "command_executed": " ".join(cmd),
496
+ "stdout": result.stdout,
497
+ "stderr": result.stderr,
498
+ "test_passed": "OK" in result.stderr,
499
+ }
500
+ except FileNotFoundError:
501
+ raise RuntimeError("ariba command not found. Please ensure it is in your PATH.")
502
+ except subprocess.CalledProcessError as e:
503
+ return {
504
+ "command_executed": " ".join(cmd),
505
+ "stdout": e.stdout,
506
+ "stderr": e.stderr,
507
+ "error": "ARIBA test suite failed",
508
+ "return_code": e.returncode,
509
+ }
Biomni/mcp_generated/mcp_ariba/app/ariba_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/app/ariba_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_ariba'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_ariba/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_ariba/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-ariba:
5
+ build: .
6
+ image: mcp-ariba:latest
7
+ container_name: mcp-ariba
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=ariba
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_ariba/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - ariba
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_ariba/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_arvados-python-client/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install arvados-python-client via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda arvados-python-client -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/arvados-python-client_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/arvados-python-client_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/arvados-python-client_server.py"]
40
+
Biomni/mcp_generated/mcp_arvados-python-client/app/arvados-python-client_server.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import List, Optional
5
+
6
+ # MCP-standardized error handling
7
+ class MCPError(Exception):
8
+ def __init__(self, message, stderr):
9
+ self.message = message
10
+ self.stderr = stderr
11
+ super().__init__(self.message)
12
+
13
+ def _run_command(cmd: List[str]):
14
+ """Helper function to run a command and handle exceptions."""
15
+ try:
16
+ command_str = " ".join(cmd)
17
+ result = subprocess.run(
18
+ cmd,
19
+ capture_output=True,
20
+ text=True,
21
+ check=True,
22
+ )
23
+ return {
24
+ "command_executed": command_str,
25
+ "stdout": result.stdout,
26
+ "stderr": result.stderr,
27
+ }
28
+ except FileNotFoundError:
29
+ raise MCPError(f"Command '{cmd[0]}' not found. Is arvados-python-client installed and in the PATH?", "")
30
+ except subprocess.CalledProcessError as e:
31
+ raise MCPError(f"Command '{' '.join(cmd)}' failed with exit code {e.returncode}", e.stderr)
32
+
33
+ from mcp.server.fastmcp import FastMCP
34
+
35
+ SERVER_NAME = 'local_arvados_python_client'
36
+ mcp = FastMCP(SERVER_NAME)
37
+
38
+ @mcp.tool()
39
+ def arv_get(
40
+ object_locator: str,
41
+ destination_path: Optional[Path] = None,
42
+ filename: Optional[str] = None,
43
+ force: bool = False,
44
+ recursive: bool = False,
45
+ progress: bool = False,
46
+ ) -> dict:
47
+ """
48
+ Downloads an object from Arvados Keep to a local path or standard output.
49
+
50
+ Args:
51
+ object_locator: The UUID or portable data hash (PDH) of the object to download.
52
+ destination_path: Local path to save the object. If a directory, the original filename is used. If not provided, content is written to stdout.
53
+ filename: The name of the file to get from a collection.
54
+ force: Overwrite existing files if they exist.
55
+ recursive: Recursively download collections.
56
+ progress: Show a progress bar during download.
57
+
58
+ Returns:
59
+ A dictionary containing the command executed, stdout, stderr, and a list of output files.
60
+ """
61
+ cmd = ["arv-get"]
62
+
63
+ if force:
64
+ cmd.append("--force")
65
+ if recursive:
66
+ cmd.append("--recursive")
67
+ if progress:
68
+ cmd.append("--progress")
69
+ if filename:
70
+ cmd.extend(["--filename", filename])
71
+
72
+ cmd.append(object_locator)
73
+
74
+ output_files = []
75
+ if destination_path:
76
+ # Ensure parent directory exists if a full path is given
77
+ if destination_path.parent and not destination_path.parent.is_dir():
78
+ destination_path.parent.mkdir(parents=True, exist_ok=True)
79
+ cmd.append(str(destination_path))
80
+ output_files.append(str(destination_path))
81
+
82
+ try:
83
+ result = _run_command(cmd)
84
+ result["output_files"] = output_files
85
+ return result
86
+ except MCPError as e:
87
+ return {
88
+ "command_executed": " ".join(cmd),
89
+ "stdout": "",
90
+ "stderr": e.stderr,
91
+ "error": e.message,
92
+ "output_files": [],
93
+ }
94
+
95
+ @mcp.tool()
96
+ def arv_put(
97
+ paths: List[Path],
98
+ name: Optional[str] = None,
99
+ owner: Optional[str] = None,
100
+ portable_data_hash: bool = False,
101
+ storage_class: Optional[str] = None,
102
+ num_retries: int = 5,
103
+ progress: bool = False,
104
+ ) -> dict:
105
+ """
106
+ Uploads local files or directories to Arvados Keep.
107
+
108
+ Args:
109
+ paths: One or more local file or directory paths to upload.
110
+ name: Set the name of the new collection.
111
+ owner: Set the owner project UUID for the new collection.
112
+ portable_data_hash: Use portable data hash to identify the collection.
113
+ storage_class: The storage class for the data (e.g., 'default', 'trash').
114
+ num_retries: Number of times to retry a failed API call.
115
+ progress: Show a progress bar during upload.
116
+
117
+ Returns:
118
+ A dictionary containing the command executed, stdout, and stderr.
119
+ """
120
+ if not paths:
121
+ raise ValueError("At least one path must be provided for upload.")
122
+
123
+ for p in paths:
124
+ if not p.exists():
125
+ raise FileNotFoundError(f"Input path does not exist: {p}")
126
+
127
+ cmd = ["arv-put"]
128
+
129
+ if name:
130
+ cmd.extend(["--name", name])
131
+ if owner:
132
+ cmd.extend(["--owner", owner])
133
+ if portable_data_hash:
134
+ cmd.append("--portable-data-hash")
135
+ if storage_class:
136
+ cmd.extend(["--storage-class", storage_class])
137
+ if progress:
138
+ cmd.append("--progress")
139
+
140
+ # num_retries is a standard arvados client option
141
+ cmd.extend(["--num-retries", str(num_retries)])
142
+
143
+ cmd.extend([str(p) for p in paths])
144
+
145
+ try:
146
+ result = _run_command(cmd)
147
+ result["output_files"] = []
148
+ return result
149
+ except MCPError as e:
150
+ return {
151
+ "command_executed": " ".join(cmd),
152
+ "stdout": "",
153
+ "stderr": e.stderr,
154
+ "error": e.message,
155
+ "output_files": [],
156
+ }
157
+
158
+ @mcp.tool()
159
+ def arv_ls(
160
+ object_locator: str,
161
+ path: Optional[str] = None,
162
+ long_format: bool = False,
163
+ recursive: bool = False,
164
+ human_readable: bool = False,
165
+ ) -> dict:
166
+ """
167
+ Lists the contents of a collection in Arvados Keep.
168
+
169
+ Args:
170
+ object_locator: The UUID or portable data hash (PDH) of the collection to list.
171
+ path: A path within the collection to list.
172
+ long_format: Use a long listing format (similar to ls -l).
173
+ recursive: List subdirectories recursively.
174
+ human_readable: With long_format, print sizes in human-readable format (e.g., 1K, 234M, 2G).
175
+
176
+ Returns:
177
+ A dictionary containing the command executed, stdout (the listing), and stderr.
178
+ """
179
+ cmd = ["arv-ls"]
180
+
181
+ if long_format:
182
+ cmd.append("-l")
183
+ if recursive:
184
+ cmd.append("-R")
185
+ if human_readable:
186
+ cmd.append("-h")
187
+
188
+ cmd.append(object_locator)
189
+ if path:
190
+ cmd.append(path)
191
+
192
+ try:
193
+ result = _run_command(cmd)
194
+ result["output_files"] = []
195
+ return result
196
+ except MCPError as e:
197
+ return {
198
+ "command_executed": " ".join(cmd),
199
+ "stdout": "",
200
+ "stderr": e.stderr,
201
+ "error": e.message,
202
+ "output_files": [],
203
+ }
204
+
205
+ @mcp.tool()
206
+ def arv_copy(
207
+ src_locators: List[str],
208
+ dest_project_uuid: str,
209
+ name: Optional[str] = None,
210
+ num_retries: int = 5,
211
+ ) -> dict:
212
+ """
213
+ Copies one or more objects to a destination project in Arvados.
214
+
215
+ Args:
216
+ src_locators: A list of source object UUIDs or portable data hashes (PDHs).
217
+ dest_project_uuid: The UUID of the destination project.
218
+ name: New name for the object(s). If copying multiple objects, this is ignored.
219
+ num_retries: Number of times to retry a failed API call.
220
+
221
+ Returns:
222
+ A dictionary containing the command executed, stdout, and stderr.
223
+ """
224
+ if not src_locators:
225
+ raise ValueError("At least one source locator must be provided.")
226
+ if not dest_project_uuid:
227
+ raise ValueError("A destination project UUID must be provided.")
228
+
229
+ cmd = ["arv-copy"]
230
+
231
+ if name and len(src_locators) == 1:
232
+ cmd.extend(["--name", name])
233
+
234
+ cmd.extend(["--num-retries", str(num_retries)])
235
+
236
+ cmd.extend(src_locators)
237
+ cmd.append(dest_project_uuid)
238
+
239
+ try:
240
+ result = _run_command(cmd)
241
+ result["output_files"] = []
242
+ return result
243
+ except MCPError as e:
244
+ return {
245
+ "command_executed": " ".join(cmd),
246
+ "stdout": "",
247
+ "stderr": e.stderr,
248
+ "error": e.message,
249
+ "output_files": [],
250
+ }
251
+
252
+ @mcp.tool()
253
+ def arv_tag(
254
+ locator: str,
255
+ tags_to_add: Optional[List[str]] = None,
256
+ keys_to_remove: Optional[List[str]] = None,
257
+ list_tags: bool = False,
258
+ num_retries: int = 5,
259
+ ) -> dict:
260
+ """
261
+ Manipulates tags on an Arvados object. It can list, add, or remove tags.
262
+
263
+ Args:
264
+ locator: The UUID or portable data hash (PDH) of the object to tag.
265
+ tags_to_add: A list of tags to add, in 'key' or 'key=value' format.
266
+ keys_to_remove: A list of tag keys to remove.
267
+ list_tags: If True, lists the existing tags on the object.
268
+ num_retries: Number of times to retry a failed API call.
269
+
270
+ Returns:
271
+ A dictionary containing the command executed, stdout, and stderr.
272
+ """
273
+ cmd = ["arv-tag"]
274
+
275
+ cmd.extend(["--num-retries", str(num_retries)])
276
+
277
+ if list_tags:
278
+ cmd.append("--list")
279
+
280
+ cmd.append(locator)
281
+
282
+ if tags_to_add:
283
+ for tag in tags_to_add:
284
+ if "=" not in tag and " " in tag:
285
+ raise ValueError(f"Invalid tag format: '{tag}'. Use 'key=value' or 'key'.")
286
+ cmd.extend(tags_to_add)
287
+
288
+ if keys_to_remove:
289
+ for key in keys_to_remove:
290
+ cmd.extend(["--remove", key])
291
+
292
+ try:
293
+ result = _run_command(cmd)
294
+ result["output_files"] = []
295
+ return result
296
+ except MCPError as e:
297
+ return {
298
+ "command_executed": " ".join(cmd),
299
+ "stdout": "",
300
+ "stderr": e.stderr,
301
+ "error": e.message,
302
+ "output_files": [],
303
+ }
304
+
305
+ if __name__ == "__main__":
306
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_arvados-python-client/app/arvados-python-client_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-python-client/app/arvados-python-client_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_arvados_python_client'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_arvados-python-client/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_arvados-python-client/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-arvados-python-client:
5
+ build: .
6
+ image: mcp-arvados-python-client:latest
7
+ container_name: mcp-arvados-python-client
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=arvados-python-client
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_arvados-python-client/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - arvados-python-client
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_arvados-python-client/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bbmap/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bbmap:
5
+ build: .
6
+ image: mcp-bbmap:latest
7
+ container_name: mcp-bbmap
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bbmap
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_bbmap/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-affy/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-affy via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-affy -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/bioconductor-affy_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-affy_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-affy_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-affy/app/bioconductor-affy_server.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, Dict, List
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # In a real MCP environment, this would be provided by the MCP framework.
8
+ def tool(func):
9
+ def wrapper(*args, **kwargs):
10
+ # In a real scenario, the MCP framework would handle
11
+ # registration and execution.
12
+ return func(*args, **kwargs)
13
+ return wrapper
14
+ mcp = type("mcp", (), {"tool": tool})
15
+
16
+
17
+ @mcp.tool
18
+ def run_affy_analysis(
19
+ cel_files_dir: Path,
20
+ output_expression_file: Path,
21
+ method: str = "rma",
22
+ pheno_data_file: Optional[Path] = None,
23
+ generate_qc_plot: bool = False,
24
+ qc_plot_file: Optional[Path] = None,
25
+ ) -> Dict:
26
+ """
27
+ Performs exploratory analysis of Affymetrix oligonucleotide arrays.
28
+
29
+ This tool reads raw Affymetrix data from .CEL files, performs background
30
+ correction, normalization, and probe summarization using either the RMA
31
+ (Robust Multi-array Average) or MAS 5.0 algorithm, and outputs a matrix
32
+ of expression values.
33
+
34
+ Args:
35
+ cel_files_dir: Path to the directory containing the input .CEL files.
36
+ output_expression_file: Path to write the output expression matrix (tab-separated).
37
+ method: The processing method to use. Must be one of 'rma' or 'mas5'.
38
+ Defaults to 'rma'.
39
+ pheno_data_file: Optional path to a phenodata file describing the samples.
40
+ This should be a tab-delimited file with row names matching
41
+ the .CEL file names.
42
+ generate_qc_plot: If True, generate a quality control boxplot of the raw
43
+ probe intensities. Defaults to False.
44
+ qc_plot_file: Path to save the QC plot image (e.g., plot.png).
45
+ Required if generate_qc_plot is True.
46
+
47
+ Returns:
48
+ A dictionary containing the executed command, stdout, stderr, and a
49
+ list of output files.
50
+ """
51
+ # 1. Input Validation
52
+ if not cel_files_dir.is_dir():
53
+ raise ValueError(f"Input directory '{cel_files_dir}' does not exist or is not a directory.")
54
+
55
+ if pheno_data_file and not pheno_data_file.is_file():
56
+ raise FileNotFoundError(f"Phenodata file '{pheno_data_file}' not found.")
57
+
58
+ method_lower = method.lower()
59
+ if method_lower not in ["rma", "mas5"]:
60
+ raise ValueError(f"Invalid method '{method}'. Must be 'rma' or 'mas5'.")
61
+
62
+ if generate_qc_plot:
63
+ if qc_plot_file is None:
64
+ raise ValueError("qc_plot_file must be provided when generate_qc_plot is True.")
65
+ if not qc_plot_file.parent.exists():
66
+ qc_plot_file.parent.mkdir(parents=True, exist_ok=True)
67
+ elif qc_plot_file is not None:
68
+ print(f"Warning: qc_plot_file '{qc_plot_file}' is specified but generate_qc_plot is False. The file will not be created.")
69
+
70
+ if not output_expression_file.parent.exists():
71
+ output_expression_file.parent.mkdir(parents=True, exist_ok=True)
72
+
73
+ # 2. R Script Generation
74
+ # This script will be executed by Rscript to perform the analysis.
75
+ r_script_content = f"""
76
+ # Load necessary libraries
77
+ if (!requireNamespace("affy", quietly = TRUE)) {{
78
+ stop("The 'affy' package is not installed. Please install it from Bioconductor.")
79
+ }}
80
+ library(affy)
81
+
82
+ # Parse command-line arguments
83
+ args <- commandArgs(trailingOnly = TRUE)
84
+ if (length(args) < 3) {{
85
+ stop("Usage: Rscript script.R <cel_dir> <output_path> <method> [pheno_path_or_NA] [plot_path_or_NA]", call. = FALSE)
86
+ }}
87
+
88
+ cel_dir <- args[1]
89
+ output_path <- args[2]
90
+ method <- args[3]
91
+ pheno_path <- if (length(args) >= 4 && args[4] != "NA") args[4] else NULL
92
+ plot_path <- if (length(args) >= 5 && args[5] != "NA") args[5] else NULL
93
+
94
+ # --- Main Logic ---
95
+ tryCatch({{
96
+ # Read the data
97
+ cat("Reading CEL files from:", cel_dir, "\\n")
98
+ if (!is.null(pheno_path)) {{
99
+ cat("Using pheno data from:", pheno_path, "\\n")
100
+ # read.AnnotatedDataFrame is deprecated, using read.phenoData is more modern
101
+ pheno <- read.phenoData(pheno_path, header = TRUE, row.names = 1)
102
+ data <- ReadAffy(celfile.path = cel_dir, phenoData = pheno)
103
+ }} else {{
104
+ data <- ReadAffy(celfile.path = cel_dir)
105
+ }}
106
+ cat("Finished reading data.\\n")
107
+
108
+ # Process the data
109
+ cat("Processing data with method:", method, "\\n")
110
+ if (method == "rma") {{
111
+ eset <- rma(data)
112
+ }} else if (method == "mas5") {{
113
+ eset <- mas5(data)
114
+ }} else {{
115
+ stop(paste("Invalid method specified:", method))
116
+ }}
117
+ cat("Finished processing.\\n")
118
+
119
+ # Extract and write expression values
120
+ cat("Writing expression matrix to:", output_path, "\\n")
121
+ expression_matrix <- exprs(eset)
122
+ write.table(expression_matrix, file = output_path, sep = "\\t", quote = FALSE, col.names = NA)
123
+ cat("Done writing expression matrix.\\n")
124
+
125
+ # Generate QC plot if requested
126
+ if (!is.null(plot_path)) {{
127
+ cat("Generating QC plot at:", plot_path, "\\n")
128
+ png(filename = plot_path, width = 800, height = 600)
129
+ boxplot(data, main = "Raw Probe Intensities", las = 2, col="lightblue")
130
+ dev.off()
131
+ cat("Done generating plot.\\n")
132
+ }}
133
+
134
+ cat("Analysis complete.\\n")
135
+ }}, error = function(e) {{
136
+ cat("An error occurred:\\n")
137
+ cat(conditionMessage(e), "\\n")
138
+ quit(status = 1)
139
+ }})
140
+ """
141
+
142
+ # 3. Subprocess Execution
143
+ output_files = {"expression_matrix": str(output_expression_file)}
144
+ cmd = ["Rscript"]
145
+
146
+ try:
147
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
148
+ r_script_file.write(r_script_content)
149
+ r_script_path = r_script_file.name
150
+
151
+ # Prepare arguments for the R script
152
+ pheno_path_arg = str(pheno_data_file.resolve()) if pheno_data_file else "NA"
153
+ plot_path_arg = str(qc_plot_file.resolve()) if generate_qc_plot and qc_plot_file else "NA"
154
+
155
+ cmd.extend([
156
+ r_script_path,
157
+ str(cel_files_dir.resolve()),
158
+ str(output_expression_file.resolve()),
159
+ method_lower,
160
+ pheno_path_arg,
161
+ plot_path_arg
162
+ ])
163
+
164
+ # Execute the command
165
+ process = subprocess.run(
166
+ cmd,
167
+ check=True,
168
+ capture_output=True,
169
+ text=True
170
+ )
171
+
172
+ if generate_qc_plot and qc_plot_file:
173
+ output_files["qc_plot"] = str(qc_plot_file)
174
+
175
+ return {
176
+ "command_executed": " ".join(cmd),
177
+ "stdout": process.stdout,
178
+ "stderr": process.stderr,
179
+ "output_files": output_files
180
+ }
181
+
182
+ except subprocess.CalledProcessError as e:
183
+ return {
184
+ "command_executed": " ".join(cmd),
185
+ "stdout": e.stdout,
186
+ "stderr": e.stderr,
187
+ "error": "R script execution failed.",
188
+ "return_code": e.returncode,
189
+ "output_files": {}
190
+ }
191
+ except Exception as e:
192
+ raise e
193
+ finally:
194
+ # Clean up the temporary R script file
195
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
196
+ Path(r_script_path).unlink()
Biomni/mcp_generated/mcp_bioconductor-affy/app/bioconductor-affy_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-affy/app/bioconductor-affy_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_affy'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-affy/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-affy
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-affy/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-alabaster.sfe via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-alabaster.sfe -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/bioconductor-alabaster.sfe_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-alabaster.sfe_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-alabaster.sfe_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_server.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Dict, Any, Optional
5
+
6
+ # It is assumed that the mcp decorator is available in the execution environment.
7
+ # from mcp import tool
8
+
9
+ # Mock decorator for local testing, not to be included in the final deliverable
10
+ # if 'mcp' not in globals():
11
+ # class mcp:
12
+ # @staticmethod
13
+ # def tool():
14
+ # def decorator(func):
15
+ # return func
16
+ # return decorator
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ from mcp.server.fastmcp import FastMCP
21
+
22
+ SERVER_NAME = 'local_bioconductor_alabaster_sfe'
23
+ mcp = FastMCP(SERVER_NAME)
24
+
25
+ @mcp.tool()
26
+ def stage_sfe_object(
27
+ input_rds: Path,
28
+ output_dir: Path,
29
+ path: str = "sfe",
30
+ ) -> Dict[str, Any]:
31
+ """
32
+ Stages a SpatialFeatureExperiment (SFE) object from an RDS file into the alabaster format.
33
+
34
+ This tool wraps the 'stageObject' function from the R package 'alabaster.sfe'.
35
+ It reads an SFE object from a specified RDS file and saves it into a structured
36
+ directory according to the alabaster specification. This serves as the primary
37
+ "save" or "serialize" function for SFE objects in the alabaster framework.
38
+
39
+ Args:
40
+ input_rds: Path to the input RDS file containing the SpatialFeatureExperiment object.
41
+ output_dir: Path to the directory where the staged object assets will be stored.
42
+ path: The relative path within 'output_dir' to save the object. Defaults to "sfe".
43
+
44
+ Returns:
45
+ A dictionary containing the execution details and the path to the main output metadata file.
46
+ """
47
+ # 1. Input Validation
48
+ if not input_rds.is_file():
49
+ raise FileNotFoundError(f"Input RDS file not found at: {input_rds}")
50
+
51
+ if not path:
52
+ raise ValueError("'path' cannot be an empty string.")
53
+
54
+ try:
55
+ output_dir.mkdir(parents=True, exist_ok=True)
56
+ except OSError as e:
57
+ raise OSError(f"Failed to create output directory {output_dir}: {e}")
58
+
59
+ # 2. R script construction
60
+ r_script_content = f"""
61
+ suppressPackageStartupMessages(library(alabaster.sfe))
62
+ suppressPackageStartupMessages(library(SpatialFeatureExperiment))
63
+
64
+ tryCatch({{
65
+ sfe_object <- readRDS('{input_rds.resolve()}')
66
+ info <- alabaster.base::stageObject(sfe_object, dir = '{output_dir.resolve()}', path = '{path}')
67
+ cat(file.path('{output_dir.resolve()}', info$path, "object.json"))
68
+ }}, error = function(e) {{
69
+ message("Error during R script execution: ", e$message)
70
+ quit(status = 1)
71
+ }})
72
+ """
73
+
74
+ # 3. Subprocess Execution
75
+ cmd = ["Rscript", "-e", r_script_content]
76
+ command_executed = " ".join(map(str, cmd))
77
+ logger.info(f"Executing command: {command_executed}")
78
+
79
+ try:
80
+ process = subprocess.run(
81
+ cmd,
82
+ check=True,
83
+ capture_output=True,
84
+ text=True,
85
+ encoding='utf-8'
86
+ )
87
+
88
+ stdout = process.stdout.strip()
89
+ stderr = process.stderr.strip()
90
+
91
+ main_output_file = Path(stdout)
92
+ if not main_output_file.is_file():
93
+ raise FileNotFoundError(
94
+ f"Expected output file not created: {main_output_file}. R stderr: {stderr}"
95
+ )
96
+
97
+ return {
98
+ "command_executed": command_executed,
99
+ "stdout": stdout,
100
+ "stderr": stderr,
101
+ "output_files": {
102
+ "staged_object_json": str(main_output_file)
103
+ }
104
+ }
105
+
106
+ except FileNotFoundError:
107
+ error_msg = "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH."
108
+ logger.error(error_msg)
109
+ raise RuntimeError(error_msg) from None
110
+ except subprocess.CalledProcessError as e:
111
+ logger.error(f"Command failed with exit code {e.returncode}")
112
+ logger.error(f"STDOUT: {e.stdout}")
113
+ logger.error(f"STDERR: {e.stderr}")
114
+ raise RuntimeError(
115
+ f"R script execution failed with exit code {e.returncode}.\n"
116
+ f"STDOUT: {e.stdout}\n"
117
+ f"STDERR: {e.stderr}"
118
+ ) from e
119
+
120
+ @mcp.tool()
121
+ def load_sfe_object(
122
+ staged_dir: Path,
123
+ output_rds: Path,
124
+ path: str = "",
125
+ ) -> Dict[str, Any]:
126
+ """
127
+ Loads a SpatialFeatureExperiment (SFE) object from the alabaster format into an RDS file.
128
+
129
+ This tool wraps the 'loadObject' function from the R package 'alabaster.base'.
130
+ It reads an SFE object from a staged directory, reconstructs it in memory, and
131
+ saves the complete object as a single RDS file. This is the "load" or "deserialize"
132
+ counterpart to the staging function.
133
+
134
+ Args:
135
+ staged_dir: Path to the directory containing the staged object assets.
136
+ output_rds: Path for the output RDS file where the loaded SFE object will be saved.
137
+ path: The relative path within 'staged_dir' where the object's 'object.json' is located.
138
+ If empty, it's assumed to be at the root of 'staged_dir'.
139
+
140
+ Returns:
141
+ A dictionary containing the execution details and the path to the output RDS file.
142
+ """
143
+ # 1. Input Validation
144
+ if not staged_dir.is_dir():
145
+ raise NotADirectoryError(f"Staged asset directory not found at: {staged_dir}")
146
+
147
+ metadata_json_path = staged_dir.joinpath(path, "object.json")
148
+ if not metadata_json_path.is_file():
149
+ raise FileNotFoundError(f"Metadata file 'object.json' not found at: {metadata_json_path}")
150
+
151
+ try:
152
+ output_rds.parent.mkdir(parents=True, exist_ok=True)
153
+ except OSError as e:
154
+ raise OSError(f"Failed to create output directory {output_rds.parent}: {e}")
155
+
156
+ # 2. R script construction
157
+ r_script_content = f"""
158
+ suppressPackageStartupMessages(library(alabaster.sfe))
159
+ suppressPackageStartupMessages(library(SpatialFeatureExperiment))
160
+
161
+ tryCatch({{
162
+ sfe_object <- alabaster.base::loadObject(
163
+ '{metadata_json_path.resolve()}',
164
+ project = '{staged_dir.resolve()}'
165
+ )
166
+ saveRDS(sfe_object, file = '{output_rds.resolve()}')
167
+ cat('{output_rds.resolve()}')
168
+ }}, error = function(e) {{
169
+ message("Error during R script execution: ", e$message)
170
+ quit(status = 1)
171
+ }})
172
+ """
173
+
174
+ # 3. Subprocess Execution
175
+ cmd = ["Rscript", "-e", r_script_content]
176
+ command_executed = " ".join(map(str, cmd))
177
+ logger.info(f"Executing command: {command_executed}")
178
+
179
+ try:
180
+ process = subprocess.run(
181
+ cmd,
182
+ check=True,
183
+ capture_output=True,
184
+ text=True,
185
+ encoding='utf-8'
186
+ )
187
+
188
+ stdout = process.stdout.strip()
189
+ stderr = process.stderr.strip()
190
+
191
+ output_file = Path(stdout)
192
+ if not output_file.is_file() or output_file != output_rds.resolve():
193
+ raise FileNotFoundError(
194
+ f"Expected output RDS file not created at {output_rds}. R stderr: {stderr}"
195
+ )
196
+
197
+ return {
198
+ "command_executed": command_executed,
199
+ "stdout": stdout,
200
+ "stderr": stderr,
201
+ "output_files": {
202
+ "output_rds": str(output_file)
203
+ }
204
+ }
205
+
206
+ except FileNotFoundError:
207
+ error_msg = "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH."
208
+ logger.error(error_msg)
209
+ raise RuntimeError(error_msg) from None
210
+ except subprocess.CalledProcessError as e:
211
+ logger.error(f"Command failed with exit code {e.returncode}")
212
+ logger.error(f"STDOUT: {e.stdout}")
213
+ logger.error(f"STDERR: {e.stderr}")
214
+ raise RuntimeError(
215
+ f"R script execution failed with exit code {e.returncode}.\n"
216
+ f"STDOUT: {e.stdout}\n"
217
+ f"STDERR: {e.stderr}"
218
+ ) from e
219
+
220
+ if __name__ == "__main__":
221
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_alabaster_sfe'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-alabaster.sfe:
5
+ build: .
6
+ image: mcp-bioconductor-alabaster.sfe:latest
7
+ container_name: mcp-bioconductor-alabaster.sfe
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-alabaster.sfe
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-alabaster.sfe
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-alabaster.sfe/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-biocneighbors/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-biocneighbors
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-biocneighbors/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-biocsingular/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-biocsingular via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-biocsingular -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY bioconductor-biocsingular_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-biocsingular_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-biocsingular_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-biocsingular/app/bioconductor-biocsingular_server.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional, List, Dict, Any
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ SERVER_NAME = 'local_bioconductor_biocsingular'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def biocsingular_run_svd(
13
+ input_path: str,
14
+ output_prefix: str,
15
+ k: int = 5,
16
+ nu: int = 5,
17
+ nv: int = 5,
18
+ algorithm: str = "Irlba",
19
+ center: bool = True,
20
+ scale: bool = False,
21
+ deferred: bool = False,
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Perform Singular Value Decomposition (SVD) on a matrix using BiocSingular.
25
+ Supports various algorithms including Exact, Irlba, and Random SVD.
26
+
27
+ Args:
28
+ input_path: Path to the input matrix file (CSV, TSV, or RDS format).
29
+ output_prefix: Prefix for the output files (will create .rds and .csv files).
30
+ k: Number of singular values to compute.
31
+ nu: Number of left singular vectors to compute.
32
+ nv: Number of right singular vectors to compute.
33
+ algorithm: SVD algorithm to use ('Exact', 'Irlba', 'Random', 'FastDot').
34
+ center: Whether to center the columns of the matrix.
35
+ scale: Whether to scale the columns of the matrix.
36
+ deferred: Whether to use deferred centering and scaling (memory efficient).
37
+ """
38
+ input_file = Path(input_path)
39
+ if not input_file.exists():
40
+ return {"error": f"Input file not found: {input_path}"}
41
+
42
+ # Validate algorithm
43
+ valid_algos = ["Exact", "Irlba", "Random", "FastDot"]
44
+ if algorithm not in valid_algos:
45
+ return {"error": f"Invalid algorithm. Must be one of: {', '.join(valid_algos)}"}
46
+
47
+ # Construct R script
48
+ r_script = f"""
49
+ library(BiocSingular)
50
+
51
+ # Load data
52
+ ext <- tolower(tools::file_ext("{input_path}"))
53
+ if (ext == "rds") {{
54
+ mat <- readRDS("{input_path}")
55
+ }} else if (ext == "csv") {{
56
+ mat <- as.matrix(read.csv("{input_path}", row.names=1))
57
+ }} else {{
58
+ mat <- as.matrix(read.table("{input_path}", sep="\\t", header=TRUE, row.names=1))
59
+ }}
60
+
61
+ # Set parameter object
62
+ param <- {algorithm}Param(deferred = {"TRUE" if deferred else "FALSE"})
63
+
64
+ # Run SVD
65
+ res <- runSVD(
66
+ mat,
67
+ k = {k},
68
+ nu = {nu},
69
+ nv = {nv},
70
+ BSPARAM = param,
71
+ center = {"TRUE" if center else "FALSE"},
72
+ scale = {"TRUE" if scale else "FALSE"}
73
+ )
74
+
75
+ # Save results
76
+ saveRDS(res, "{output_prefix}_svd.rds")
77
+ write.csv(res$u, "{output_prefix}_u.csv")
78
+ write.csv(res$v, "{output_prefix}_v.csv")
79
+ write.csv(data.frame(d=res$d), "{output_prefix}_d.csv")
80
+ """
81
+
82
+ try:
83
+ result = subprocess.run(
84
+ ["Rscript", "-e", r_script],
85
+ capture_output=True,
86
+ text=True,
87
+ check=True
88
+ )
89
+
90
+ output_files = [
91
+ f"{output_prefix}_svd.rds",
92
+ f"{output_prefix}_u.csv",
93
+ f"{output_prefix}_v.csv",
94
+ f"{output_prefix}_d.csv"
95
+ ]
96
+
97
+ return {
98
+ "command_executed": f"BiocSingular::runSVD via Rscript",
99
+ "stdout": result.stdout,
100
+ "stderr": result.stderr,
101
+ "output_files": [str(Path(f).absolute()) for f in output_files if Path(f).exists()]
102
+ }
103
+ except subprocess.CalledProcessError as e:
104
+ return {
105
+ "error": "R execution failed",
106
+ "stdout": e.stdout,
107
+ "stderr": e.stderr
108
+ }
109
+
110
+ @mcp.tool()
111
+ def biocsingular_run_pca(
112
+ input_path: str,
113
+ output_prefix: str,
114
+ rank: int = 5,
115
+ algorithm: str = "Irlba",
116
+ center: bool = True,
117
+ scale: bool = False,
118
+ deferred: bool = False,
119
+ ) -> Dict[str, Any]:
120
+ """
121
+ Perform Principal Component Analysis (PCA) on a matrix using BiocSingular.
122
+ Optimized for large-scale genomic data.
123
+
124
+ Args:
125
+ input_path: Path to the input matrix file (CSV, TSV, or RDS format).
126
+ output_prefix: Prefix for the output files.
127
+ rank: Number of principal components to compute.
128
+ algorithm: PCA algorithm to use ('Exact', 'Irlba', 'Random', 'FastDot').
129
+ center: Whether to center the columns.
130
+ scale: Whether to scale the columns.
131
+ deferred: Whether to use deferred centering and scaling.
132
+ """
133
+ input_file = Path(input_path)
134
+ if not input_file.exists():
135
+ return {"error": f"Input file not found: {input_path}"}
136
+
137
+ valid_algos = ["Exact", "Irlba", "Random", "FastDot"]
138
+ if algorithm not in valid_algos:
139
+ return {"error": f"Invalid algorithm. Must be one of: {', '.join(valid_algos)}"}
140
+
141
+ r_script = f"""
142
+ library(BiocSingular)
143
+
144
+ # Load data
145
+ ext <- tolower(tools::file_ext("{input_path}"))
146
+ if (ext == "rds") {{
147
+ mat <- readRDS("{input_path}")
148
+ }} else if (ext == "csv") {{
149
+ mat <- as.matrix(read.csv("{input_path}", row.names=1))
150
+ }} else {{
151
+ mat <- as.matrix(read.table("{input_path}", sep="\\t", header=TRUE, row.names=1))
152
+ }}
153
+
154
+ # Set parameter object
155
+ param <- {algorithm}Param(deferred = {"TRUE" if deferred else "FALSE"})
156
+
157
+ # Run PCA
158
+ res <- runPCA(
159
+ mat,
160
+ rank = {rank},
161
+ BSPARAM = param,
162
+ center = {"TRUE" if center else "FALSE"},
163
+ scale = {"TRUE" if scale else "FALSE"}
164
+ )
165
+
166
+ # Save results
167
+ saveRDS(res, "{output_prefix}_pca.rds")
168
+ write.csv(res$x, "{output_prefix}_pcs.csv")
169
+ write.csv(res$rotation, "{output_prefix}_loadings.csv")
170
+ """
171
+
172
+ try:
173
+ result = subprocess.run(
174
+ ["Rscript", "-e", r_script],
175
+ capture_output=True,
176
+ text=True,
177
+ check=True
178
+ )
179
+
180
+ output_files = [
181
+ f"{output_prefix}_pca.rds",
182
+ f"{output_prefix}_pcs.csv",
183
+ f"{output_prefix}_loadings.csv"
184
+ ]
185
+
186
+ return {
187
+ "command_executed": f"BiocSingular::runPCA via Rscript",
188
+ "stdout": result.stdout,
189
+ "stderr": result.stderr,
190
+ "output_files": [str(Path(f).absolute()) for f in output_files if Path(f).exists()]
191
+ }
192
+ except subprocess.CalledProcessError as e:
193
+ return {
194
+ "error": "R execution failed",
195
+ "stdout": e.stdout,
196
+ "stderr": e.stderr
197
+ }
198
+
199
+ @mcp.tool()
200
+ def biocsingular_get_info() -> Dict[str, Any]:
201
+ """
202
+ Check if BiocSingular is installed and return version information.
203
+ """
204
+ r_script = "library(BiocSingular); cat(as.character(packageVersion('BiocSingular')))"
205
+
206
+ try:
207
+ result = subprocess.run(
208
+ ["Rscript", "-e", r_script],
209
+ capture_output=True,
210
+ text=True,
211
+ check=True
212
+ )
213
+ return {
214
+ "biocsingular_version": result.stdout.strip(),
215
+ "status": "installed"
216
+ }
217
+ except subprocess.CalledProcessError:
218
+ return {
219
+ "status": "not_found",
220
+ "error": "BiocSingular is not installed in the R environment."
221
+ }
222
+
223
+ if __name__ == "__main__":
224
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-biocsingular/app/bioconductor-biocsingular_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_help_txt/mcp_bioconductor-biocsingular/app/bioconductor-biocsingular_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_biocsingular'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-biocsingular/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-biocsingular:
5
+ build: .
6
+ image: mcp-bioconductor-biocsingular:latest
7
+ container_name: mcp-bioconductor-biocsingular
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-biocsingular
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_bioconductor-biocsingular/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-biocsingular
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-biocsingular/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-bulksignalr/app/bioconductor-bulksignalr_server.py ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # The actual MCP environment will provide this.
8
+ class mcp:
9
+ @staticmethod
10
+ def tool():
11
+ def decorator(func):
12
+ return func
13
+ return decorator
14
+
15
+ # --- R Script Templates ---
16
+
17
+ R_SCRIPT_BULK_DE = """
18
+ # Load libraries
19
+ suppressPackageStartupMessages(library(argparse))
20
+ suppressPackageStartupMessages(library(bulksignalr))
21
+ suppressPackageStartupMessages(library(SingleCellExperiment))
22
+ suppressPackageStartupMessages(library(Matrix))
23
+
24
+ # Create a parser
25
+ parser <- ArgumentParser(description = "Run bulksignalr bulk_de")
26
+
27
+ # Add arguments
28
+ parser$add_argument("--counts", type = "character", required = TRUE, help = "Path to the counts matrix file (TSV format)")
29
+ parser$add_argument("--metadata", type = "character", required = TRUE, help = "Path to the metadata file (TSV format)")
30
+ parser$add_argument("--output", type = "character", required = TRUE, help = "Path to the output DE results file (TSV format)")
31
+ parser$add_argument("--design", type = "character", required = TRUE, help = "Design formula for the model (e.g., '~ 0 + group')")
32
+ parser$add_argument("--sample_col", type = "character", default = "sample", help = "Column name for samples in metadata")
33
+ parser$add_argument("--group_col", type = "character", default = "group", help = "Column name for groups in metadata")
34
+ parser$add_argument("--lfc_threshold", type = "double", default = 0, help = "Log-fold change threshold")
35
+ parser$add_argument("--fdr_threshold", type = "double", default = 0.05, help = "FDR threshold")
36
+ parser$add_argument("--min_cells", type = "integer", default = 3, help = "Minimum number of cells expressing a gene")
37
+ parser$add_argument("--min_samples", type = "integer", default = 2, help = "Minimum number of samples in a group")
38
+ parser$add_argument("--n_cores", type = "integer", default = 1, help = "Number of cores to use")
39
+
40
+ # Parse arguments
41
+ args <- parser$parse_args()
42
+
43
+ # Read data
44
+ counts <- read.csv(args$counts, sep = "\\t", row.names = 1, check.names = FALSE)
45
+ counts_matrix <- as.matrix(counts)
46
+ metadata <- read.csv(args$metadata, sep = "\\t", row.names = 1, check.names = FALSE)
47
+
48
+ # Ensure metadata rows match counts columns
49
+ if (!all(colnames(counts_matrix) %in% rownames(metadata))) {
50
+ stop("Not all column names in the counts matrix are present as row names in the metadata.")
51
+ }
52
+ metadata <- metadata[colnames(counts_matrix), , drop = FALSE]
53
+
54
+ # Create SingleCellExperiment object
55
+ sce <- SingleCellExperiment(assays = list(counts = counts_matrix),
56
+ colData = metadata)
57
+
58
+ # Run bulk_de
59
+ de_results <- bulk_de(
60
+ object = sce,
61
+ design = as.formula(args$design),
62
+ sample_col = args$sample_col,
63
+ group_col = args$group_col,
64
+ lfc_threshold = args$lfc_threshold,
65
+ fdr_threshold = args$fdr_threshold,
66
+ min_cells = args$min_cells,
67
+ min_samples = args$min_samples,
68
+ n_cores = args$n_cores
69
+ )
70
+
71
+ # Save results
72
+ write.table(de_results, file = args$output, sep = "\\t", row.names = FALSE, quote = FALSE)
73
+
74
+ cat("bulk_de analysis completed successfully.\\n")
75
+ """
76
+
77
+ R_SCRIPT_BULK_WALK = """
78
+ # Load libraries
79
+ suppressPackageStartupMessages(library(argparse))
80
+ suppressPackageStartupMessages(library(bulksignalr))
81
+ suppressPackageStartupMessages(library(SingleCellExperiment))
82
+ suppressPackageStartupMessages(library(Matrix))
83
+
84
+ # Create a parser
85
+ parser <- ArgumentParser(description = "Run bulksignalr bulk_walk")
86
+
87
+ # Add arguments
88
+ parser$add_argument("--counts", type = "character", required = TRUE, help = "Path to the counts matrix file (TSV format)")
89
+ parser$add_argument("--metadata", type = "character", required = TRUE, help = "Path to the metadata file (TSV format)")
90
+ parser$add_argument("--de_results", type = "character", required = TRUE, help = "Path to the DE results file from bulk_de (TSV format)")
91
+ parser$add_argument("--output", type = "character", required = TRUE, help = "Path to the output co-regulation results file (TSV format)")
92
+ parser$add_argument("--sample_col", type = "character", default = "sample", help = "Column name for samples in metadata")
93
+ parser$add_argument("--group_col", type = "character", default = "group", help = "Column name for groups in metadata")
94
+ parser$add_argument("--cor_method", type = "character", choices = c("spearman", "pearson"), default = "spearman", help = "Correlation method")
95
+ parser$add_argument("--cor_threshold", type = "double", default = 0.4, help = "Correlation threshold")
96
+ parser$add_argument("--n_cores", type = "integer", default = 1, help = "Number of cores to use")
97
+
98
+ # Parse arguments
99
+ args <- parser$parse_args()
100
+
101
+ # Read data
102
+ counts <- read.csv(args$counts, sep = "\\t", row.names = 1, check.names = FALSE)
103
+ counts_matrix <- as.matrix(counts)
104
+ metadata <- read.csv(args$metadata, sep = "\\t", row.names = 1, check.names = FALSE)
105
+ de_results <- read.csv(args$de_results, sep = "\\t", check.names = FALSE)
106
+
107
+ # Ensure metadata rows match counts columns
108
+ if (!all(colnames(counts_matrix) %in% rownames(metadata))) {
109
+ stop("Not all column names in the counts matrix are present as row names in the metadata.")
110
+ }
111
+ metadata <- metadata[colnames(counts_matrix), , drop = FALSE]
112
+
113
+ # Create SingleCellExperiment object
114
+ sce <- SingleCellExperiment(assays = list(counts = counts_matrix),
115
+ colData = metadata)
116
+ colData(sce)[[args$sample_col]] <- colData(sce)[[args$sample_col]]
117
+ colData(sce)[[args$group_col]] <- colData(sce)[[args$group_col]]
118
+
119
+ # Run bulk_walk
120
+ walk_results <- bulk_walk(
121
+ object = sce,
122
+ de_results = de_results,
123
+ sample_col = args$sample_col,
124
+ group_col = args$group_col,
125
+ cor_method = args$cor_method,
126
+ cor_threshold = args$cor_threshold,
127
+ n_cores = args$n_cores
128
+ )
129
+
130
+ # Save results
131
+ write.table(walk_results, file = args$output, sep = "\\t", row.names = FALSE, quote = FALSE)
132
+
133
+ cat("bulk_walk analysis completed successfully.\\n")
134
+ """
135
+
136
+ R_SCRIPT_SIGNAL_WALK = """
137
+ # Load libraries
138
+ suppressPackageStartupMessages(library(argparse))
139
+ suppressPackageStartupMessages(library(bulksignalr))
140
+ suppressPackageStartupMessages(library(SingleCellExperiment))
141
+ suppressPackageStartupMessages(library(Matrix))
142
+
143
+ # Create a parser
144
+ parser <- ArgumentParser(description = "Run bulksignalr signal_walk")
145
+
146
+ # Add arguments
147
+ parser$add_argument("--counts", type = "character", required = TRUE, help = "Path to the counts matrix file (TSV format)")
148
+ parser$add_argument("--metadata", type = "character", required = TRUE, help = "Path to the metadata file (TSV format)")
149
+ parser$add_argument("--bulk_walk_results", type = "character", required = TRUE, help = "Path to the bulk_walk results file (TSV format)")
150
+ parser$add_argument("--output", type = "character", required = TRUE, help = "Path to the output signaling results file (TSV format)")
151
+ parser$add_argument("--sample_col", type = "character", default = "sample", help = "Column name for samples in metadata")
152
+ parser$add_argument("--group_col", type = "character", default = "group", help = "Column name for groups in metadata")
153
+ parser$add_argument("--lr_db", type = "character", default = NULL, help = "Optional path to a custom ligand-receptor database file (TSV format)")
154
+ parser$add_argument("--n_perms", type = "integer", default = 1000, help = "Number of permutations for significance testing")
155
+ parser$add_argument("--n_cores", type = "integer", default = 1, help = "Number of cores to use")
156
+
157
+ # Parse arguments
158
+ args <- parser$parse_args()
159
+
160
+ # Read data
161
+ counts <- read.csv(args$counts, sep = "\\t", row.names = 1, check.names = FALSE)
162
+ counts_matrix <- as.matrix(counts)
163
+ metadata <- read.csv(args$metadata, sep = "\\t", row.names = 1, check.names = FALSE)
164
+ bulk_walk_results <- read.csv(args$bulk_walk_results, sep = "\\t", check.names = FALSE)
165
+
166
+ # Load ligand-receptor database
167
+ if (!is.null(args$lr_db)) {
168
+ lr_db <- read.csv(args$lr_db, sep = "\\t", check.names = FALSE)
169
+ } else {
170
+ lr_db <- bulksignalr::lr_db
171
+ }
172
+
173
+ # Ensure metadata rows match counts columns
174
+ if (!all(colnames(counts_matrix) %in% rownames(metadata))) {
175
+ stop("Not all column names in the counts matrix are present as row names in the metadata.")
176
+ }
177
+ metadata <- metadata[colnames(counts_matrix), , drop = FALSE]
178
+
179
+ # Create SingleCellExperiment object
180
+ sce <- SingleCellExperiment(assays = list(counts = counts_matrix),
181
+ colData = metadata)
182
+ colData(sce)[[args$sample_col]] <- colData(sce)[[args$sample_col]]
183
+ colData(sce)[[args$group_col]] <- colData(sce)[[args$group_col]]
184
+
185
+ # Run signal_walk
186
+ signal_results <- signal_walk(
187
+ object = sce,
188
+ bulk_walk_results = bulk_walk_results,
189
+ ligand_receptor_db = lr_db,
190
+ sample_col = args$sample_col,
191
+ group_col = args$group_col,
192
+ n_perms = args$n_perms,
193
+ n_cores = args$n_cores
194
+ )
195
+
196
+ # Save results
197
+ write.table(signal_results, file = args$output, sep = "\\t", row.names = FALSE, quote = FALSE)
198
+
199
+ cat("signal_walk analysis completed successfully.\\n")
200
+ """
201
+
202
+ # --- MCP Tools ---
203
+
204
+ from mcp.server.fastmcp import FastMCP
205
+
206
+ SERVER_NAME = 'local_bioconductor_bulksignalr'
207
+ mcp = FastMCP(SERVER_NAME)
208
+
209
+ @mcp.tool()
210
+ def bulk_de(
211
+ counts_file: Path,
212
+ metadata_file: Path,
213
+ design: str,
214
+ output_file: Path,
215
+ sample_col: str = "sample",
216
+ group_col: str = "group",
217
+ lfc_threshold: float = 0.0,
218
+ fdr_threshold: float = 0.05,
219
+ min_cells: int = 3,
220
+ min_samples: int = 2,
221
+ n_cores: int = 1,
222
+ ):
223
+ """
224
+ Performs differential expression analysis using bulksignalr::bulk_de.
225
+
226
+ This tool requires an R environment with the 'bulksignalr', 'SingleCellExperiment',
227
+ 'Matrix', and 'argparse' packages installed.
228
+
229
+ Args:
230
+ counts_file: Path to the counts matrix file (TSV format, genes as rows, cells/samples as columns).
231
+ metadata_file: Path to the metadata file (TSV format, cells/samples as rows).
232
+ design: Design formula for the model (e.g., '~ 0 + group').
233
+ output_file: Path to save the differential expression results (TSV format).
234
+ sample_col: Column name for samples in the metadata file.
235
+ group_col: Column name for groups in the metadata file.
236
+ lfc_threshold: Log-fold change threshold for differential expression.
237
+ fdr_threshold: False discovery rate (FDR) threshold.
238
+ min_cells: Minimum number of cells a gene must be expressed in to be included.
239
+ min_samples: Minimum number of samples required within a group for comparison.
240
+ n_cores: Number of CPU cores to use for parallel processing.
241
+ """
242
+ # Input validation
243
+ if not counts_file.is_file():
244
+ raise FileNotFoundError(f"Counts file not found: {counts_file}")
245
+ if not metadata_file.is_file():
246
+ raise FileNotFoundError(f"Metadata file not found: {metadata_file}")
247
+ if not design.strip():
248
+ raise ValueError("Design formula cannot be empty.")
249
+ if not (0 <= fdr_threshold <= 1):
250
+ raise ValueError("fdr_threshold must be between 0 and 1.")
251
+ if lfc_threshold < 0:
252
+ raise ValueError("lfc_threshold must be non-negative.")
253
+ if min_cells < 0:
254
+ raise ValueError("min_cells must be non-negative.")
255
+ if min_samples < 1:
256
+ raise ValueError("min_samples must be at least 1.")
257
+ if n_cores < 1:
258
+ raise ValueError("n_cores must be at least 1.")
259
+
260
+ output_file.parent.mkdir(parents=True, exist_ok=True)
261
+
262
+ r_script_path = ""
263
+ try:
264
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
265
+ r_script_file.write(R_SCRIPT_BULK_DE)
266
+ r_script_path = r_script_file.name
267
+
268
+ cmd = [
269
+ "Rscript", r_script_path,
270
+ "--counts", str(counts_file),
271
+ "--metadata", str(metadata_file),
272
+ "--output", str(output_file),
273
+ "--design", design,
274
+ "--sample_col", sample_col,
275
+ "--group_col", group_col,
276
+ "--lfc_threshold", str(lfc_threshold),
277
+ "--fdr_threshold", str(fdr_threshold),
278
+ "--min_cells", str(min_cells),
279
+ "--min_samples", str(min_samples),
280
+ "--n_cores", str(n_cores),
281
+ ]
282
+
283
+ process = subprocess.run(cmd, check=True, capture_output=True, text=True)
284
+
285
+ return {
286
+ "command_executed": " ".join(cmd),
287
+ "stdout": process.stdout,
288
+ "stderr": process.stderr,
289
+ "output_files": [str(output_file)],
290
+ }
291
+ except subprocess.CalledProcessError as e:
292
+ raise RuntimeError(
293
+ f"R script execution failed with return code {e.returncode}.\n"
294
+ f"Command: {' '.join(e.cmd)}\n"
295
+ f"Stdout: {e.stdout}\n"
296
+ f"Stderr: {e.stderr}"
297
+ ) from e
298
+ finally:
299
+ if r_script_path and Path(r_script_path).exists():
300
+ Path(r_script_path).unlink()
301
+
302
+
303
+ @mcp.tool()
304
+ def bulk_walk(
305
+ counts_file: Path,
306
+ metadata_file: Path,
307
+ de_results_file: Path,
308
+ output_file: Path,
309
+ sample_col: str = "sample",
310
+ group_col: str = "group",
311
+ cor_method: str = "spearman",
312
+ cor_threshold: float = 0.4,
313
+ n_cores: int = 1,
314
+ ):
315
+ """
316
+ Identifies co-regulated gene clusters using bulksignalr::bulk_walk.
317
+
318
+ This tool requires an R environment with the 'bulksignalr', 'SingleCellExperiment',
319
+ 'Matrix', and 'argparse' packages installed.
320
+
321
+ Args:
322
+ counts_file: Path to the counts matrix file (TSV format).
323
+ metadata_file: Path to the metadata file (TSV format).
324
+ de_results_file: Path to the differential expression results file from `bulk_de`.
325
+ output_file: Path to save the co-regulation cluster results (TSV format).
326
+ sample_col: Column name for samples in the metadata file.
327
+ group_col: Column name for groups in the metadata file.
328
+ cor_method: Correlation method to use ('spearman' or 'pearson').
329
+ cor_threshold: Correlation threshold for defining co-regulation.
330
+ n_cores: Number of CPU cores to use for parallel processing.
331
+ """
332
+ # Input validation
333
+ if not counts_file.is_file():
334
+ raise FileNotFoundError(f"Counts file not found: {counts_file}")
335
+ if not metadata_file.is_file():
336
+ raise FileNotFoundError(f"Metadata file not found: {metadata_file}")
337
+ if not de_results_file.is_file():
338
+ raise FileNotFoundError(f"DE results file not found: {de_results_file}")
339
+ if cor_method not in ["spearman", "pearson"]:
340
+ raise ValueError("cor_method must be either 'spearman' or 'pearson'.")
341
+ if not (0 <= cor_threshold <= 1):
342
+ raise ValueError("cor_threshold must be between 0 and 1.")
343
+ if n_cores < 1:
344
+ raise ValueError("n_cores must be at least 1.")
345
+
346
+ output_file.parent.mkdir(parents=True, exist_ok=True)
347
+
348
+ r_script_path = ""
349
+ try:
350
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
351
+ r_script_file.write(R_SCRIPT_BULK_WALK)
352
+ r_script_path = r_script_file.name
353
+
354
+ cmd = [
355
+ "Rscript", r_script_path,
356
+ "--counts", str(counts_file),
357
+ "--metadata", str(metadata_file),
358
+ "--de_results", str(de_results_file),
359
+ "--output", str(output_file),
360
+ "--sample_col", sample_col,
361
+ "--group_col", group_col,
362
+ "--cor_method", cor_method,
363
+ "--cor_threshold", str(cor_threshold),
364
+ "--n_cores", str(n_cores),
365
+ ]
366
+
367
+ process = subprocess.run(cmd, check=True, capture_output=True, text=True)
368
+
369
+ return {
370
+ "command_executed": " ".join(cmd),
371
+ "stdout": process.stdout,
372
+ "stderr": process.stderr,
373
+ "output_files": [str(output_file)],
374
+ }
375
+ except subprocess.CalledProcessError as e:
376
+ raise RuntimeError(
377
+ f"R script execution failed with return code {e.returncode}.\n"
378
+ f"Command: {' '.join(e.cmd)}\n"
379
+ f"Stdout: {e.stdout}\n"
380
+ f"Stderr: {e.stderr}"
381
+ ) from e
382
+ finally:
383
+ if r_script_path and Path(r_script_path).exists():
384
+ Path(r_script_path).unlink()
385
+
386
+
387
+ @mcp.tool()
388
+ def signal_walk(
389
+ counts_file: Path,
390
+ metadata_file: Path,
391
+ bulk_walk_results_file: Path,
392
+ output_file: Path,
393
+ sample_col: str = "sample",
394
+ group_col: str = "group",
395
+ ligand_receptor_db_file: Optional[Path] = None,
396
+ n_perms: int = 1000,
397
+ n_cores: int = 1,
398
+ ):
399
+ """
400
+ Identifies potential signaling genes using bulksignalr::signal_walk.
401
+
402
+ This tool requires an R environment with the 'bulksignalr', 'SingleCellExperiment',
403
+ 'Matrix', and 'argparse' packages installed.
404
+
405
+ Args:
406
+ counts_file: Path to the counts matrix file (TSV format).
407
+ metadata_file: Path to the metadata file (TSV format).
408
+ bulk_walk_results_file: Path to the co-regulation results from `bulk_walk`.
409
+ output_file: Path to save the signaling analysis results (TSV format).
410
+ sample_col: Column name for samples in the metadata file.
411
+ group_col: Column name for groups in the metadata file.
412
+ ligand_receptor_db_file: Optional path to a custom ligand-receptor database (TSV).
413
+ If not provided, the built-in database is used.
414
+ n_perms: Number of permutations for significance testing.
415
+ n_cores: Number of CPU cores to use for parallel processing.
416
+ """
417
+ # Input validation
418
+ if not counts_file.is_file():
419
+ raise FileNotFoundError(f"Counts file not found: {counts_file}")
420
+ if not metadata_file.is_file():
421
+ raise FileNotFoundError(f"Metadata file not found: {metadata_file}")
422
+ if not bulk_walk_results_file.is_file():
423
+ raise FileNotFoundError(f"bulk_walk results file not found: {bulk_walk_results_file}")
424
+ if ligand_receptor_db_file and not ligand_receptor_db_file.is_file():
425
+ raise FileNotFoundError(f"Ligand-receptor DB file not found: {ligand_receptor_db_file}")
426
+ if n_perms < 1:
427
+ raise ValueError("n_perms must be at least 1.")
428
+ if n_cores < 1:
429
+ raise ValueError("n_cores must be at least 1.")
430
+
431
+ output_file.parent.mkdir(parents=True, exist_ok=True)
432
+
433
+ r_script_path = ""
434
+ try:
435
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
436
+ r_script_file.write(R_SCRIPT_SIGNAL_WALK)
437
+ r_script_path = r_script_file.name
438
+
439
+ cmd = [
440
+ "Rscript", r_script_path,
441
+ "--counts", str(counts_file),
442
+ "--metadata", str(metadata_file),
443
+ "--bulk_walk_results", str(bulk_walk_results_file),
444
+ "--output", str(output_file),
445
+ "--sample_col", sample_col,
446
+ "--group_col", group_col,
447
+ "--n_perms", str(n_perms),
448
+ "--n_cores", str(n_cores),
449
+ ]
450
+ if ligand_receptor_db_file:
451
+ cmd.extend(["--lr_db", str(ligand_receptor_db_file)])
452
+
453
+ process = subprocess.run(cmd, check=True, capture_output=True, text=True)
454
+
455
+ return {
456
+ "command_executed": " ".join(cmd),
457
+ "stdout": process.stdout,
458
+ "stderr": process.stderr,
459
+ "output_files": [str(output_file)],
460
+ }
461
+ except subprocess.CalledProcessError as e:
462
+ raise RuntimeError(
463
+ f"R script execution failed with return code {e.returncode}.\n"
464
+ f"Command: {' '.join(e.cmd)}\n"
465
+ f"Stdout: {e.stdout}\n"
466
+ f"Stderr: {e.stderr}"
467
+ ) from e
468
+ finally:
469
+ if r_script_path and Path(r_script_path).exists():
470
+ Path(r_script_path).unlink()
471
+
472
+ if __name__ == "__main__":
473
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-clusterprofiler/app/bioconductor-clusterprofiler_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-clusterprofiler/app/bioconductor-clusterprofiler_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_clusterprofiler'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-genomeinfodb via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-genomeinfodb -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/bioconductor-genomeinfodb_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-genomeinfodb_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-genomeinfodb_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import Optional, List, Dict
6
+ import sys
7
+ import logging
8
+
9
+ # Configure logging
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # Mock the mcp.tool decorator for standalone execution
14
+ class mcp:
15
+ def tool(func):
16
+ return func
17
+
18
+ def _check_rscript():
19
+ """Checks if Rscript is available in the system PATH."""
20
+ if not shutil.which("Rscript"):
21
+ raise EnvironmentError(
22
+ "Rscript not found in PATH. R and the GenomeInfoDb Bioconductor "
23
+ "package are required to run these tools."
24
+ )
25
+
26
+ @mcp.tool
27
+ def genomeinfodb_list_styles(species: str) -> Dict:
28
+ """
29
+ Lists the available chromosome naming styles (e.g., UCSC, NCBI) for a given species.
30
+
31
+ This tool wraps the `genomeStyles()` function from the R/Bioconductor GenomeInfoDb package.
32
+
33
+ Args:
34
+ species: The scientific name of the species (e.g., "Homo sapiens", "Mus musculus").
35
+
36
+ Returns:
37
+ A dictionary containing the command executed, stdout (with the list of styles),
38
+ stderr, and a list of available styles.
39
+ """
40
+ _check_rscript()
41
+ if not species:
42
+ raise ValueError("The 'species' argument cannot be empty.")
43
+
44
+ r_script_content = f"""
45
+ suppressPackageStartupMessages(library(GenomeInfoDb))
46
+ tryCatch({{
47
+ styles <- genomeStyles('{species}')
48
+ if (length(styles) == 0) {{
49
+ cat("No styles found for species: {species}\\n", file=stderr())
50
+ }} else {{
51
+ cat(paste(names(styles), collapse='\\n'))
52
+ }}
53
+ }}, error = function(e) {{
54
+ cat(paste("Error:", e$message, "\\n"), file=stderr())
55
+ quit(status = 1)
56
+ }})
57
+ """
58
+
59
+ command = ["Rscript", "-e", r_script_content]
60
+ command_executed = " ".join(command)
61
+ logger.info(f"Executing command: {command_executed}")
62
+
63
+ try:
64
+ result = subprocess.run(
65
+ command,
66
+ capture_output=True,
67
+ text=True,
68
+ check=True,
69
+ )
70
+ styles_list = result.stdout.strip().split('\n') if result.stdout.strip() else []
71
+ return {
72
+ "command_executed": command_executed,
73
+ "stdout": result.stdout,
74
+ "stderr": result.stderr,
75
+ "styles": styles_list,
76
+ }
77
+ except subprocess.CalledProcessError as e:
78
+ logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}")
79
+ return {
80
+ "command_executed": command_executed,
81
+ "stdout": e.stdout,
82
+ "stderr": e.stderr,
83
+ "error": "Failed to list genome styles. Check if the species name is correct and GenomeInfoDb is installed.",
84
+ }
85
+
86
+ @mcp.tool
87
+ def genomeinfodb_map_seqlevels(
88
+ target_style: str,
89
+ species: str,
90
+ input_file: Path,
91
+ output_file: Path,
92
+ ) -> Dict:
93
+ """
94
+ Maps chromosome names from an input file to a specified naming convention (style).
95
+
96
+ This tool wraps the `mapSeqlevels()` function from the R/Bioconductor GenomeInfoDb package.
97
+ It produces a two-column TSV file: 'original' and 'mapped'.
98
+
99
+ Args:
100
+ target_style: The target naming style (e.g., "UCSC", "NCBI", "Ensembl").
101
+ species: The scientific name of the species (e.g., "Homo sapiens").
102
+ input_file: Path to a file containing one chromosome name per line.
103
+ output_file: Path to write the output TSV file with original and mapped names.
104
+
105
+ Returns:
106
+ A dictionary containing the command executed, stdout, stderr, and the path to the output file.
107
+ """
108
+ _check_rscript()
109
+ if not target_style:
110
+ raise ValueError("The 'target_style' argument cannot be empty.")
111
+ if not species:
112
+ raise ValueError("The 'species' argument cannot be empty.")
113
+ if not input_file.is_file():
114
+ raise FileNotFoundError(f"Input file not found: {input_file}")
115
+
116
+ output_file.parent.mkdir(parents=True, exist_ok=True)
117
+
118
+ r_script_content = f"""
119
+ suppressPackageStartupMessages(library(GenomeInfoDb))
120
+ args <- commandArgs(trailingOnly=TRUE)
121
+ inputFile <- args[1]
122
+ targetStyle <- args[2]
123
+ species <- args[3]
124
+ outputFile <- args[4]
125
+
126
+ tryCatch({{
127
+ seqs <- readLines(inputFile)
128
+ if (length(seqs) == 0) {{
129
+ stop("Input file is empty.")
130
+ }}
131
+ mapped_seqs <- mapSeqlevels(seqs, style=targetStyle, species=species)
132
+ output_df <- data.frame(original=seqs, mapped=mapped_seqs)
133
+ write.table(output_df, file=outputFile, sep='\\t', row.names=FALSE, col.names=TRUE, quote=FALSE)
134
+ }}, error = function(e) {{
135
+ cat(paste("Error:", e$message, "\\n"), file=stderr())
136
+ quit(status = 1)
137
+ }})
138
+ """
139
+
140
+ command = [
141
+ "Rscript",
142
+ "-e",
143
+ r_script_content,
144
+ str(input_file),
145
+ target_style,
146
+ species,
147
+ str(output_file),
148
+ ]
149
+ command_executed = " ".join(command)
150
+ logger.info(f"Executing command: {command_executed}")
151
+
152
+ try:
153
+ result = subprocess.run(
154
+ command,
155
+ capture_output=True,
156
+ text=True,
157
+ check=True,
158
+ )
159
+ return {
160
+ "command_executed": command_executed,
161
+ "stdout": result.stdout,
162
+ "stderr": result.stderr,
163
+ "output_files": {"mapped_seqlevels": str(output_file)},
164
+ }
165
+ except subprocess.CalledProcessError as e:
166
+ logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}")
167
+ return {
168
+ "command_executed": command_executed,
169
+ "stdout": e.stdout,
170
+ "stderr": e.stderr,
171
+ "error": "Failed to map seqlevels. Check parameters and ensure input file is correctly formatted.",
172
+ }
173
+
174
+ @mcp.tool
175
+ def genomeinfodb_get_seqinfo(
176
+ genome: str,
177
+ output_file: Path,
178
+ ) -> Dict:
179
+ """
180
+ Fetches sequence information (lengths, circularity) for a given genome assembly.
181
+
182
+ This tool wraps the `Seqinfo()` constructor from the R/Bioconductor GenomeInfoDb package.
183
+ It produces a TSV file containing seqnames, seqlengths, isCircular, and genome.
184
+
185
+ Args:
186
+ genome: The genome assembly identifier (e.g., "hg19", "hg38", "mm10").
187
+ output_file: Path to write the output TSV file with sequence information.
188
+
189
+ Returns:
190
+ A dictionary containing the command executed, stdout, stderr, and the path to the output file.
191
+ """
192
+ _check_rscript()
193
+ if not genome:
194
+ raise ValueError("The 'genome' argument cannot be empty.")
195
+
196
+ output_file.parent.mkdir(parents=True, exist_ok=True)
197
+
198
+ r_script_content = f"""
199
+ suppressPackageStartupMessages(library(GenomeInfoDb))
200
+ args <- commandArgs(trailingOnly=TRUE)
201
+ genome_id <- args[1]
202
+ outputFile <- args[2]
203
+
204
+ tryCatch({{
205
+ si <- Seqinfo(genome=genome_id)
206
+ si_df <- as.data.frame(si)
207
+ write.table(si_df, file=outputFile, sep='\\t', row.names=TRUE, col.names=NA, quote=FALSE)
208
+ }}, error = function(e) {{
209
+ cat(paste("Error:", e$message, "\\n"), file=stderr())
210
+ quit(status = 1)
211
+ }})
212
+ """
213
+
214
+ command = ["Rscript", "-e", r_script_content, genome, str(output_file)]
215
+ command_executed = " ".join(command)
216
+ logger.info(f"Executing command: {command_executed}")
217
+
218
+ try:
219
+ result = subprocess.run(
220
+ command,
221
+ capture_output=True,
222
+ text=True,
223
+ check=True,
224
+ )
225
+ return {
226
+ "command_executed": command_executed,
227
+ "stdout": result.stdout,
228
+ "stderr": result.stderr,
229
+ "output_files": {"seqinfo": str(output_file)},
230
+ }
231
+ except subprocess.CalledProcessError as e:
232
+ logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}")
233
+ return {
234
+ "command_executed": command_executed,
235
+ "stdout": e.stdout,
236
+ "stderr": e.stderr,
237
+ "error": "Failed to get seqinfo. Check if the genome identifier is correct.",
238
+ }
239
+
240
+ @mcp.tool
241
+ def genomeinfodb_rename_seqlevels(
242
+ input_file: Path,
243
+ mapping_file: Path,
244
+ output_file: Path,
245
+ ) -> Dict:
246
+ """
247
+ Renames chromosome names from an input file based on a provided mapping file.
248
+
249
+ This tool wraps the `renameSeqlevels()` function from the R/Bioconductor GenomeInfoDb package.
250
+ It produces a two-column TSV file: 'original' and 'renamed'.
251
+
252
+ Args:
253
+ input_file: Path to a file containing one chromosome name per line.
254
+ mapping_file: Path to a two-column TSV file (old_name<tab>new_name) without a header.
255
+ output_file: Path to write the output TSV file with original and renamed names.
256
+
257
+ Returns:
258
+ A dictionary containing the command executed, stdout, stderr, and the path to the output file.
259
+ """
260
+ _check_rscript()
261
+ if not input_file.is_file():
262
+ raise FileNotFoundError(f"Input file not found: {input_file}")
263
+ if not mapping_file.is_file():
264
+ raise FileNotFoundError(f"Mapping file not found: {mapping_file}")
265
+
266
+ output_file.parent.mkdir(parents=True, exist_ok=True)
267
+
268
+ r_script_content = f"""
269
+ suppressPackageStartupMessages(library(GenomeInfoDb))
270
+ args <- commandArgs(trailingOnly=TRUE)
271
+ inputFile <- args[1]
272
+ mappingFile <- args[2]
273
+ outputFile <- args[3]
274
+
275
+ tryCatch({{
276
+ seqs <- readLines(inputFile)
277
+ if (length(seqs) == 0) {{
278
+ stop("Input file is empty.")
279
+ }}
280
+ mapping_df <- read.table(mappingFile, sep='\\t', header=FALSE, stringsAsFactors=FALSE, col.names=c("old", "new"))
281
+ if (nrow(mapping_df) == 0) {{
282
+ stop("Mapping file is empty.")
283
+ }}
284
+ value <- setNames(mapping_df$new, mapping_df$old)
285
+ renamed_seqs <- renameSeqlevels(seqs, value)
286
+ output_df <- data.frame(original=seqs, renamed=renamed_seqs)
287
+ write.table(output_df, file=outputFile, sep='\\t', row.names=FALSE, col.names=TRUE, quote=FALSE)
288
+ }}, error = function(e) {{
289
+ cat(paste("Error:", e$message, "\\n"), file=stderr())
290
+ quit(status = 1)
291
+ }})
292
+ """
293
+
294
+ command = [
295
+ "Rscript",
296
+ "-e",
297
+ r_script_content,
298
+ str(input_file),
299
+ str(mapping_file),
300
+ str(output_file),
301
+ ]
302
+ command_executed = " ".join(command)
303
+ logger.info(f"Executing command: {command_executed}")
304
+
305
+ try:
306
+ result = subprocess.run(
307
+ command,
308
+ capture_output=True,
309
+ text=True,
310
+ check=True,
311
+ )
312
+ return {
313
+ "command_executed": command_executed,
314
+ "stdout": result.stdout,
315
+ "stderr": result.stderr,
316
+ "output_files": {"renamed_seqlevels": str(output_file)},
317
+ }
318
+ except subprocess.CalledProcessError as e:
319
+ logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}")
320
+ return {
321
+ "command_executed": command_executed,
322
+ "stdout": e.stdout,
323
+ "stderr": e.stderr,
324
+ "error": "Failed to rename seqlevels. Check file formats and content.",
325
+ }
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_genomeinfodb'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-genomeinfodb:
5
+ build: .
6
+ image: mcp-bioconductor-genomeinfodb:latest
7
+ container_name: mcp-bioconductor-genomeinfodb
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-genomeinfodb
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-genomeinfodb
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-genomeinfodb/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-ggtree/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-ggtree via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-ggtree -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/bioconductor-ggtree_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-ggtree_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-ggtree_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-ggtree/app/bioconductor-ggtree_server.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, List, Dict, Any
5
+ import os
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_bioconductor_ggtree'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def run_ggtree_script(
14
+ r_script_content: str,
15
+ input_tree_file: Optional[Path] = None,
16
+ input_annotation_file: Optional[Path] = None,
17
+ output_image_format: str = "png",
18
+ image_width: int = 800,
19
+ image_height: int = 600,
20
+ output_filename_prefix: str = "ggtree_plot",
21
+ ) -> Dict[str, Any]:
22
+ """
23
+ Executes a custom R script using the bioconductor-ggtree package for phylogenetic tree visualization.
24
+
25
+ This tool provides a flexible way to leverage ggtree's capabilities by allowing
26
+ users to provide arbitrary R code. It also facilitates common tasks like
27
+ loading tree and annotation files and saving plots.
28
+
29
+ Note: The 'bioconductor-ggtree' package is an R library and does not expose
30
+ direct command-line functions or subcommands. This MCP tool provides a
31
+ generic R script execution environment where 'ggtree' and its dependencies
32
+ are available. Users are expected to provide R code that utilizes 'ggtree'
33
+ functions.
34
+
35
+ Args:
36
+ r_script_content: A string containing the R code to execute. This script
37
+ should assume 'ggtree', 'treeio', 'ggplot2', and 'dplyr'
38
+ are loaded. It can access input files via variables
39
+ `input_tree_file` and `input_annotation_file` (which
40
+ will contain the POSIX paths as strings). The script
41
+ should write any desired output files to the current
42
+ working directory.
43
+ input_tree_file: Optional path to a phylogenetic tree file (e.g., Newick, Nexus).
44
+ If provided, its POSIX path will be available as the
45
+ `input_tree_file` variable in the R script.
46
+ input_annotation_file: Optional path to an annotation data file (e.g., CSV, TSV).
47
+ If provided, its POSIX path will be available as the
48
+ `input_annotation_file` variable in the R script.
49
+ output_image_format: The desired format for the output image. Supported: "png", "pdf", "svg", "jpeg", "tiff", "bmp".
50
+ This format will be used for the default `ggsave` call
51
+ if a ggplot object `p` is found and not explicitly saved
52
+ by `r_script_content`.
53
+ image_width: Width of the output image. For raster formats (png, jpeg, etc.),
54
+ this is in pixels. For vector formats (pdf, svg), this is
55
+ converted to inches (assuming 96 DPI for conversion from pixels).
56
+ image_height: Height of the output image. Similar to `image_width`.
57
+ output_filename_prefix: A prefix for the output image file. The full filename
58
+ will be '{prefix}.{format}'. This is used for the
59
+ default `ggsave` call.
60
+
61
+ Returns:
62
+ A dictionary containing:
63
+ - command_executed: The Rscript command that was run.
64
+ - stdout: Standard output from the R script.
65
+ - stderr: Standard error from the R script.
66
+ - output_files: A list of paths to any generated output files.
67
+ - error: An error message if the R script execution failed.
68
+ """
69
+ # Input validation
70
+ if not r_script_content.strip():
71
+ raise ValueError("r_script_content cannot be empty.")
72
+
73
+ if input_tree_file:
74
+ if not input_tree_file.is_file():
75
+ raise FileNotFoundError(f"Input tree file not found: {input_tree_file}")
76
+ input_tree_file_posix = input_tree_file.as_posix()
77
+ else:
78
+ input_tree_file_posix = ""
79
+
80
+ if input_annotation_file:
81
+ if not input_annotation_file.is_file():
82
+ raise FileNotFoundError(f"Input annotation file not found: {input_annotation_file}")
83
+ input_annotation_file_posix = input_annotation_file.as_posix()
84
+ else:
85
+ input_annotation_file_posix = ""
86
+
87
+ supported_formats = {"png", "pdf", "svg", "jpeg", "tiff", "bmp"}
88
+ if output_image_format.lower() not in supported_formats:
89
+ raise ValueError(f"Unsupported output image format: {output_image_format}. "
90
+ f"Supported formats are: {', '.join(supported_formats)}")
91
+
92
+ if image_width <= 0:
93
+ raise ValueError("Image width must be a positive integer.")
94
+ if image_height <= 0:
95
+ raise ValueError("Image height must be a positive integer.")
96
+ if not output_filename_prefix:
97
+ raise ValueError("Output filename prefix cannot be empty.")
98
+
99
+ with tempfile.TemporaryDirectory() as tmpdir:
100
+ work_dir = Path(tmpdir)
101
+
102
+ # Define output image path for default ggsave
103
+ output_image_name = f"{output_filename_prefix}.{output_image_format.lower()}"
104
+
105
+ # Construct the R script
106
+ r_code = f"""
107
+ # Load necessary libraries
108
+ suppressPackageStartupMessages(library(ggtree))
109
+ suppressPackageStartupMessages(library(treeio))
110
+ suppressPackageStartupMessages(library(ggplot2))
111
+ suppressPackageStartupMessages(library(dplyr)) # Often useful for data manipulation
112
+
113
+ # Set working directory for outputs
114
+ setwd("{work_dir.as_posix()}")
115
+
116
+ # Make input file paths available as R variables
117
+ input_tree_file <- if ("{input_tree_file_posix}" != "") {{
118
+ "{input_tree_file_posix}"
119
+ }} else {{
120
+ NULL
121
+ }}
122
+ input_annotation_file <- if ("{input_annotation_file_posix}" != "") {{
123
+ "{input_annotation_file_posix}"
124
+ }} else {{
125
+ NULL
126
+ }}
127
+
128
+ # Parameters for default ggsave
129
+ output_image_name_param <- "{output_image_name}"
130
+ image_width_param <- {image_width}
131
+ image_height_param <- {image_height}
132
+ output_image_format_param <- "{output_image_format.lower()}"
133
+
134
+ # Determine units and DPI for ggsave based on format
135
+ units_val <- "in" # Default for vector graphics
136
+ dpi_val <- NULL # Default for vector graphics
137
+
138
+ if (output_image_format_param %in% c("png", "jpeg", "tiff", "bmp")) {{
139
+ units_val <- "px"
140
+ dpi_val <- 300 # Common DPI for raster images
141
+ }} else {{ # pdf, svg, etc.
142
+ # Convert pixels to inches for vector formats if input was in pixels
143
+ # Assuming 96 DPI for screen pixels to inches conversion if no specific DPI is given
144
+ image_width_param <- image_width_param / 96
145
+ image_height_param <- image_height_param / 96
146
+ }}
147
+
148
+ # User-provided R script content
149
+ # This is where the user's custom ggtree logic should go.
150
+ # The user's script can use 'input_tree_file', 'input_annotation_file',
151
+ # 'output_image_name_param', 'image_width_param', 'image_height_param',
152
+ # 'units_val', 'dpi_val' if they wish to override or extend the default
153
+ # saving behavior.
154
+ {r_script_content}
155
+
156
+ # Attempt to save the last ggplot object 'p' if it exists and
157
+ # the user's script hasn't explicitly saved a plot with the default name.
158
+ # This provides a fallback for simple plotting tasks.
159
+ if (exists("p") && inherits(p, "ggplot")) {{
160
+ if (!file.exists(output_image_name_param)) {{ # Only save if user script didn't already save to this name
161
+ message(paste0("Saving ggplot object 'p' to ", output_image_name_param, " using default parameters."))
162
+ ggsave(output_image_name_param, plot = p, width = image_width_param, height = image_height_param, units = units_val, dpi = dpi_val)
163
+ }} else {{
164
+ message(paste0("File '", output_image_name_param, "' already exists. Skipping default ggsave."))
165
+ }}
166
+ }} else {{
167
+ message("No 'p' (ggplot object) found or saved by user script. No default image output will be generated.")
168
+ }}
169
+ """
170
+
171
+ r_script_file = work_dir / "run_ggtree.R"
172
+ r_script_file.write_text(r_code)
173
+
174
+ command = ["Rscript", r_script_file.as_posix()]
175
+
176
+ # Execute the R script
177
+ try:
178
+ process = subprocess.run(
179
+ command,
180
+ cwd=work_dir,
181
+ capture_output=True,
182
+ text=True,
183
+ check=True,
184
+ env=os.environ # Inherit environment to ensure R is found
185
+ )
186
+ stdout = process.stdout
187
+ stderr = process.stderr
188
+ except subprocess.CalledProcessError as e:
189
+ return {
190
+ "command_executed": " ".join(command),
191
+ "stdout": e.stdout,
192
+ "stderr": e.stderr,
193
+ "output_files": [],
194
+ "error": f"Rscript execution failed with exit code {e.returncode}",
195
+ }
196
+ except FileNotFoundError:
197
+ return {
198
+ "command_executed": " ".join(command),
199
+ "stdout": "",
200
+ "stderr": "Rscript command not found. Is R installed and in PATH?",
201
+ "output_files": [],
202
+ "error": "Rscript executable not found.",
203
+ }
204
+
205
+ # Collect output files
206
+ output_files = [f.as_posix() for f in work_dir.iterdir() if f.is_file() and f != r_script_file]
207
+
208
+ return {
209
+ "command_executed": " ".join(command),
210
+ "stdout": stdout,
211
+ "stderr": stderr,
212
+ "output_files": output_files,
213
+ }
214
+
215
+ if __name__ == "__main__":
216
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-ggtree/app/bioconductor-ggtree_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggtree/app/bioconductor-ggtree_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_ggtree'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-ggtree/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-ggtree/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-ggtree:
5
+ build: .
6
+ image: mcp-bioconductor-ggtree:latest
7
+ container_name: mcp-bioconductor-ggtree
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-ggtree
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+