czty commited on
Commit
ea975d9
·
verified ·
1 Parent(s): c9c564e

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_bcftools/Dockerfile +40 -0
  2. Biomni/mcp_generated/mcp_bcftools/app/__pycache__/bcftools_shim_server.cpython-311.pyc +0 -0
  3. Biomni/mcp_generated/mcp_bcftools/app/bcftools_server.py +866 -0
  4. Biomni/mcp_generated/mcp_bcftools/app/bcftools_shim_server.py +55 -0
  5. Biomni/mcp_generated/mcp_bcftools/docker-compose.yml +22 -0
  6. Biomni/mcp_generated/mcp_bcftools/environment.yaml +10 -0
  7. Biomni/mcp_generated/mcp_bcftools/requirements.txt +2 -0
  8. Biomni/mcp_generated/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py +297 -0
  9. Biomni/mcp_generated/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_shim_server.py +55 -0
  10. Biomni/mcp_generated/mcp_bioconductor-awaggregator/Dockerfile +40 -0
  11. Biomni/mcp_generated/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py +222 -0
  12. Biomni/mcp_generated/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_shim_server.py +55 -0
  13. Biomni/mcp_generated/mcp_bioconductor-awaggregator/app/requirements.txt +1 -0
  14. Biomni/mcp_generated/mcp_bioconductor-awaggregator/docker-compose.yml +22 -0
  15. Biomni/mcp_generated/mcp_bioconductor-awaggregator/environment.yaml +10 -0
  16. Biomni/mcp_generated/mcp_bioconductor-awaggregator/requirements.txt +2 -0
  17. Biomni/mcp_generated/mcp_bioconductor-deseq2/Dockerfile +40 -0
  18. Biomni/mcp_generated/mcp_bioconductor-deseq2/app/__pycache__/bioconductor-deseq2_server.cpython-311.pyc +0 -0
  19. Biomni/mcp_generated/mcp_bioconductor-deseq2/app/__pycache__/bioconductor-deseq2_shim_server.cpython-311.pyc +0 -0
  20. Biomni/mcp_generated/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py +299 -0
  21. Biomni/mcp_generated/mcp_bioconductor-deseq2/app/bioconductor-deseq2_shim_server.py +55 -0
  22. Biomni/mcp_generated/mcp_bioconductor-deseq2/app/requirements.txt +1 -0
  23. Biomni/mcp_generated/mcp_bioconductor-deseq2/docker-compose.yml +22 -0
  24. Biomni/mcp_generated/mcp_bioconductor-deseq2/environment.yaml +10 -0
  25. Biomni/mcp_generated/mcp_bioconductor-deseq2/requirements.txt +2 -0
  26. Biomni/mcp_generated/mcp_bioconductor-gosemsim/Dockerfile +40 -0
  27. Biomni/mcp_generated/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py +281 -0
  28. Biomni/mcp_generated/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_shim_server.py +55 -0
  29. Biomni/mcp_generated/mcp_bioconductor-gosemsim/app/requirements.txt +1 -0
  30. Biomni/mcp_generated/mcp_bioconductor-gosemsim/docker-compose.yml +22 -0
  31. Biomni/mcp_generated/mcp_bioconductor-gosemsim/environment.yaml +10 -0
  32. Biomni/mcp_generated/mcp_bioconductor-gosemsim/requirements.txt +2 -0
  33. Biomni/mcp_generated/mcp_bioconductor-infercnv/app/bioconductor-infercnv_shim_server.py +55 -0
  34. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/Dockerfile +40 -0
  35. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py +231 -0
  36. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_shim_server.py +55 -0
  37. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/app/requirements.txt +1 -0
  38. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/docker-compose.yml +22 -0
  39. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/environment.yaml +10 -0
  40. Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/requirements.txt +2 -0
  41. Biomni/mcp_generated/mcp_bioconductor-mia/Dockerfile +40 -0
  42. Biomni/mcp_generated/mcp_bioconductor-mia/app/bioconductor-mia_server.py +134 -0
  43. Biomni/mcp_generated/mcp_bioconductor-mia/app/bioconductor-mia_shim_server.py +55 -0
  44. Biomni/mcp_generated/mcp_bioconductor-mia/app/requirements.txt +1 -0
  45. Biomni/mcp_generated/mcp_bioconductor-mia/docker-compose.yml +22 -0
  46. Biomni/mcp_generated/mcp_bioconductor-mia/environment.yaml +10 -0
  47. Biomni/mcp_generated/mcp_bioconductor-mia/requirements.txt +2 -0
  48. Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/Dockerfile +40 -0
  49. Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py +380 -0
  50. Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_shim_server.py +55 -0
Biomni/mcp_generated/mcp_bcftools/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 bcftools via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bcftools -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 bcftools_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/bcftools_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/bcftools_server.py"]
40
+
Biomni/mcp_generated/mcp_bcftools/app/__pycache__/bcftools_shim_server.cpython-311.pyc ADDED
Binary file (3.55 kB). View file
 
Biomni/mcp_generated/mcp_bcftools/app/bcftools_server.py ADDED
@@ -0,0 +1,866 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import List, Optional
5
+
6
+ # Note: The @mcp.tool decorator is not defined here.
7
+ # It is assumed to be provided by the Model Context Protocol (MCP) framework.
8
+ # This code is designed to be used within that framework.
9
+
10
+ # Helper function to handle subprocess execution and error reporting
11
+ def _run_bcftools_command(cmd: List[str], output_file: Optional[Path] = None):
12
+ """
13
+ A helper function to execute a bcftools command, handle errors,
14
+ and return a structured dictionary.
15
+ """
16
+ command_str = " ".join(cmd)
17
+ try:
18
+ result = subprocess.run(
19
+ cmd,
20
+ capture_output=True,
21
+ text=True,
22
+ check=True,
23
+ )
24
+ output_files = [str(output_file)] if output_file and output_file.exists() else []
25
+
26
+ # For commands that create index files automatically
27
+ if output_file and str(output_file).endswith((".bcf", ".vcf.gz")):
28
+ index_extensions = [".csi", ".tbi"]
29
+ for ext in index_extensions:
30
+ index_file = Path(str(output_file) + ext)
31
+ if index_file.exists():
32
+ output_files.append(str(index_file))
33
+
34
+ return {
35
+ "command_executed": command_str,
36
+ "stdout": result.stdout,
37
+ "stderr": result.stderr,
38
+ "output_files": output_files,
39
+ "return_code": 0,
40
+ }
41
+ except FileNotFoundError:
42
+ return {
43
+ "command_executed": command_str,
44
+ "stdout": "",
45
+ "stderr": "Error: 'bcftools' command not found. Please ensure it is in your PATH.",
46
+ "output_files": [],
47
+ "return_code": 1,
48
+ "error": "FileNotFoundError"
49
+ }
50
+ except subprocess.CalledProcessError as e:
51
+ return {
52
+ "command_executed": command_str,
53
+ "stdout": e.stdout,
54
+ "stderr": e.stderr,
55
+ "output_files": [],
56
+ "return_code": e.returncode,
57
+ "error": "CalledProcessError"
58
+ }
59
+
60
+ from mcp.server.fastmcp import FastMCP
61
+
62
+ SERVER_NAME = 'local_bcftools'
63
+ mcp = FastMCP(SERVER_NAME)
64
+
65
+ @mcp.tool()
66
+ def bcftools_annotate(
67
+ input_file: Path,
68
+ annotations: Optional[Path] = None,
69
+ columns: Optional[str] = None,
70
+ header_lines: Optional[Path] = None,
71
+ output: Optional[Path] = None,
72
+ output_type: str = "v",
73
+ remove: Optional[str] = None,
74
+ set_id: Optional[str] = None,
75
+ regions: Optional[str] = None,
76
+ regions_file: Optional[Path] = None,
77
+ targets: Optional[str] = None,
78
+ targets_file: Optional[Path] = None,
79
+ include: Optional[str] = None,
80
+ exclude: Optional[str] = None,
81
+ samples: Optional[str] = None,
82
+ samples_file: Optional[Path] = None,
83
+ threads: int = 1,
84
+ rename_chrs: Optional[Path] = None,
85
+ no_version: bool = False,
86
+ single_overlaps: bool = False,
87
+ mark_sites: Optional[str] = None,
88
+ ):
89
+ """
90
+ Annotate VCF/BCF files with information from other files.
91
+ """
92
+ if not input_file.exists():
93
+ raise FileNotFoundError(f"Input file not found: {input_file}")
94
+ if annotations and not annotations.exists():
95
+ raise FileNotFoundError(f"Annotations file not found: {annotations}")
96
+ if header_lines and not header_lines.exists():
97
+ raise FileNotFoundError(f"Header lines file not found: {header_lines}")
98
+ if output_type not in ["b", "u", "z", "v"]:
99
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
100
+
101
+ cmd = ["bcftools", "annotate", str(input_file)]
102
+
103
+ if annotations:
104
+ cmd.extend(["-a", str(annotations)])
105
+ if columns:
106
+ cmd.extend(["-c", columns])
107
+ if header_lines:
108
+ cmd.extend(["-h", str(header_lines)])
109
+ if output:
110
+ cmd.extend(["-o", str(output)])
111
+ if output_type:
112
+ cmd.extend(["-O", output_type])
113
+ if remove:
114
+ cmd.extend(["-x", remove])
115
+ if set_id:
116
+ cmd.extend(["--set-id", set_id])
117
+ if regions:
118
+ cmd.extend(["-r", regions])
119
+ if regions_file:
120
+ cmd.extend(["-R", str(regions_file)])
121
+ if targets:
122
+ cmd.extend(["-t", targets])
123
+ if targets_file:
124
+ cmd.extend(["-T", str(targets_file)])
125
+ if include:
126
+ cmd.extend(["-i", include])
127
+ if exclude:
128
+ cmd.extend(["-e", exclude])
129
+ if samples:
130
+ cmd.extend(["-s", samples])
131
+ if samples_file:
132
+ cmd.extend(["-S", str(samples_file)])
133
+ if threads > 1:
134
+ cmd.extend(["--threads", str(threads)])
135
+ if rename_chrs:
136
+ cmd.extend(["--rename-chrs", str(rename_chrs)])
137
+ if no_version:
138
+ cmd.append("--no-version")
139
+ if single_overlaps:
140
+ cmd.append("--single-overlaps")
141
+ if mark_sites:
142
+ cmd.extend(["--mark-sites", mark_sites])
143
+
144
+ return _run_bcftools_command(cmd, output)
145
+
146
+ @mcp.tool()
147
+ def bcftools_call(
148
+ input_file: Path,
149
+ output: Optional[Path] = None,
150
+ output_type: str = "v",
151
+ ploidy: Optional[str] = None,
152
+ ploidy_file: Optional[Path] = None,
153
+ call_method: str = "multiallelic",
154
+ constrain_alleles: bool = False,
155
+ group_samples: Optional[str] = None,
156
+ samples: Optional[str] = None,
157
+ samples_file: Optional[Path] = None,
158
+ format_fields: Optional[str] = None,
159
+ keep_alts: bool = False,
160
+ keep_masked_ref: bool = False,
161
+ skip_variants: bool = False,
162
+ threads: int = 1,
163
+ ):
164
+ """
165
+ Call genotypes from likelihoods.
166
+ """
167
+ if not input_file.exists():
168
+ raise FileNotFoundError(f"Input file not found: {input_file}")
169
+ if output_type not in ["b", "u", "z", "v"]:
170
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
171
+ if call_method not in ["consensus", "multiallelic"]:
172
+ raise ValueError("call_method must be 'consensus' or 'multiallelic'.")
173
+
174
+ cmd = ["bcftools", "call", str(input_file)]
175
+
176
+ if output:
177
+ cmd.extend(["-o", str(output)])
178
+ if output_type:
179
+ cmd.extend(["-O", output_type])
180
+ if ploidy:
181
+ cmd.extend(["-p", ploidy])
182
+ if ploidy_file:
183
+ cmd.extend(["-P", str(ploidy_file)])
184
+ if call_method == "consensus":
185
+ cmd.append("-c")
186
+ else: # multiallelic is default, but -m is explicit
187
+ cmd.append("-m")
188
+ if constrain_alleles:
189
+ cmd.append("-C alleles")
190
+ if group_samples:
191
+ cmd.extend(["-G", group_samples])
192
+ if samples:
193
+ cmd.extend(["-s", samples])
194
+ if samples_file:
195
+ cmd.extend(["-S", str(samples_file)])
196
+ if format_fields:
197
+ cmd.extend(["-f", format_fields])
198
+ if keep_alts:
199
+ cmd.append("-A")
200
+ if keep_masked_ref:
201
+ cmd.append("-M")
202
+ if skip_variants:
203
+ cmd.append("-V")
204
+ if threads > 1:
205
+ cmd.extend(["--threads", str(threads)])
206
+
207
+ return _run_bcftools_command(cmd, output)
208
+
209
+ @mcp.tool()
210
+ def bcftools_concat(
211
+ input_files: List[Path],
212
+ output: Optional[Path] = None,
213
+ output_type: str = "v",
214
+ allow_overlaps: bool = False,
215
+ remove_duplicates: bool = False,
216
+ naive: bool = False,
217
+ threads: int = 1,
218
+ ):
219
+ """
220
+ Concatenate VCF/BCF files from non-overlapping regions.
221
+ """
222
+ if not input_files:
223
+ raise ValueError("At least one input file must be provided.")
224
+ for f in input_files:
225
+ if not f.exists():
226
+ raise FileNotFoundError(f"Input file not found: {f}")
227
+ if output_type not in ["b", "u", "z", "v"]:
228
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
229
+
230
+ cmd = ["bcftools", "concat"]
231
+
232
+ if allow_overlaps:
233
+ cmd.append("-a")
234
+ if remove_duplicates:
235
+ cmd.append("-d")
236
+ if naive:
237
+ cmd.append("-n")
238
+ if output:
239
+ cmd.extend(["-o", str(output)])
240
+ if output_type:
241
+ cmd.extend(["-O", output_type])
242
+ if threads > 1:
243
+ cmd.extend(["--threads", str(threads)])
244
+
245
+ cmd.extend([str(f) for f in input_files])
246
+
247
+ return _run_bcftools_command(cmd, output)
248
+
249
+ @mcp.tool()
250
+ def bcftools_consensus(
251
+ input_file: Path,
252
+ fasta_ref: Path,
253
+ output: Optional[Path] = None,
254
+ chain: Optional[Path] = None,
255
+ haplotype: Optional[str] = None,
256
+ iupac_codes: bool = False,
257
+ mark_del: Optional[str] = None,
258
+ mark_ins: Optional[str] = None,
259
+ mark_snv: Optional[str] = None,
260
+ missing: Optional[str] = None,
261
+ sample: Optional[str] = None,
262
+ include: Optional[str] = None,
263
+ exclude: Optional[str] = None,
264
+ ):
265
+ """
266
+ Create a consensus sequence by applying VCF variants to a reference FASTA file.
267
+ """
268
+ if not input_file.exists():
269
+ raise FileNotFoundError(f"Input VCF/BCF file not found: {input_file}")
270
+ if not fasta_ref.exists():
271
+ raise FileNotFoundError(f"Reference FASTA file not found: {fasta_ref}")
272
+ if haplotype and haplotype not in ["1", "2", "R", "A", "IUPAC", "all"]:
273
+ raise ValueError("haplotype must be one of '1', '2', 'R', 'A', 'IUPAC', 'all'.")
274
+
275
+ cmd = ["bcftools", "consensus", str(input_file)]
276
+ cmd.extend(["-f", str(fasta_ref)])
277
+
278
+ if output:
279
+ cmd.extend(["-o", str(output)])
280
+ if chain:
281
+ cmd.extend(["-c", str(chain)])
282
+ if haplotype:
283
+ cmd.extend(["-H", haplotype])
284
+ if iupac_codes:
285
+ cmd.append("-i")
286
+ if mark_del:
287
+ cmd.extend(["--mark-del", mark_del])
288
+ if mark_ins:
289
+ cmd.extend(["--mark-ins", mark_ins])
290
+ if mark_snv:
291
+ cmd.extend(["--mark-snv", mark_snv])
292
+ if missing:
293
+ cmd.extend(["-m", missing])
294
+ if sample:
295
+ cmd.extend(["-s", sample])
296
+ if include:
297
+ cmd.extend(["--include", include])
298
+ if exclude:
299
+ cmd.extend(["--exclude", exclude])
300
+
301
+ return _run_bcftools_command(cmd, output)
302
+
303
+ @mcp.tool()
304
+ def bcftools_filter(
305
+ input_file: Path,
306
+ output: Optional[Path] = None,
307
+ output_type: str = "v",
308
+ include: Optional[str] = None,
309
+ exclude: Optional[str] = None,
310
+ soft_filter: Optional[str] = None,
311
+ set_gt: Optional[str] = None,
312
+ mode: str = "+",
313
+ threads: int = 1,
314
+ ):
315
+ """
316
+ Apply filters to VCF/BCF files.
317
+ """
318
+ if not input_file.exists():
319
+ raise FileNotFoundError(f"Input file not found: {input_file}")
320
+ if output_type not in ["b", "u", "z", "v"]:
321
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
322
+ if mode not in ["+", "x", "X"]:
323
+ raise ValueError("mode must be one of '+', 'x', 'X'.")
324
+
325
+ cmd = ["bcftools", "filter", str(input_file)]
326
+
327
+ if output:
328
+ cmd.extend(["-o", str(output)])
329
+ if output_type:
330
+ cmd.extend(["-O", output_type])
331
+ if include:
332
+ cmd.extend(["-i", include])
333
+ if exclude:
334
+ cmd.extend(["-e", exclude])
335
+ if soft_filter:
336
+ cmd.extend(["-s", soft_filter])
337
+ if set_gt:
338
+ cmd.extend(["-S", set_gt])
339
+ if mode != "+":
340
+ cmd.extend(["-m", mode])
341
+ if threads > 1:
342
+ cmd.extend(["--threads", str(threads)])
343
+
344
+ return _run_bcftools_command(cmd, output)
345
+
346
+ @mcp.tool()
347
+ def bcftools_index(
348
+ input_file: Path,
349
+ force: bool = False,
350
+ stats: bool = False,
351
+ threads: int = 1,
352
+ csi: bool = False,
353
+ tbi: bool = False,
354
+ min_csi_shift: int = 14,
355
+ ):
356
+ """
357
+ Index VCF/BCF files for fast random access.
358
+ """
359
+ if not input_file.exists():
360
+ raise FileNotFoundError(f"Input file not found: {input_file}")
361
+ if csi and tbi:
362
+ raise ValueError("Cannot specify both --csi (-c) and --tbi (-t).")
363
+
364
+ cmd = ["bcftools", "index"]
365
+
366
+ if force:
367
+ cmd.append("-f")
368
+ if stats:
369
+ cmd.append("-n")
370
+ if threads > 1:
371
+ cmd.extend(["--threads", str(threads)])
372
+ if csi:
373
+ cmd.append("-c")
374
+ if tbi:
375
+ cmd.append("-t")
376
+ if csi and min_csi_shift != 14:
377
+ cmd.extend(["-m", str(min_csi_shift)])
378
+
379
+ cmd.append(str(input_file))
380
+
381
+ # The output file is the index file itself
382
+ output_idx_file = None
383
+ if csi:
384
+ output_idx_file = Path(str(input_file) + ".csi")
385
+ else: # default is tbi for vcf.gz, csi for bcf
386
+ if str(input_file).endswith(".bcf"):
387
+ output_idx_file = Path(str(input_file) + ".csi")
388
+ else:
389
+ output_idx_file = Path(str(input_file) + ".tbi")
390
+
391
+ return _run_bcftools_command(cmd, output_idx_file)
392
+
393
+ @mcp.tool()
394
+ def bcftools_isec(
395
+ input_files: List[Path],
396
+ output_dir: Path,
397
+ output_type: str = "v",
398
+ complement: bool = False,
399
+ collapse: Optional[str] = None,
400
+ exclude: Optional[str] = None,
401
+ include: Optional[str] = None,
402
+ n_sites: Optional[str] = None,
403
+ regions: Optional[str] = None,
404
+ regions_file: Optional[Path] = None,
405
+ targets: Optional[str] = None,
406
+ targets_file: Optional[Path] = None,
407
+ threads: int = 1,
408
+ ):
409
+ """
410
+ Create intersections, unions, and complements of VCF/BCF files.
411
+ """
412
+ if len(input_files) < 2:
413
+ raise ValueError("At least two input files must be provided for isec.")
414
+ for f in input_files:
415
+ if not f.exists():
416
+ raise FileNotFoundError(f"Input file not found: {f}")
417
+ if output_type not in ["b", "u", "z", "v"]:
418
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
419
+ if collapse and collapse not in ["none", "all", "snps", "indels", "both", "any"]:
420
+ raise ValueError("Invalid value for collapse.")
421
+
422
+ output_dir.mkdir(parents=True, exist_ok=True)
423
+
424
+ cmd = ["bcftools", "isec"]
425
+ cmd.extend(["-p", str(output_dir)])
426
+ cmd.extend(["-O", output_type])
427
+
428
+ if complement:
429
+ cmd.append("-c")
430
+ if collapse:
431
+ cmd.extend(["--collapse", collapse])
432
+ if exclude:
433
+ cmd.extend(["-e", exclude])
434
+ if include:
435
+ cmd.extend(["-i", include])
436
+ if n_sites:
437
+ cmd.extend(["-n", n_sites])
438
+ if regions:
439
+ cmd.extend(["-r", regions])
440
+ if regions_file:
441
+ cmd.extend(["-R", str(regions_file)])
442
+ if targets:
443
+ cmd.extend(["-t", targets])
444
+ if targets_file:
445
+ cmd.extend(["-T", str(targets_file)])
446
+ if threads > 1:
447
+ cmd.extend(["--threads", str(threads)])
448
+
449
+ cmd.extend([str(f) for f in input_files])
450
+
451
+ # isec doesn't have a single output file, it creates a directory
452
+ # We will return the directory path in a structured way if needed,
453
+ # but for now, we'll rely on the user knowing the output is a directory.
454
+ # The _run_bcftools_command helper won't find a single output file.
455
+ result = _run_bcftools_command(cmd)
456
+
457
+ # Manually list created files
458
+ if result["return_code"] == 0:
459
+ created_files = [str(p) for p in output_dir.glob('*')]
460
+ result["output_files"] = created_files
461
+
462
+ return result
463
+
464
+ @mcp.tool()
465
+ def bcftools_merge(
466
+ input_files: List[Path],
467
+ output: Optional[Path] = None,
468
+ output_type: str = "v",
469
+ info_rules: Optional[str] = None,
470
+ merge_logic: Optional[str] = None,
471
+ threads: int = 1,
472
+ ):
473
+ """
474
+ Merge multiple VCF/BCF files from different samples into a single file.
475
+ """
476
+ if not input_files:
477
+ raise ValueError("At least one input file must be provided.")
478
+ for f in input_files:
479
+ if not f.exists():
480
+ raise FileNotFoundError(f"Input file not found: {f}")
481
+ if output_type not in ["b", "u", "z", "v"]:
482
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
483
+
484
+ cmd = ["bcftools", "merge"]
485
+
486
+ if output:
487
+ cmd.extend(["-o", str(output)])
488
+ if output_type:
489
+ cmd.extend(["-O", output_type])
490
+ if info_rules:
491
+ cmd.extend(["--info-rules", info_rules])
492
+ if merge_logic:
493
+ cmd.extend(["-m", merge_logic])
494
+ if threads > 1:
495
+ cmd.extend(["--threads", str(threads)])
496
+
497
+ cmd.extend([str(f) for f in input_files])
498
+
499
+ return _run_bcftools_command(cmd, output)
500
+
501
+ @mcp.tool()
502
+ def bcftools_mpileup(
503
+ input_bams: List[Path],
504
+ fasta_ref: Path,
505
+ output: Optional[Path] = None,
506
+ output_type: str = "v",
507
+ regions: Optional[str] = None,
508
+ regions_file: Optional[Path] = None,
509
+ annotate: Optional[str] = None,
510
+ max_depth: int = 250,
511
+ min_base_quality: int = 13,
512
+ min_mapping_quality: int = 0,
513
+ adjust_mq: int = 50,
514
+ no_baq: bool = False,
515
+ threads: int = 1,
516
+ ):
517
+ """
518
+ Generate VCF/BCF from alignment files (BAM/CRAM).
519
+ """
520
+ if not input_bams:
521
+ raise ValueError("At least one input BAM/CRAM file must be provided.")
522
+ for f in input_bams:
523
+ if not f.exists():
524
+ raise FileNotFoundError(f"Input alignment file not found: {f}")
525
+ if not fasta_ref.exists():
526
+ raise FileNotFoundError(f"Reference FASTA file not found: {fasta_ref}")
527
+ if output_type not in ["b", "u", "z", "v"]:
528
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
529
+
530
+ cmd = ["bcftools", "mpileup"]
531
+ cmd.extend(["-f", str(fasta_ref)])
532
+
533
+ if output:
534
+ cmd.extend(["-o", str(output)])
535
+ if output_type:
536
+ cmd.extend(["-O", output_type])
537
+ if regions:
538
+ cmd.extend(["-r", regions])
539
+ if regions_file:
540
+ cmd.extend(["-R", str(regions_file)])
541
+ if annotate:
542
+ cmd.extend(["-a", annotate])
543
+ if max_depth != 250:
544
+ cmd.extend(["-d", str(max_depth)])
545
+ if min_base_quality != 13:
546
+ cmd.extend(["-q", str(min_base_quality)])
547
+ if min_mapping_quality != 0:
548
+ cmd.extend(["-Q", str(min_mapping_quality)])
549
+ if adjust_mq != 50:
550
+ cmd.extend(["-C", str(adjust_mq)])
551
+ if no_baq:
552
+ cmd.append("-B")
553
+ if threads > 1:
554
+ cmd.extend(["--threads", str(threads)])
555
+
556
+ cmd.extend([str(f) for f in input_bams])
557
+
558
+ return _run_bcftools_command(cmd, output)
559
+
560
+ @mcp.tool()
561
+ def bcftools_norm(
562
+ input_file: Path,
563
+ fasta_ref: Path,
564
+ output: Optional[Path] = None,
565
+ output_type: str = "v",
566
+ check_ref: str = "w",
567
+ multiallelics: Optional[str] = None,
568
+ atomize: bool = False,
569
+ rm_dup: Optional[str] = None,
570
+ threads: int = 1,
571
+ ):
572
+ """
573
+ Left-align and normalize indels, check REF, split multiallelic sites.
574
+ """
575
+ if not input_file.exists():
576
+ raise FileNotFoundError(f"Input file not found: {input_file}")
577
+ if not fasta_ref.exists():
578
+ raise FileNotFoundError(f"Reference FASTA file not found: {fasta_ref}")
579
+ if output_type not in ["b", "u", "z", "v"]:
580
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
581
+ if check_ref not in ["w", "e", "x", "s"]:
582
+ raise ValueError("check_ref must be one of 'w', 'e', 'x', 's'.")
583
+
584
+ cmd = ["bcftools", "norm", str(input_file)]
585
+ cmd.extend(["-f", str(fasta_ref)])
586
+
587
+ if output:
588
+ cmd.extend(["-o", str(output)])
589
+ if output_type:
590
+ cmd.extend(["-O", output_type])
591
+ if check_ref != "w":
592
+ cmd.extend(["-c", check_ref])
593
+ if multiallelics:
594
+ cmd.extend(["-m", multiallelics])
595
+ if atomize:
596
+ cmd.append("-a")
597
+ if rm_dup:
598
+ cmd.extend(["-d", rm_dup])
599
+ if threads > 1:
600
+ cmd.extend(["--threads", str(threads)])
601
+
602
+ return _run_bcftools_command(cmd, output)
603
+
604
+ @mcp.tool()
605
+ def bcftools_query(
606
+ input_file: Path,
607
+ format_string: str,
608
+ output: Optional[Path] = None,
609
+ include: Optional[str] = None,
610
+ exclude: Optional[str] = None,
611
+ regions: Optional[str] = None,
612
+ regions_file: Optional[Path] = None,
613
+ samples: Optional[str] = None,
614
+ samples_file: Optional[Path] = None,
615
+ list_samples: bool = False,
616
+ ):
617
+ """
618
+ Extracts fields from VCF/BCF files and prints them in a user-defined format.
619
+ """
620
+ if not input_file.exists():
621
+ raise FileNotFoundError(f"Input file not found: {input_file}")
622
+
623
+ cmd = ["bcftools", "query", str(input_file)]
624
+
625
+ if list_samples:
626
+ cmd.append("-l")
627
+ else:
628
+ cmd.extend(["-f", format_string])
629
+
630
+ if output:
631
+ cmd.extend(["-o", str(output)])
632
+ if include:
633
+ cmd.extend(["-i", include])
634
+ if exclude:
635
+ cmd.extend(["-e", exclude])
636
+ if regions:
637
+ cmd.extend(["-r", regions])
638
+ if regions_file:
639
+ cmd.extend(["-R", str(regions_file)])
640
+ if samples:
641
+ cmd.extend(["-s", samples])
642
+ if samples_file:
643
+ cmd.extend(["-S", str(samples_file)])
644
+
645
+ return _run_bcftools_command(cmd, output)
646
+
647
+ @mcp.tool()
648
+ def bcftools_reheader(
649
+ input_file: Path,
650
+ header: Optional[Path] = None,
651
+ samples: Optional[Path] = None,
652
+ output: Optional[Path] = None,
653
+ output_type: str = "v",
654
+ ):
655
+ """
656
+ Modify VCF/BCF header, e.g., rename samples.
657
+ """
658
+ if not input_file.exists():
659
+ raise FileNotFoundError(f"Input file not found: {input_file}")
660
+ if header and not header.exists():
661
+ raise FileNotFoundError(f"Header file not found: {header}")
662
+ if samples and not samples.exists():
663
+ raise FileNotFoundError(f"Samples file not found: {samples}")
664
+ if not header and not samples:
665
+ raise ValueError("Either 'header' or 'samples' file must be provided.")
666
+ if output_type not in ["b", "u", "z", "v"]:
667
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
668
+
669
+ cmd = ["bcftools", "reheader"]
670
+
671
+ if header:
672
+ cmd.extend(["-h", str(header)])
673
+ if samples:
674
+ cmd.extend(["-s", str(samples)])
675
+ if output:
676
+ cmd.extend(["-o", str(output)])
677
+
678
+ cmd.append(str(input_file))
679
+
680
+ # Reheader does not support -O, output type is inferred from extension
681
+ return _run_bcftools_command(cmd, output)
682
+
683
+ @mcp.tool()
684
+ def bcftools_sort(
685
+ input_file: Path,
686
+ output: Optional[Path] = None,
687
+ output_type: str = "v",
688
+ max_mem: str = "768M",
689
+ temp_dir: Optional[Path] = None,
690
+ ):
691
+ """
692
+ Sort VCF/BCF file by chromosome and position.
693
+ """
694
+ if not input_file.exists():
695
+ raise FileNotFoundError(f"Input file not found: {input_file}")
696
+ if output_type not in ["b", "u", "z", "v"]:
697
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
698
+
699
+ cmd = ["bcftools", "sort", str(input_file)]
700
+
701
+ if output:
702
+ cmd.extend(["-o", str(output)])
703
+ if output_type:
704
+ cmd.extend(["-O", output_type])
705
+ if max_mem:
706
+ cmd.extend(["-m", max_mem])
707
+ if temp_dir:
708
+ temp_dir.mkdir(exist_ok=True, parents=True)
709
+ cmd.extend(["-T", str(temp_dir)])
710
+
711
+ return _run_bcftools_command(cmd, output)
712
+
713
+ @mcp.tool()
714
+ def bcftools_stats(
715
+ input_files: List[Path],
716
+ fasta_ref: Optional[Path] = None,
717
+ output_dir: Optional[Path] = None,
718
+ regions: Optional[str] = None,
719
+ regions_file: Optional[Path] = None,
720
+ samples: Optional[str] = None,
721
+ samples_file: Optional[Path] = None,
722
+ threads: int = 1,
723
+ ):
724
+ """
725
+ Produce VCF/BCF stats, create plots with plot-vcfstats.
726
+ """
727
+ if not input_files:
728
+ raise ValueError("At least one input file must be provided.")
729
+ for f in input_files:
730
+ if not f.exists():
731
+ raise FileNotFoundError(f"Input file not found: {f}")
732
+ if fasta_ref and not fasta_ref.exists():
733
+ raise FileNotFoundError(f"Reference FASTA file not found: {fasta_ref}")
734
+
735
+ cmd = ["bcftools", "stats"]
736
+
737
+ if fasta_ref:
738
+ cmd.extend(["-f", str(fasta_ref)])
739
+ if regions:
740
+ cmd.extend(["-r", regions])
741
+ if regions_file:
742
+ cmd.extend(["-R", str(regions_file)])
743
+ if samples:
744
+ cmd.extend(["-s", samples])
745
+ if samples_file:
746
+ cmd.extend(["-S", str(samples_file)])
747
+ if threads > 1:
748
+ cmd.extend(["--threads", str(threads)])
749
+
750
+ cmd.extend([str(f) for f in input_files])
751
+
752
+ # stats command outputs to stdout by default.
753
+ # If output_dir is provided, we redirect stdout to a file.
754
+ output_file = None
755
+ if output_dir:
756
+ output_dir.mkdir(exist_ok=True, parents=True)
757
+ output_file = output_dir / "stats.txt"
758
+ with open(output_file, "w") as f_out:
759
+ command_str = " ".join(cmd)
760
+ try:
761
+ result = subprocess.run(
762
+ cmd,
763
+ stdout=f_out,
764
+ stderr=subprocess.PIPE,
765
+ text=True,
766
+ check=True,
767
+ )
768
+ return {
769
+ "command_executed": command_str,
770
+ "stdout": f"Stats written to {output_file}",
771
+ "stderr": result.stderr,
772
+ "output_files": [str(output_file)],
773
+ "return_code": 0,
774
+ }
775
+ except subprocess.CalledProcessError as e:
776
+ return {
777
+ "command_executed": command_str,
778
+ "stdout": e.stdout or "",
779
+ "stderr": e.stderr,
780
+ "output_files": [],
781
+ "return_code": e.returncode,
782
+ "error": "CalledProcessError"
783
+ }
784
+ else:
785
+ # No output file, capture stdout directly
786
+ return _run_bcftools_command(cmd)
787
+
788
+ @mcp.tool()
789
+ def bcftools_view(
790
+ input_file: Path,
791
+ output: Optional[Path] = None,
792
+ output_type: str = "v",
793
+ header_only: bool = False,
794
+ no_header: bool = False,
795
+ drop_genotypes: bool = False,
796
+ trim_alt_alleles: bool = False,
797
+ min_alleles: Optional[int] = None,
798
+ max_alleles: Optional[int] = None,
799
+ min_ac: Optional[int] = None,
800
+ max_ac: Optional[int] = None,
801
+ min_af: Optional[float] = None,
802
+ max_af: Optional[float] = None,
803
+ types: Optional[str] = None,
804
+ include: Optional[str] = None,
805
+ exclude: Optional[str] = None,
806
+ regions: Optional[str] = None,
807
+ regions_file: Optional[Path] = None,
808
+ samples: Optional[str] = None,
809
+ samples_file: Optional[Path] = None,
810
+ threads: int = 1,
811
+ ):
812
+ """
813
+ Subset, filter, and convert VCF and BCF files.
814
+ """
815
+ if not input_file.exists():
816
+ raise FileNotFoundError(f"Input file not found: {input_file}")
817
+ if output_type not in ["b", "u", "z", "v"]:
818
+ raise ValueError("output_type must be one of 'b', 'u', 'z', 'v'.")
819
+
820
+ cmd = ["bcftools", "view", str(input_file)]
821
+
822
+ if output:
823
+ cmd.extend(["-o", str(output)])
824
+ if output_type:
825
+ cmd.extend(["-O", output_type])
826
+ if header_only:
827
+ cmd.append("-h")
828
+ if no_header:
829
+ cmd.append("-H")
830
+ if drop_genotypes:
831
+ cmd.append("-G")
832
+ if trim_alt_alleles:
833
+ cmd.append("-a")
834
+ if min_alleles is not None:
835
+ cmd.extend(["-m", str(min_alleles)])
836
+ if max_alleles is not None:
837
+ cmd.extend(["-M", str(max_alleles)])
838
+ if min_ac is not None:
839
+ cmd.extend(["-c", str(min_ac)])
840
+ if max_ac is not None:
841
+ cmd.extend(["-C", str(max_ac)])
842
+ if min_af is not None:
843
+ cmd.extend(["-q", str(min_af)])
844
+ if max_af is not None:
845
+ cmd.extend(["-Q", str(max_af)])
846
+ if types:
847
+ cmd.extend(["-v", types])
848
+ if include:
849
+ cmd.extend(["-i", include])
850
+ if exclude:
851
+ cmd.extend(["-e", exclude])
852
+ if regions:
853
+ cmd.extend(["-r", regions])
854
+ if regions_file:
855
+ cmd.extend(["-R", str(regions_file)])
856
+ if samples:
857
+ cmd.extend(["-s", samples])
858
+ if samples_file:
859
+ cmd.extend(["-S", str(samples_file)])
860
+ if threads > 1:
861
+ cmd.extend(["--threads", str(threads)])
862
+
863
+ return _run_bcftools_command(cmd, output)
864
+
865
+ if __name__ == "__main__":
866
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bcftools/app/bcftools_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_bcftools/app/bcftools_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bcftools'
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_bcftools/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bcftools:
5
+ build: .
6
+ image: mcp-bcftools:latest
7
+ container_name: mcp-bcftools
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bcftools
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_bcftools/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bcftools
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bcftools/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, List, Dict, Any
6
+
7
+ # No need to import mcp as per instructions.
8
+
9
+ def _run_r_script(script_content: str, cwd: Path) -> Dict[str, Any]:
10
+ """
11
+ Executes an R script and captures its output.
12
+
13
+ Args:
14
+ script_content: The R script as a string.
15
+ cwd: The current working directory for the R script execution.
16
+
17
+ Returns:
18
+ A dictionary containing execution details, including command, stdout, stderr,
19
+ and an error message if the script fails.
20
+ """
21
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file:
22
+ r_script_file.write(script_content)
23
+ r_script_path = Path(r_script_file.name)
24
+
25
+ command = ["Rscript", str(r_script_path)]
26
+
27
+ stdout = ""
28
+ stderr = ""
29
+ try:
30
+ process = subprocess.run(
31
+ command,
32
+ capture_output=True,
33
+ text=True,
34
+ check=True,
35
+ cwd=cwd # Run R script in the specified working directory
36
+ )
37
+ stdout = process.stdout
38
+ stderr = process.stderr
39
+ except subprocess.CalledProcessError as e:
40
+ return {
41
+ "command_executed": " ".join(command),
42
+ "stdout": e.stdout,
43
+ "stderr": e.stderr,
44
+ "error": f"R script execution failed with exit code {e.returncode}",
45
+ "output_files": [],
46
+ }
47
+ finally:
48
+ os.remove(r_script_path)
49
+
50
+ return {
51
+ "command_executed": " ".join(command),
52
+ "stdout": stdout,
53
+ "stderr": stderr,
54
+ "output_files": [], # This will be populated by the calling function
55
+ }
56
+
57
+ from mcp.server.fastmcp import FastMCP
58
+
59
+ SERVER_NAME = 'local_bioconductor_alabaster_spatial'
60
+ mcp = FastMCP(SERVER_NAME)
61
+
62
+ @mcp.tool()
63
+ def save_spatial_experiment(
64
+ input_spatial_experiment_rds: Path,
65
+ output_directory: Path,
66
+ precomputed: bool = True,
67
+ deferred: bool = False,
68
+ compression_level: Optional[int] = None,
69
+ compression_filter: Optional[str] = None,
70
+ ) -> Dict[str, Any]:
71
+ """
72
+ Saves a SpatialExperiment object and its images into file artifacts.
73
+
74
+ This function serializes an R SpatialExperiment object (provided as an RDS file)
75
+ into a directory structure of file artifacts, offering a portable alternative
76
+ to direct RDS serialization. The `alabaster.spatial` package handles the
77
+ conversion and storage of the object and its associated images.
78
+
79
+ Args:
80
+ input_spatial_experiment_rds: Path to an RDS file containing the SpatialExperiment object.
81
+ This file must exist.
82
+ output_directory: Path to the directory where the file artifacts will be saved.
83
+ This directory will be created if it does not exist.
84
+ precomputed: Logical, whether to save precomputed results. Defaults to TRUE.
85
+ deferred: Logical, whether to defer saving of certain components. Defaults to FALSE.
86
+ compression_level: Integer, compression level for HDF5 files (0-9).
87
+ If None, R's default (typically 6) will be used.
88
+ compression_filter: String, compression filter for HDF5 files (e.g., "gzip", "lzf").
89
+ If None, no specific filter is applied.
90
+
91
+ Returns:
92
+ A dictionary containing execution details, including the command executed,
93
+ stdout, stderr, and a list of generated output files/directories.
94
+ Returns an error if input file is not found, output path is invalid,
95
+ compression_level is out of range, or R script execution fails.
96
+ """
97
+ # Input validation
98
+ if not input_spatial_experiment_rds.is_file():
99
+ return {
100
+ "command_executed": "",
101
+ "stdout": "",
102
+ "stderr": f"Error: Input RDS file not found at '{input_spatial_experiment_rds}'",
103
+ "output_files": [],
104
+ }
105
+
106
+ if output_directory.exists() and not output_directory.is_dir():
107
+ return {
108
+ "command_executed": "",
109
+ "stdout": "",
110
+ "stderr": f"Error: Output path '{output_directory}' exists but is not a directory.",
111
+ "output_files": [],
112
+ }
113
+
114
+ try:
115
+ output_directory.mkdir(parents=True, exist_ok=True)
116
+ except OSError as e:
117
+ return {
118
+ "command_executed": "",
119
+ "stdout": "",
120
+ "stderr": f"Error: Could not create output directory '{output_directory}': {e}",
121
+ "output_files": [],
122
+ }
123
+
124
+ if compression_level is not None and not (0 <= compression_level <= 9):
125
+ return {
126
+ "command_executed": "",
127
+ "stdout": "",
128
+ "stderr": f"Error: 'compression_level' must be an integer between 0 and 9, got {compression_level}",
129
+ "output_files": [],
130
+ }
131
+
132
+ # Construct R script
133
+ r_script_template = """
134
+ suppressPackageStartupMessages({{
135
+ library(alabaster.spatial)
136
+ library(SpatialExperiment)
137
+ library(S4Vectors)
138
+ library(alabaster.base)
139
+ library(rhdf5) # Explicitly load for HDF5 operations
140
+ }})
141
+
142
+ input_rds_path <- "{input_rds_path}"
143
+ output_dir_path <- "{output_dir_path}"
144
+
145
+ # Load the SpatialExperiment object
146
+ if (!file.exists(input_rds_path)) {{
147
+ stop(paste0("Input RDS file not found: ", input_rds_path))
148
+ }}
149
+ se_object <- readRDS(input_rds_path)
150
+
151
+ # Ensure it's a SpatialExperiment object
152
+ if (!is(se_object, "SpatialExperiment")) {{
153
+ stop("Loaded object is not a SpatialExperiment object.")
154
+ }}
155
+
156
+ # Prepare additional arguments for saveObject
157
+ save_args <- list(
158
+ x = se_object,
159
+ path = output_dir_path,
160
+ precomputed = {precomputed_r},
161
+ deferred = {deferred_r}
162
+ )
163
+ {compression_level_r}
164
+ {compression_filter_r}
165
+
166
+ # Call saveObject
167
+ do.call(alabaster.spatial::saveObject, save_args)
168
+
169
+ message(paste0("SpatialExperiment object saved to: ", output_dir_path))
170
+ """
171
+
172
+ compression_level_r = f"save_args$compression_level <- {compression_level}" if compression_level is not None else ""
173
+ compression_filter_r = f"save_args$compression_filter <- '{compression_filter}'" if compression_filter is not None else ""
174
+
175
+ r_script_content = r_script_template.format(
176
+ input_rds_path=input_spatial_experiment_rds.resolve(),
177
+ output_dir_path=output_directory.resolve(),
178
+ precomputed_r=str(precomputed).upper(),
179
+ deferred_r=str(deferred).upper(),
180
+ compression_level_r=compression_level_r,
181
+ compression_filter_r=compression_filter_r,
182
+ )
183
+
184
+ # Execute R script
185
+ result = _run_r_script(r_script_content, output_directory.parent) # Run in parent dir to allow output_directory creation
186
+ if "error" in result:
187
+ return result
188
+
189
+ result["output_files"] = [str(output_directory.resolve())]
190
+ return result
191
+
192
+
193
+ @mcp.tool()
194
+ def read_spatial_experiment(
195
+ input_directory: Path,
196
+ output_spatial_experiment_rds: Path,
197
+ metadata_only: bool = False,
198
+ image_format: Optional[str] = None,
199
+ ) -> Dict[str, Any]:
200
+ """
201
+ Loads a SpatialExperiment object and its images from file artifacts.
202
+
203
+ This function reconstructs an R SpatialExperiment object from a directory
204
+ of file artifacts previously saved by `save_spatial_experiment`.
205
+
206
+ Args:
207
+ input_directory: Path to the directory containing the file artifacts.
208
+ This directory must exist and be valid.
209
+ output_spatial_experiment_rds: Path to the output RDS file where the
210
+ reconstructed SpatialExperiment object will be saved.
211
+ The parent directory will be created if it does not exist.
212
+ metadata_only: Logical, whether to only load metadata and not the full data.
213
+ Defaults to FALSE.
214
+ image_format: String, desired image format for loading (e.g., "png", "jpeg", "tiff").
215
+ If None, images are loaded in their original format.
216
+
217
+ Returns:
218
+ A dictionary containing execution details, including the command executed,
219
+ stdout, stderr, and a list of generated output files.
220
+ Returns an error if input directory is not found, output path is invalid,
221
+ or R script execution fails.
222
+ """
223
+ # Input validation
224
+ if not input_directory.is_dir():
225
+ return {
226
+ "command_executed": "",
227
+ "stdout": "",
228
+ "stderr": f"Error: Input directory not found at '{input_directory}'",
229
+ "output_files": [],
230
+ }
231
+
232
+ if output_spatial_experiment_rds.exists() and not output_spatial_experiment_rds.is_file():
233
+ return {
234
+ "command_executed": "",
235
+ "stdout": "",
236
+ "stderr": f"Error: Output path '{output_spatial_experiment_rds}' exists but is not a file.",
237
+ "output_files": [],
238
+ }
239
+
240
+ try:
241
+ output_spatial_experiment_rds.parent.mkdir(parents=True, exist_ok=True)
242
+ except OSError as e:
243
+ return {
244
+ "command_executed": "",
245
+ "stdout": "",
246
+ "stderr": f"Error: Could not create parent directory for output RDS file '{output_spatial_experiment_rds}': {e}",
247
+ "output_files": [],
248
+ }
249
+
250
+ # Construct R script
251
+ r_script_template = """
252
+ suppressPackageStartupMessages({{
253
+ library(alabaster.spatial)
254
+ library(SpatialExperiment)
255
+ library(S4Vectors)
256
+ library(alabaster.base)
257
+ library(rhdf5) # Explicitly load for HDF5 operations
258
+ }})
259
+
260
+ input_dir_path <- "{input_dir_path}"
261
+ output_rds_path <- "{output_rds_path}"
262
+
263
+ # Prepare additional arguments for readObject
264
+ read_args <- list(
265
+ path = input_dir_path,
266
+ metadata.only = {metadata_only_r}
267
+ )
268
+ {image_format_r}
269
+
270
+ # Call readObject
271
+ se_object <- do.call(alabaster.spatial::readObject, read_args)
272
+
273
+ # Save the SpatialExperiment object to RDS
274
+ saveRDS(se_object, file = output_rds_path)
275
+
276
+ message(paste0("SpatialExperiment object loaded from: ", input_dir_path, " and saved to: ", output_rds_path))
277
+ """
278
+
279
+ image_format_r = f"read_args$image.format <- '{image_format}'" if image_format is not None else ""
280
+
281
+ r_script_content = r_script_template.format(
282
+ input_dir_path=input_directory.resolve(),
283
+ output_rds_path=output_spatial_experiment_rds.resolve(),
284
+ metadata_only_r=str(metadata_only).upper(),
285
+ image_format_r=image_format_r,
286
+ )
287
+
288
+ # Execute R script
289
+ result = _run_r_script(r_script_content, output_spatial_experiment_rds.parent)
290
+ if "error" in result:
291
+ return result
292
+
293
+ result["output_files"] = [str(output_spatial_experiment_rds.resolve())]
294
+ return result
295
+
296
+ if __name__ == "__main__":
297
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_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.spatial/app/bioconductor-alabaster.spatial_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_alabaster_spatial'
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-awaggregator/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-awaggregator via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-awaggregator -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-awaggregator_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-awaggregator_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-awaggregator_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Dict, List
4
+
5
+ # MCP decorator is assumed to be available in the execution environment.
6
+ # No import is needed as per the instructions.
7
+ # class mcp:
8
+ # @staticmethod
9
+ # def tool():
10
+ # def decorator(func):
11
+ # return func
12
+ # return decorator
13
+
14
+
15
+ def _run_awaggregator_cli(cmd: List[str], output_map: Dict[str, Path]) -> Dict:
16
+ """
17
+ A private helper function to execute the awaggregator wrapper script,
18
+ handle subprocess execution, and format the output.
19
+ """
20
+ command_executed = " ".join(map(str, cmd))
21
+ try:
22
+ result = subprocess.run(
23
+ cmd,
24
+ capture_output=True,
25
+ text=True,
26
+ check=True,
27
+ )
28
+ return {
29
+ "command_executed": command_executed,
30
+ "stdout": result.stdout,
31
+ "stderr": result.stderr,
32
+ "output_files": {key: str(path) for key, path in output_map.items()},
33
+ }
34
+ except FileNotFoundError:
35
+ # This error is raised if 'Rscript' or the wrapper is not in PATH
36
+ return {
37
+ "command_executed": command_executed,
38
+ "stdout": "",
39
+ "stderr": "Error: 'Rscript' or the wrapper script 'run_awaggregator.R' not found. "
40
+ "Please ensure R and the tool's wrapper script are in the system's PATH.",
41
+ "output_files": {},
42
+ "error": "Executable not found."
43
+ }
44
+ except subprocess.CalledProcessError as e:
45
+ # This error is raised for non-zero exit codes from the tool
46
+ return {
47
+ "command_executed": command_executed,
48
+ "stdout": e.stdout,
49
+ "stderr": e.stderr,
50
+ "output_files": {},
51
+ "error": f"Tool execution failed with exit code {e.returncode}",
52
+ }
53
+
54
+
55
+ from mcp.server.fastmcp import FastMCP
56
+
57
+ SERVER_NAME = 'local_bioconductor_awaggregator'
58
+ mcp = FastMCP(SERVER_NAME)
59
+
60
+ @mcp.tool()
61
+ def aw_weight(
62
+ expression_matrix: Path,
63
+ design_matrix: Path,
64
+ output_weights_file: Path,
65
+ ):
66
+ """
67
+ Computes adaptive weights for gene expression data meta-analysis.
68
+
69
+ This tool wraps the `aw.weight` function from the Bioconductor `awaggregator` package.
70
+ It takes an expression data matrix and a design matrix to compute weights for each study.
71
+ The resulting weights matrix can be used with the `aw_mean` or `aw_median` tools.
72
+
73
+ Args:
74
+ expression_matrix: Path to the input expression data matrix file (e.g., CSV, TSV).
75
+ Rows should represent genes and columns should represent samples.
76
+ design_matrix: Path to the input design matrix file (e.g., CSV, TSV).
77
+ This matrix describes the experimental design.
78
+ output_weights_file: Path to save the computed weights matrix.
79
+
80
+ Returns:
81
+ A dictionary containing the execution command, stdout, stderr, and a
82
+ path to the output weights file.
83
+ """
84
+ # --- Input Validation ---
85
+ if not expression_matrix.is_file():
86
+ raise FileNotFoundError(f"Input expression matrix file not found: {expression_matrix}")
87
+ if not design_matrix.is_file():
88
+ raise FileNotFoundError(f"Input design matrix file not found: {design_matrix}")
89
+
90
+ output_dir = output_weights_file.parent
91
+ if not output_dir.is_dir():
92
+ raise NotADirectoryError(f"Output directory does not exist: {output_dir}")
93
+
94
+ # --- Command Construction ---
95
+ # Assumes a wrapper script 'run_awaggregator.R' is available that handles CLI parsing.
96
+ cmd = [
97
+ "Rscript",
98
+ "run_awaggregator.R",
99
+ "--method",
100
+ "weight",
101
+ "--expression",
102
+ expression_matrix,
103
+ "--design",
104
+ design_matrix,
105
+ "--output",
106
+ output_weights_file,
107
+ ]
108
+
109
+ return _run_awaggregator_cli(cmd, {"weights_matrix": output_weights_file})
110
+
111
+
112
+ @mcp.tool()
113
+ def aw_mean(
114
+ data_matrix: Path,
115
+ weights_matrix: Path,
116
+ output_file: Path,
117
+ na_rm: bool = False,
118
+ ):
119
+ """
120
+ Performs adaptive-weighted mean aggregation on gene expression data.
121
+
122
+ This tool wraps the `aw.mean` function from the Bioconductor `awaggregator` package.
123
+ It takes a data matrix and a corresponding weights matrix to compute the weighted mean
124
+ for each gene (row) across studies (columns).
125
+
126
+ Args:
127
+ data_matrix: Path to the input data matrix file (e.g., CSV, TSV).
128
+ Rows should represent genes and columns should represent studies.
129
+ weights_matrix: Path to the input weights matrix file (e.g., CSV, TSV).
130
+ Must have the same dimensions as the data matrix.
131
+ output_file: Path to save the aggregated results.
132
+ na_rm: If True, NA values will be removed before computation. Defaults to False.
133
+
134
+ Returns:
135
+ A dictionary containing the execution command, stdout, stderr, and a
136
+ path to the output file.
137
+ """
138
+ # --- Input Validation ---
139
+ if not data_matrix.is_file():
140
+ raise FileNotFoundError(f"Input data matrix file not found: {data_matrix}")
141
+ if not weights_matrix.is_file():
142
+ raise FileNotFoundError(f"Input weights matrix file not found: {weights_matrix}")
143
+
144
+ output_dir = output_file.parent
145
+ if not output_dir.is_dir():
146
+ raise NotADirectoryError(f"Output directory does not exist: {output_dir}")
147
+
148
+ # --- Command Construction ---
149
+ cmd = [
150
+ "Rscript",
151
+ "run_awaggregator.R",
152
+ "--method",
153
+ "mean",
154
+ "--data",
155
+ data_matrix,
156
+ "--weights",
157
+ weights_matrix,
158
+ "--output",
159
+ output_file,
160
+ ]
161
+ if na_rm:
162
+ cmd.append("--na-rm")
163
+
164
+ return _run_awaggregator_cli(cmd, {"aggregated_data": output_file})
165
+
166
+
167
+ @mcp.tool()
168
+ def aw_median(
169
+ data_matrix: Path,
170
+ weights_matrix: Path,
171
+ output_file: Path,
172
+ na_rm: bool = False,
173
+ ):
174
+ """
175
+ Performs adaptive-weighted median aggregation on gene expression data.
176
+
177
+ This tool wraps the `aw.median` function from the Bioconductor `awaggregator` package.
178
+ It takes a data matrix and a corresponding weights matrix to compute the weighted median
179
+ for each gene (row) across studies (columns).
180
+
181
+ Args:
182
+ data_matrix: Path to the input data matrix file (e.g., CSV, TSV).
183
+ Rows should represent genes and columns should represent studies.
184
+ weights_matrix: Path to the input weights matrix file (e.g., CSV, TSV).
185
+ Must have the same dimensions as the data matrix.
186
+ output_file: Path to save the aggregated results.
187
+ na_rm: If True, NA values will be removed before computation. Defaults to False.
188
+
189
+ Returns:
190
+ A dictionary containing the execution command, stdout, stderr, and a
191
+ path to the output file.
192
+ """
193
+ # --- Input Validation ---
194
+ if not data_matrix.is_file():
195
+ raise FileNotFoundError(f"Input data matrix file not found: {data_matrix}")
196
+ if not weights_matrix.is_file():
197
+ raise FileNotFoundError(f"Input weights matrix file not found: {weights_matrix}")
198
+
199
+ output_dir = output_file.parent
200
+ if not output_dir.is_dir():
201
+ raise NotADirectoryError(f"Output directory does not exist: {output_dir}")
202
+
203
+ # --- Command Construction ---
204
+ cmd = [
205
+ "Rscript",
206
+ "run_awaggregator.R",
207
+ "--method",
208
+ "median",
209
+ "--data",
210
+ data_matrix,
211
+ "--weights",
212
+ weights_matrix,
213
+ "--output",
214
+ output_file,
215
+ ]
216
+ if na_rm:
217
+ cmd.append("--na-rm")
218
+
219
+ return _run_awaggregator_cli(cmd, {"aggregated_data": output_file})
220
+
221
+ if __name__ == "__main__":
222
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_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-awaggregator/app/bioconductor-awaggregator_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_awaggregator'
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-awaggregator/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-awaggregator/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-awaggregator:
5
+ build: .
6
+ image: mcp-bioconductor-awaggregator:latest
7
+ container_name: mcp-bioconductor-awaggregator
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-awaggregator
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-awaggregator/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-awaggregator
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-awaggregator/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-deseq2/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-deseq2 via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-deseq2 -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-deseq2_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-deseq2_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-deseq2_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-deseq2/app/__pycache__/bioconductor-deseq2_server.cpython-311.pyc ADDED
Binary file (15.7 kB). View file
 
Biomni/mcp_generated/mcp_bioconductor-deseq2/app/__pycache__/bioconductor-deseq2_shim_server.cpython-311.pyc ADDED
Binary file (3.6 kB). View file
 
Biomni/mcp_generated/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
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_deseq2'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def deseq2_differential_expression(
13
+ counts_path: str,
14
+ metadata_path: str,
15
+ design_formula: str,
16
+ output_results_path: str,
17
+ contrast_column: Optional[str] = None,
18
+ reference_level: Optional[str] = None,
19
+ treated_level: Optional[str] = None,
20
+ alpha: float = 0.05,
21
+ lfc_threshold: float = 0.0,
22
+ test_type: str = "Wald",
23
+ fit_type: str = "parametric",
24
+ reduced_formula: Optional[str] = None,
25
+ shrink_lfc: bool = True,
26
+ shrink_type: str = "apeglm"
27
+ ):
28
+ """
29
+ Performs differential expression analysis using DESeq2.
30
+
31
+ Args:
32
+ counts_path: Path to the raw counts matrix (CSV/TSV, genes as rows, samples as columns).
33
+ metadata_path: Path to the sample metadata (CSV/TSV, samples as rows).
34
+ design_formula: The design formula (e.g., '~ condition' or '~ genotype + condition').
35
+ output_results_path: Path where the results CSV will be saved.
36
+ contrast_column: The column in metadata to use for comparison.
37
+ reference_level: The baseline/control level in the contrast column.
38
+ treated_level: The treatment level in the contrast column.
39
+ alpha: Significance threshold (FDR).
40
+ lfc_threshold: Log2 fold change threshold for null hypothesis.
41
+ test_type: 'Wald' or 'LRT' (Likelihood Ratio Test).
42
+ fit_type: 'parametric', 'local', or 'mean'.
43
+ reduced_formula: Required if test_type is 'LRT' (e.g., '~ 1').
44
+ shrink_lfc: Whether to apply Log2 Fold Change shrinkage.
45
+ shrink_type: Type of shrinkage estimator ('apeglm', 'ashr', or 'normal').
46
+ """
47
+ counts_p = Path(counts_path)
48
+ meta_p = Path(metadata_path)
49
+ out_p = Path(output_results_path)
50
+
51
+ if not counts_p.exists():
52
+ raise FileNotFoundError(f"Counts file not found: {counts_path}")
53
+ if not meta_p.exists():
54
+ raise FileNotFoundError(f"Metadata file not found: {metadata_path}")
55
+ if test_type == "LRT" and not reduced_formula:
56
+ raise ValueError("reduced_formula is required when test_type is 'LRT'")
57
+
58
+ # Construct R script
59
+ r_script = f"""
60
+ library(DESeq2)
61
+
62
+ # Load data
63
+ counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE)
64
+ coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE)
65
+
66
+ # Ensure factors
67
+ for(col in colnames(coldata)) {{
68
+ if(is.character(coldata[[col]])) coldata[[col]] <- as.factor(coldata[[col]])
69
+ }}
70
+
71
+ # Create DESeqDataSet
72
+ dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}"))
73
+
74
+ # Run DESeq
75
+ if ("{test_type}" == "LRT") {{
76
+ dds <- DESeq(dds, test="LRT", reduced=as.formula("{reduced_formula}"), fitType="{fit_type}")
77
+ }} else {{
78
+ dds <- DESeq(dds, test="Wald", fitType="{fit_type}")
79
+ }}
80
+
81
+ # Results extraction
82
+ if (!is.null("{contrast_column}") && !is.null("{reference_level}") && !is.null("{treated_level}")) {{
83
+ res <- results(dds, contrast=c("{contrast_column}", "{treated_level}", "{reference_level}"),
84
+ alpha={alpha}, lfcThreshold={lfc_threshold})
85
+
86
+ if ({str(shrink_lfc).upper()}) {{
87
+ # Shrinkage requires the coefficient name or contrast
88
+ # apeglm is preferred but requires 'coef'
89
+ res <- lfcShrink(dds, contrast=c("{contrast_column}", "{treated_level}", "{reference_level}"),
90
+ res=res, type="{shrink_type}")
91
+ }}
92
+ }} else {{
93
+ res <- results(dds, alpha={alpha}, lfcThreshold={lfc_threshold})
94
+ }}
95
+
96
+ write.csv(as.data.frame(res), file="{out_p}")
97
+ """
98
+
99
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
100
+ tmp.write(r_script)
101
+ tmp_path = tmp.name
102
+
103
+ try:
104
+ cmd = ["Rscript", tmp_path]
105
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
106
+ return {
107
+ "command_executed": " ".join(cmd),
108
+ "stdout": result.stdout,
109
+ "stderr": result.stderr,
110
+ "output_files": [str(out_p)]
111
+ }
112
+ except subprocess.CalledProcessError as e:
113
+ return {
114
+ "error": "DESeq2 execution failed",
115
+ "stdout": e.stdout,
116
+ "stderr": e.stderr,
117
+ "command_executed": " ".join(e.cmd)
118
+ }
119
+ finally:
120
+ Path(tmp_path).unlink(missing_ok=True)
121
+
122
+ @mcp.tool()
123
+ def deseq2_transform_counts(
124
+ counts_path: str,
125
+ metadata_path: str,
126
+ design_formula: str,
127
+ output_path: str,
128
+ method: str = "vst",
129
+ blind: bool = True
130
+ ):
131
+ """
132
+ Transforms count data using Variance Stabilizing Transformation (VST) or Regularized Log (rlog).
133
+ Useful for PCA, clustering, and visualization.
134
+
135
+ Args:
136
+ counts_path: Path to raw counts matrix.
137
+ metadata_path: Path to sample metadata.
138
+ design_formula: Design formula (e.g., '~ condition').
139
+ output_path: Path to save the transformed matrix CSV.
140
+ method: Transformation method ('vst' or 'rlog').
141
+ blind: Whether to perform transformation in a blind manner (independent of design).
142
+ """
143
+ counts_p = Path(counts_path)
144
+ meta_p = Path(metadata_path)
145
+ out_p = Path(output_path)
146
+
147
+ if method not in ["vst", "rlog"]:
148
+ raise ValueError("Method must be 'vst' or 'rlog'")
149
+
150
+ r_script = f"""
151
+ library(DESeq2)
152
+ counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE)
153
+ coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE)
154
+ dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}"))
155
+
156
+ if ("{method}" == "vst") {{
157
+ transformed <- vst(dds, blind={str(blind).upper()})
158
+ }} else {{
159
+ transformed <- rlog(dds, blind={str(blind).upper()})
160
+ }}
161
+
162
+ write.csv(assay(transformed), file="{out_p}")
163
+ """
164
+
165
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
166
+ tmp.write(r_script)
167
+ tmp_path = tmp.name
168
+
169
+ try:
170
+ cmd = ["Rscript", tmp_path]
171
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
172
+ return {
173
+ "command_executed": " ".join(cmd),
174
+ "stdout": result.stdout,
175
+ "stderr": result.stderr,
176
+ "output_files": [str(out_p)]
177
+ }
178
+ except subprocess.CalledProcessError as e:
179
+ return {"error": str(e), "stderr": e.stderr}
180
+ finally:
181
+ Path(tmp_path).unlink(missing_ok=True)
182
+
183
+ @mcp.tool()
184
+ def deseq2_get_normalized_counts(
185
+ counts_path: str,
186
+ metadata_path: str,
187
+ design_formula: str,
188
+ output_path: str,
189
+ normalization_type: str = "ratio"
190
+ ):
191
+ """
192
+ Calculates size factors and returns normalized counts.
193
+
194
+ Args:
195
+ counts_path: Path to raw counts matrix.
196
+ metadata_path: Path to sample metadata.
197
+ design_formula: Design formula.
198
+ output_path: Path to save normalized counts CSV.
199
+ normalization_type: Method for size factor estimation ('ratio', 'poscounts', 'iterate').
200
+ """
201
+ counts_p = Path(counts_path)
202
+ meta_p = Path(metadata_path)
203
+ out_p = Path(output_path)
204
+
205
+ r_script = f"""
206
+ library(DESeq2)
207
+ counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE)
208
+ coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE)
209
+ dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}"))
210
+ dds <- estimateSizeFactors(dds, type="{normalization_type}")
211
+ norm_counts <- counts(dds, normalized=TRUE)
212
+ write.csv(norm_counts, file="{out_p}")
213
+ """
214
+
215
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
216
+ tmp.write(r_script)
217
+ tmp_path = tmp.name
218
+
219
+ try:
220
+ cmd = ["Rscript", tmp_path]
221
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
222
+ return {
223
+ "command_executed": " ".join(cmd),
224
+ "stdout": result.stdout,
225
+ "stderr": result.stderr,
226
+ "output_files": [str(out_p)]
227
+ }
228
+ except subprocess.CalledProcessError as e:
229
+ return {"error": str(e), "stderr": e.stderr}
230
+ finally:
231
+ Path(tmp_path).unlink(missing_ok=True)
232
+
233
+ @mcp.tool()
234
+ def deseq2_plot_pca(
235
+ counts_path: str,
236
+ metadata_path: str,
237
+ design_formula: str,
238
+ intgroup_columns: List[str],
239
+ output_plot_path: str,
240
+ ntop: int = 500
241
+ ):
242
+ """
243
+ Generates a PCA plot from DESeq2 transformed data.
244
+
245
+ Args:
246
+ counts_path: Path to raw counts matrix.
247
+ metadata_path: Path to sample metadata.
248
+ design_formula: Design formula.
249
+ intgroup_columns: List of columns in metadata to use for grouping/coloring.
250
+ output_plot_path: Path to save the PCA plot (PDF or PNG).
251
+ ntop: Number of top variable genes to use for PCA.
252
+ """
253
+ counts_p = Path(counts_path)
254
+ meta_p = Path(metadata_path)
255
+ out_p = Path(output_plot_path)
256
+
257
+ intgroup_r = 'c("' + '","'.join(intgroup_columns) + '")'
258
+
259
+ r_script = f"""
260
+ library(DESeq2)
261
+ library(ggplot2)
262
+ counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE)
263
+ coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE)
264
+ dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}"))
265
+ vsd <- vst(dds, blind=TRUE)
266
+
267
+ pca_data <- plotPCA(vsd, intgroup={intgroup_r}, ntop={ntop}, returnData=TRUE)
268
+ percentVar <- round(100 * attr(pca_data, "percentVar"))
269
+
270
+ p <- ggplot(pca_data, aes(PC1, PC2, color={intgroup_columns[0]})) +
271
+ geom_point(size=3) +
272
+ xlab(paste0("PC1: ", percentVar[1], "% variance")) +
273
+ ylab(paste0("PC2: ", percentVar[2], "% variance")) +
274
+ coord_fixed() +
275
+ theme_bw()
276
+
277
+ ggsave("{out_p}", plot=p, device="{out_p.suffix[1:]}")
278
+ """
279
+
280
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
281
+ tmp.write(r_script)
282
+ tmp_path = tmp.name
283
+
284
+ try:
285
+ cmd = ["Rscript", tmp_path]
286
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
287
+ return {
288
+ "command_executed": " ".join(cmd),
289
+ "stdout": result.stdout,
290
+ "stderr": result.stderr,
291
+ "output_files": [str(out_p)]
292
+ }
293
+ except subprocess.CalledProcessError as e:
294
+ return {"error": str(e), "stderr": e.stderr}
295
+ finally:
296
+ Path(tmp_path).unlink(missing_ok=True)
297
+
298
+ if __name__ == "__main__":
299
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-deseq2/app/bioconductor-deseq2_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-deseq2/app/bioconductor-deseq2_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_deseq2'
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-deseq2/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-deseq2/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-deseq2:
5
+ build: .
6
+ image: mcp-bioconductor-deseq2:latest
7
+ container_name: mcp-bioconductor-deseq2
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-deseq2
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-deseq2/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-deseq2
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-deseq2/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-gosemsim/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-gosemsim via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-gosemsim -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-gosemsim_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-gosemsim_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-gosemsim_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import json
3
+ import tempfile
4
+ from pathlib import Path
5
+ from typing import List, Optional, Literal, Union
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_bioconductor_gosemsim'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def go_similarity(
14
+ term1: str,
15
+ term2: str,
16
+ org_db: str = "org.Hs.eg.db",
17
+ ont: Literal["BP", "MF", "CC"] = "BP",
18
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
19
+ keytype: str = "ENTREZID"
20
+ ):
21
+ """
22
+ Calculate semantic similarity between two GO terms using GOSemSim.
23
+
24
+ Args:
25
+ term1: First GO term ID (e.g., 'GO:0004022').
26
+ term2: Second GO term ID (e.g., 'GO:0005515').
27
+ org_db: The annotation OrgDb package (e.g., 'org.Hs.eg.db' for human).
28
+ ont: One of "BP", "MF", or "CC" ontologies.
29
+ measure: Semantic similarity method ("Resnik", "Lin", "Rel", "Jiang", or "Wang").
30
+ keytype: Key type of the gene identifiers in the OrgDb.
31
+ """
32
+ if not term1.startswith("GO:") or not term2.startswith("GO:"):
33
+ return {"error": "GO terms must start with 'GO:' prefix."}
34
+
35
+ r_script = f"""
36
+ suppressPackageStartupMessages(library(GOSemSim))
37
+ suppressPackageStartupMessages(library({org_db}))
38
+
39
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
40
+ sim <- goSim('{term1}', '{term2}', semData=hsGO, measure='{measure}')
41
+ cat(sim)
42
+ """
43
+
44
+ return _execute_r_script(r_script)
45
+
46
+ @mcp.tool()
47
+ def mgo_similarity(
48
+ terms: List[str],
49
+ org_db: str = "org.Hs.eg.db",
50
+ ont: Literal["BP", "MF", "CC"] = "BP",
51
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
52
+ keytype: str = "ENTREZID"
53
+ ):
54
+ """
55
+ Calculate semantic similarity matrix for a list of GO terms.
56
+
57
+ Args:
58
+ terms: List of GO term IDs.
59
+ org_db: The annotation OrgDb package.
60
+ ont: One of "BP", "MF", or "CC" ontologies.
61
+ measure: Semantic similarity method.
62
+ keytype: Key type of the gene identifiers.
63
+ """
64
+ terms_r = 'c("' + '","'.join(terms) + '")'
65
+ r_script = f"""
66
+ suppressPackageStartupMessages(library(GOSemSim))
67
+ suppressPackageStartupMessages(library({org_db}))
68
+
69
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
70
+ sim_matrix <- mgoSim({terms_r}, semData=hsGO, measure='{measure}', combine=NULL)
71
+ write.csv(sim_matrix, row.names=TRUE)
72
+ """
73
+
74
+ return _execute_r_script(r_script)
75
+
76
+ @mcp.tool()
77
+ def gene_similarity(
78
+ gene1: str,
79
+ gene2: str,
80
+ org_db: str = "org.Hs.eg.db",
81
+ ont: Literal["BP", "MF", "CC"] = "BP",
82
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
83
+ combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA",
84
+ keytype: str = "ENTREZID"
85
+ ):
86
+ """
87
+ Calculate semantic similarity between two genes based on their GO annotations.
88
+
89
+ Args:
90
+ gene1: First gene ID.
91
+ gene2: Second gene ID.
92
+ org_db: The annotation OrgDb package.
93
+ ont: One of "BP", "MF", or "CC" ontologies.
94
+ measure: Semantic similarity method.
95
+ combine: Strategy to combine similarities of multiple GO terms ("max", "avg", "rcmax", "BMA").
96
+ keytype: Key type of the gene identifiers (e.g., 'ENTREZID', 'SYMBOL').
97
+ """
98
+ r_script = f"""
99
+ suppressPackageStartupMessages(library(GOSemSim))
100
+ suppressPackageStartupMessages(library({org_db}))
101
+
102
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
103
+ sim <- geneSim('{gene1}', '{gene2}', semData=hsGO, measure='{measure}', combine='{combine}')
104
+ print(sim)
105
+ """
106
+
107
+ return _execute_r_script(r_script)
108
+
109
+ @mcp.tool()
110
+ def mgene_similarity(
111
+ genes: List[str],
112
+ org_db: str = "org.Hs.eg.db",
113
+ ont: Literal["BP", "MF", "CC"] = "BP",
114
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
115
+ combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA",
116
+ keytype: str = "ENTREZID"
117
+ ):
118
+ """
119
+ Calculate semantic similarity matrix for a list of genes.
120
+
121
+ Args:
122
+ genes: List of gene IDs.
123
+ org_db: The annotation OrgDb package.
124
+ ont: One of "BP", "MF", or "CC" ontologies.
125
+ measure: Semantic similarity method.
126
+ combine: Strategy to combine similarities.
127
+ keytype: Key type of the gene identifiers.
128
+ """
129
+ genes_r = 'c("' + '","'.join(genes) + '")'
130
+ r_script = f"""
131
+ suppressPackageStartupMessages(library(GOSemSim))
132
+ suppressPackageStartupMessages(library({org_db}))
133
+
134
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
135
+ sim_matrix <- mgeneSim({genes_r}, semData=hsGO, measure='{measure}', combine='{combine}')
136
+ write.csv(sim_matrix, row.names=TRUE)
137
+ """
138
+
139
+ return _execute_r_script(r_script)
140
+
141
+ @mcp.tool()
142
+ def cluster_similarity(
143
+ cluster1: List[str],
144
+ cluster2: List[str],
145
+ org_db: str = "org.Hs.eg.db",
146
+ ont: Literal["BP", "MF", "CC"] = "BP",
147
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
148
+ combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA",
149
+ keytype: str = "ENTREZID"
150
+ ):
151
+ """
152
+ Calculate semantic similarity between two clusters (sets) of genes.
153
+
154
+ Args:
155
+ cluster1: List of gene IDs in the first cluster.
156
+ cluster2: List of gene IDs in the second cluster.
157
+ org_db: The annotation OrgDb package.
158
+ ont: One of "BP", "MF", or "CC" ontologies.
159
+ measure: Semantic similarity method.
160
+ combine: Strategy to combine similarities.
161
+ keytype: Key type of the gene identifiers.
162
+ """
163
+ c1_r = 'c("' + '","'.join(cluster1) + '")'
164
+ c2_r = 'c("' + '","'.join(cluster2) + '")'
165
+
166
+ r_script = f"""
167
+ suppressPackageStartupMessages(library(GOSemSim))
168
+ suppressPackageStartupMessages(library({org_db}))
169
+
170
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
171
+ sim <- clusterSim({c1_r}, {c2_r}, semData=hsGO, measure='{measure}', combine='{combine}')
172
+ cat(sim)
173
+ """
174
+
175
+ return _execute_r_script(r_script)
176
+
177
+ @mcp.tool()
178
+ def mcluster_similarity(
179
+ clusters_json: str,
180
+ org_db: str = "org.Hs.eg.db",
181
+ ont: Literal["BP", "MF", "CC"] = "BP",
182
+ measure: Literal["Resnik", "Lin", "Rel", "Jiang", "Wang"] = "Wang",
183
+ combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA",
184
+ keytype: str = "ENTREZID"
185
+ ):
186
+ """
187
+ Calculate semantic similarity matrix between multiple clusters of genes.
188
+
189
+ Args:
190
+ clusters_json: A JSON string representing a dictionary where keys are cluster names
191
+ and values are lists of gene IDs.
192
+ org_db: The annotation OrgDb package.
193
+ ont: One of "BP", "MF", or "CC" ontologies.
194
+ measure: Semantic similarity method.
195
+ combine: Strategy to combine similarities.
196
+ keytype: Key type of the gene identifiers.
197
+ """
198
+ try:
199
+ clusters_dict = json.loads(clusters_json)
200
+ except json.JSONDecodeError:
201
+ return {"error": "Invalid JSON format for clusters_json."}
202
+
203
+ # Construct R list of character vectors
204
+ r_list_elements = []
205
+ for name, genes in clusters_dict.items():
206
+ genes_str = 'c("' + '","'.join(genes) + '")'
207
+ r_list_elements.append(f'"{name}" = {genes_str}')
208
+
209
+ clusters_r = "list(" + ", ".join(r_list_elements) + ")"
210
+
211
+ r_script = f"""
212
+ suppressPackageStartupMessages(library(GOSemSim))
213
+ suppressPackageStartupMessages(library({org_db}))
214
+
215
+ hsGO <- godata('{org_db}', ont='{ont}', computeIC=TRUE, keytype='{keytype}')
216
+ clusters <- {clusters_r}
217
+ sim_matrix <- mclusterSim(clusters, semData=hsGO, measure='{measure}', combine='{combine}')
218
+ write.csv(sim_matrix, row.names=TRUE)
219
+ """
220
+
221
+ return _execute_r_script(r_script)
222
+
223
+ def _execute_r_script(script_content: str):
224
+ """
225
+ Internal helper to execute R code via Rscript.
226
+ """
227
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
228
+ tmp.write(script_content)
229
+ tmp_path = tmp.name
230
+
231
+ try:
232
+ # Note: We assume Rscript is in the PATH and GOSemSim is installed.
233
+ # If using a specific conda environment, the user should run the MCP server within it.
234
+ result = subprocess.run(
235
+ ["Rscript", tmp_path],
236
+ capture_output=True,
237
+ text=True,
238
+ check=True
239
+ )
240
+
241
+ return {
242
+ "command_executed": f"Rscript {tmp_path}",
243
+ "stdout": result.stdout.strip(),
244
+ "stderr": result.stderr.strip(),
245
+ "status": "success"
246
+ }
247
+ except subprocess.CalledProcessError as e:
248
+ return {
249
+ "command_executed": f"Rscript {tmp_path}",
250
+ "stdout": e.stdout,
251
+ "stderr": e.stderr,
252
+ "error": "R execution failed. Ensure R and GOSemSim are installed and the OrgDb package is available.",
253
+ "status": "error"
254
+ }
255
+ finally:
256
+ if Path(tmp_path).exists():
257
+ Path(tmp_path).unlink()
258
+
259
+ @mcp.tool()
260
+ def list_supported_orgdbs():
261
+ """
262
+ Returns a list of commonly used Bioconductor OrgDb packages for GOSemSim.
263
+ """
264
+ org_dbs = {
265
+ "human": "org.Hs.eg.db",
266
+ "mouse": "org.Mm.eg.db",
267
+ "rat": "org.Rn.eg.db",
268
+ "fly": "org.Dm.eg.db",
269
+ "zebrafish": "org.Dr.eg.db",
270
+ "yeast": "org.Sc.sgd.db",
271
+ "arabidopsis": "org.At.tair.db",
272
+ "chicken": "org.Gg.eg.db",
273
+ "bovine": "org.Bt.eg.db",
274
+ "worm": "org.Ce.eg.db",
275
+ "pig": "org.Ss.eg.db",
276
+ "rhesus": "org.Mmu.eg.db"
277
+ }
278
+ return {"supported_org_dbs": org_dbs}
279
+
280
+ if __name__ == "__main__":
281
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_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-gosemsim/app/bioconductor-gosemsim_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_gosemsim'
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-gosemsim/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-gosemsim/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-gosemsim:
5
+ build: .
6
+ image: mcp-bioconductor-gosemsim:latest
7
+ container_name: mcp-bioconductor-gosemsim
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-gosemsim
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-gosemsim/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-gosemsim
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-gosemsim/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-infercnv/app/bioconductor-infercnv_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-infercnv/app/bioconductor-infercnv_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_infercnv'
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-matrixgenerics/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-matrixgenerics via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-matrixgenerics -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-matrixgenerics_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-matrixgenerics_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-matrixgenerics_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Literal, Optional
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # In a real MCP environment, this would be imported.
8
+ def mcp_tool_decorator_placeholder(*args, **kwargs):
9
+ def decorator(func):
10
+ return func
11
+ return decorator
12
+
13
+ mcp = type("mcp", (), {"tool": mcp_tool_decorator_placeholder})
14
+
15
+
16
+ # A comprehensive list of functions provided by the MatrixGenerics package.
17
+ MatrixGenericsOperation = Literal[
18
+ "colAlls", "colAnyNAs", "colAnys", "colAvgsPerRow", "colCounts", "colCummaxs",
19
+ "colCummins", "colCumprods", "colCumsums", "colDiffs", "colIQRDiffs", "colIQRs",
20
+ "colLogSumExps", "colMadDiffs", "colMads", "colMaxs", "colMeans2", "colMedians",
21
+ "colMins", "colOrderStats", "colProds", "colQuantiles", "colRanges", "colRanks",
22
+ "colSdDiffs", "colSds", "colSums2", "colTabulates", "colVarDiffs", "colVars",
23
+ "colWeightedMads", "colWeightedMeans", "colWeightedMedians", "colWeightedSds",
24
+ "colWeightedVars", "rowAlls", "rowAnyNAs", "rowAnys", "rowAvgsPerCol",
25
+ "rowCounts", "rowCummaxs", "rowCummins", "rowCumprods", "rowCumsums",
26
+ "rowDiffs", "rowIQRDiffs", "rowIQRs", "rowLogSumExps", "rowMadDiffs",
27
+ "rowMads", "rowMaxs", "rowMeans2", "rowMedians", "rowMins", "rowOrderStats",
28
+ "rowProds", "rowQuantiles", "rowRanges", "rowRanks", "rowSdDiffs", "rowSds",
29
+ "rowSums2", "rowTabulates", "rowVarDiffs", "rowVars", "rowWeightedMads",
30
+ "rowWeightedMeans", "rowWeightedMedians", "rowWeightedSds", "rowWeightedVars"
31
+ ]
32
+
33
+ TiesMethod = Literal["average", "first", "last", "random", "max", "min"]
34
+
35
+ from mcp.server.fastmcp import FastMCP
36
+
37
+ SERVER_NAME = 'local_bioconductor_matrixgenerics'
38
+ mcp = FastMCP(SERVER_NAME)
39
+
40
+ @mcp.tool()
41
+ def perform_matrix_statistic(
42
+ input_matrix: Path,
43
+ operation: MatrixGenericsOperation,
44
+ output_filename: str,
45
+ na_rm: bool = True,
46
+ delimiter: Literal[",", "\t"] = "\t",
47
+ has_header: bool = True,
48
+ has_rownames: bool = True,
49
+ probs: Optional[str] = None,
50
+ weights: Optional[Path] = None,
51
+ ties_method: TiesMethod = "average",
52
+ value: Optional[float] = None,
53
+ lag: int = 1,
54
+ differences: int = 1,
55
+ which: Optional[int] = None,
56
+ preserve_shape: bool = False
57
+ ) -> dict:
58
+ """
59
+ Performs a specified summary statistic on a matrix using the MatrixGenerics R package.
60
+
61
+ This tool serves as a wrapper around the bioconductor-matrixgenerics R library,
62
+ which provides generic functions for matrix statistics. It reads a matrix from a file,
63
+ applies the chosen statistical operation, and saves the result to a new file.
64
+
65
+ Args:
66
+ input_matrix: Path to the input matrix file (e.g., CSV or TSV).
67
+ operation: The summary statistic to compute.
68
+ output_filename: The name for the output file that will be created.
69
+ na_rm: If True, NA/NaN values are removed before computation.
70
+ delimiter: Delimiter used in the input matrix file (',' or '\t').
71
+ has_header: Whether the input file has a header row.
72
+ has_rownames: Whether the first column of the input file contains row names.
73
+ probs: Comma-separated string of probabilities for quantile functions (e.g., "0.25,0.5,0.75").
74
+ weights: Path to a single-column file of numeric weights for weighted operations.
75
+ ties_method: Method for handling ties in ranking functions.
76
+ value: The specific value to count for '*Counts' functions.
77
+ lag: The lag for '*Diffs' functions.
78
+ differences: The number of differences for '*Diffs' functions.
79
+ which: The order statistic to extract for '*OrderStats' functions.
80
+ preserve_shape: For '*Ranks' functions, if TRUE the output has the same dimensions as the input.
81
+
82
+ Returns:
83
+ A dictionary containing the executed command, stdout, stderr, and a list of output files.
84
+ """
85
+ # --- Input Validation ---
86
+ if not input_matrix.is_file():
87
+ raise FileNotFoundError(f"Input matrix file not found: {input_matrix}")
88
+
89
+ if weights and not weights.is_file():
90
+ raise FileNotFoundError(f"Weights file not found: {weights}")
91
+
92
+ if not output_filename:
93
+ raise ValueError("Output filename cannot be empty.")
94
+
95
+ # --- R Script Generation ---
96
+ r_script = f"""
97
+ # Load necessary library
98
+ if (!require("MatrixGenerics", quietly = TRUE)) {{
99
+ stop("MatrixGenerics package is not installed.")
100
+ }}
101
+
102
+ # --- Argument Parsing ---
103
+ args <- commandArgs(trailingOnly = TRUE)
104
+ input_file <- args[1]
105
+ output_file <- args[2]
106
+ operation <- args[3]
107
+ na_rm <- as.logical(args[4])
108
+ sep <- args[5]
109
+ header <- as.logical(args[6])
110
+ rownames_col <- if (as.logical(args[7])) 1 else NULL
111
+ probs_str <- args[8]
112
+ weights_file <- args[9]
113
+ ties_method <- args[10]
114
+ value_str <- args[11]
115
+ lag <- as.integer(args[12])
116
+ differences <- as.integer(args[13])
117
+ which_str <- args[14]
118
+ preserve_shape <- as.logical(args[15])
119
+
120
+ # --- Data Loading ---
121
+ tryCatch({{
122
+ data <- read.delim(input_file, sep = sep, header = header, row.names = rownames_col, check.names = FALSE)
123
+ mat <- as.matrix(data)
124
+ }}, error = function(e) {{
125
+ stop(paste("Error reading input matrix:", e$message))
126
+ }})
127
+
128
+ # --- Dynamic Function Call ---
129
+ # Build the list of parameters
130
+ params <- list(x = mat)
131
+
132
+ # Add common parameters if the function accepts them
133
+ func_args <- names(formals(operation))
134
+ if ("na.rm" %in% func_args) params$na.rm <- na_rm
135
+ if ("ties.method" %in% func_args) params$ties.method <- ties_method
136
+ if ("value" %in% func_args && value_str != "NULL") params$value <- as.numeric(value_str)
137
+ if ("lag" %in% func_args) params$lag <- lag
138
+ if ("differences" %in% func_args) params$differences <- differences
139
+ if ("which" %in% func_args && which_str != "NULL") params$which <- as.integer(which_str)
140
+ if ("preserveShape" %in% func_args) params$preserveShape <- preserve_shape
141
+
142
+ # Handle special parameters
143
+ if (grepl("Quantiles", operation) && probs_str != "NULL") {{
144
+ params$probs <- as.numeric(strsplit(probs_str, ",")[[1]])
145
+ }}
146
+ if (grepl("Weighted", operation) && weights_file != "NULL") {{
147
+ tryCatch({{
148
+ weights_vector <- read.table(weights_file, header = FALSE, colClasses = "numeric")[[1]]
149
+ params$w <- weights_vector
150
+ }}, error = function(e) {{
151
+ stop(paste("Error reading weights file:", e$message))
152
+ }})
153
+ }}
154
+
155
+ # Get the function from the package namespace
156
+ func <- get(operation, asNamespace("MatrixGenerics"))
157
+
158
+ # Execute the function
159
+ result <- do.call(func, params)
160
+
161
+ # --- Output Writing ---
162
+ # write.table is robust for vectors, matrices, and data.frames
163
+ write.table(result, file = output_file, sep = "\\t", quote = FALSE, col.names = NA)
164
+ """
165
+
166
+ with tempfile.TemporaryDirectory() as temp_dir:
167
+ temp_path = Path(temp_dir)
168
+ r_script_path = temp_path / "matrix_op.R"
169
+ with open(r_script_path, "w") as f:
170
+ f.write(r_script)
171
+
172
+ output_path = Path.cwd() / output_filename
173
+
174
+ # --- Command Construction ---
175
+ cmd = [
176
+ "Rscript",
177
+ str(r_script_path),
178
+ str(input_matrix),
179
+ str(output_path),
180
+ operation,
181
+ str(na_rm).upper(),
182
+ "\\t" if delimiter == "\t" else ",",
183
+ str(has_header).upper(),
184
+ str(has_rownames).upper(),
185
+ probs if probs else "NULL",
186
+ str(weights) if weights else "NULL",
187
+ ties_method,
188
+ str(value) if value is not None else "NULL",
189
+ str(lag),
190
+ str(differences),
191
+ str(which) if which is not None else "NULL",
192
+ str(preserve_shape).upper()
193
+ ]
194
+
195
+ # --- Subprocess Execution ---
196
+ try:
197
+ process = subprocess.run(
198
+ cmd,
199
+ check=True,
200
+ capture_output=True,
201
+ text=True,
202
+ )
203
+ stdout = process.stdout
204
+ stderr = process.stderr
205
+ except subprocess.CalledProcessError as e:
206
+ return {
207
+ "command_executed": " ".join(map(str, cmd)),
208
+ "stdout": e.stdout,
209
+ "stderr": e.stderr,
210
+ "output_files": [],
211
+ "error": "R script execution failed."
212
+ }
213
+ except FileNotFoundError:
214
+ return {
215
+ "command_executed": " ".join(map(str, cmd)),
216
+ "stdout": "",
217
+ "stderr": "Rscript command not found. Please ensure R is installed and in your PATH.",
218
+ "output_files": [],
219
+ "error": "Dependency not found."
220
+ }
221
+
222
+ # --- Structured Result Return ---
223
+ return {
224
+ "command_executed": " ".join(map(str, cmd)),
225
+ "stdout": stdout,
226
+ "stderr": stderr,
227
+ "output_files": [str(output_path)]
228
+ }
229
+
230
+ if __name__ == "__main__":
231
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_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-matrixgenerics/app/bioconductor-matrixgenerics_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_matrixgenerics'
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-matrixgenerics/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-matrixgenerics:
5
+ build: .
6
+ image: mcp-bioconductor-matrixgenerics:latest
7
+ container_name: mcp-bioconductor-matrixgenerics
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-matrixgenerics
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-matrixgenerics/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-matrixgenerics
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-matrixgenerics/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-mia/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-mia via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-mia -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-mia_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-mia_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-mia_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-mia/app/bioconductor-mia_server.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
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_mia'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def run_mia_r_script(
13
+ r_script_content: str,
14
+ input_rds_path: Optional[Path] = None,
15
+ output_rds_path: Optional[Path] = None,
16
+ working_directory: Optional[Path] = None,
17
+ ) -> Dict[str, Any]:
18
+ """
19
+ Executes an R script that utilizes the bioconductor-mia package for microbiome analysis.
20
+
21
+ This tool provides a generic interface to run arbitrary R code that is expected
22
+ to use the 'mia' Bioconductor package. It allows for loading an input R data
23
+ object (e.g., a SummarizedExperiment saved as .rds) and saving an output R data
24
+ object.
25
+
26
+ Args:
27
+ r_script_content: A string containing the R script to be executed.
28
+ The script should assume 'mia' is loaded.
29
+ If `input_rds_path` is provided, the loaded object will
30
+ be available as `input_data`.
31
+ If `output_rds_path` is provided, the script should
32
+ assign the object to be saved to a variable named
33
+ `output_data`.
34
+ input_rds_path: Optional path to an R data object (.rds file) to be loaded
35
+ into the R environment before executing the script.
36
+ The object will be loaded as 'input_data'.
37
+ output_rds_path: Optional path where the R script should save an R data
38
+ object (e.g., a SummarizedExperiment) using saveRDS().
39
+ The R script must assign the object to be saved to a
40
+ variable named 'output_data'.
41
+ working_directory: Optional path to a directory where the R script will be
42
+ executed. This can be useful for scripts that use
43
+ relative paths for additional input/output files.
44
+
45
+ Returns:
46
+ A dictionary containing the command executed, stdout, stderr, and any
47
+ generated output files.
48
+ """
49
+ if not r_script_content.strip():
50
+ raise ValueError("r_script_content cannot be empty.")
51
+
52
+ output_files: List[Path] = []
53
+
54
+ # Validate input_rds_path if provided
55
+ if input_rds_path:
56
+ if not input_rds_path.is_file():
57
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds_path}")
58
+ if not input_rds_path.suffix == ".rds":
59
+ raise ValueError(f"Input RDS file must have a '.rds' extension: {input_rds_path}")
60
+
61
+ # Prepare output_rds_path if provided
62
+ if output_rds_path:
63
+ output_rds_path = output_rds_path.resolve() # Ensure absolute path
64
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True) # Create parent dirs
65
+ if not output_rds_path.suffix == ".rds":
66
+ raise ValueError(f"Output RDS file must have a '.rds' extension: {output_rds_path}")
67
+ output_files.append(output_rds_path)
68
+
69
+ # Create a temporary directory for the R script
70
+ with tempfile.TemporaryDirectory() as tmpdir:
71
+ tmpdir_path = Path(tmpdir)
72
+
73
+ # Determine the actual working directory for the R process
74
+ actual_working_dir = working_directory if working_directory else tmpdir_path
75
+ actual_working_dir.mkdir(parents=True, exist_ok=True) # Ensure it exists
76
+
77
+ r_script_file = tmpdir_path / "mia_script.R"
78
+
79
+ # Construct the full R script content
80
+ full_r_script_lines = [
81
+ "library(mia)",
82
+ "options(warn=-1)", # Suppress warnings for cleaner output
83
+ ]
84
+
85
+ if input_rds_path:
86
+ full_r_script_lines.append(f"input_data <- readRDS('{input_rds_path.resolve()}')")
87
+
88
+ full_r_script_lines.append(r_script_content)
89
+
90
+ if output_rds_path:
91
+ # The user's script is expected to assign the result to 'output_data'
92
+ # We will then save 'output_data' to the specified path.
93
+ full_r_script_lines.append(f"if (exists('output_data')) {{ saveRDS(output_data, '{output_rds_path}') }} else {{ message('Warning: output_data variable not found, no RDS file saved.') }}")
94
+
95
+ r_script_file.write_text("\n".join(full_r_script_lines))
96
+
97
+ command = ["Rscript", str(r_script_file)]
98
+
99
+ try:
100
+ process = subprocess.run(
101
+ command,
102
+ cwd=actual_working_dir,
103
+ capture_output=True,
104
+ text=True,
105
+ check=True,
106
+ )
107
+ stdout = process.stdout
108
+ stderr = process.stderr
109
+ except subprocess.CalledProcessError as e:
110
+ return {
111
+ "command_executed": " ".join(command),
112
+ "stdout": e.stdout,
113
+ "stderr": e.stderr,
114
+ "error": f"Rscript execution failed with exit code {e.returncode}",
115
+ "output_files": [],
116
+ }
117
+ except FileNotFoundError:
118
+ return {
119
+ "command_executed": " ".join(command),
120
+ "stdout": "",
121
+ "stderr": "Error: Rscript command not found. Is R installed and in PATH?",
122
+ "error": "Rscript not found",
123
+ "output_files": [],
124
+ }
125
+
126
+ return {
127
+ "command_executed": " ".join(command),
128
+ "stdout": stdout,
129
+ "stderr": stderr,
130
+ "output_files": [str(p) for p in output_files],
131
+ }
132
+
133
+ if __name__ == "__main__":
134
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-mia/app/bioconductor-mia_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-mia/app/bioconductor-mia_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_mia'
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-mia/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-mia/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-mia:
5
+ build: .
6
+ image: mcp-bioconductor-mia:latest
7
+ container_name: mcp-bioconductor-mia
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-mia
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-mia/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-mia
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-mia/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/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-org.ce.eg.db via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-org.ce.eg.db -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-org.ce.eg.db_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-org.ce.eg.db_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-org.ce.eg.db_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import textwrap
3
+ from pathlib import Path
4
+ from typing import List, Optional, Dict, Any
5
+
6
+ # MCP decorator is not imported as per instructions, but assumed to be present.
7
+ # from mcp import tool as mcp_tool
8
+ # For the purpose of this script, we'll define a dummy decorator.
9
+ class mcp:
10
+ def tool():
11
+ def decorator(f):
12
+ return f
13
+ return decorator
14
+
15
+ from mcp.server.fastmcp import FastMCP
16
+
17
+ SERVER_NAME = 'local_bioconductor_org_ce_eg_db'
18
+ mcp = FastMCP(SERVER_NAME)
19
+
20
+ @mcp.tool()
21
+ def select_annotations(
22
+ keys: List[str],
23
+ keytype: str,
24
+ columns: List[str],
25
+ output_file: str,
26
+ output_format: str = "tsv",
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Perform a complex query on the org.Ce.eg.db C. elegans annotation database using the select() method.
30
+ This returns a table of mappings between the provided keys and requested columns.
31
+
32
+ Args:
33
+ keys: List of identifiers to look up (e.g., ["172469", "172470"] or ["skn-1", "gfp"]).
34
+ keytype: The type of the input keys (e.g., "ENTREZID", "SYMBOL", "WORMBASE", "ENSEMBL").
35
+ columns: The annotation types to retrieve (e.g., ["GO", "EVIDENCE", "ONTOLOGY", "GENENAME"]).
36
+ output_file: Path to save the resulting table.
37
+ output_format: Format of the output file ("tsv" or "csv"). Defaults to "tsv".
38
+ """
39
+ # Input validation
40
+ if not keys:
41
+ raise ValueError("The 'keys' list cannot be empty.")
42
+ if not keytype:
43
+ raise ValueError("The 'keytype' parameter must be specified.")
44
+ if not columns:
45
+ raise ValueError("The 'columns' list cannot be empty.")
46
+
47
+ valid_formats = ["tsv", "csv"]
48
+ if output_format not in valid_formats:
49
+ raise ValueError(f"output_format must be one of {valid_formats}")
50
+
51
+ out_path = Path(output_file).resolve()
52
+ out_path.parent.mkdir(parents=True, exist_ok=True)
53
+
54
+ # Prepare R vectors safely
55
+ keys_str = ", ".join([f'"{k}"' for k in keys])
56
+ cols_str = ", ".join([f'"{c}"' for c in columns])
57
+
58
+ # The R script uses f-string formatting. Literal curly braces for R code blocks
59
+ # must be escaped by doubling them (e.g., {{ and }}).
60
+ r_script = textwrap.dedent(f"""
61
+ suppressPackageStartupMessages(library(org.Ce.eg.db))
62
+ suppressPackageStartupMessages(library(AnnotationDbi))
63
+
64
+ # Validate inputs in R
65
+ if (!("{keytype}" %in% keytypes(org.Ce.eg.db))) {{{{
66
+ stop(paste("Invalid keytype:", "{keytype}"))
67
+ }}}}
68
+
69
+ requested_cols <- c({cols_str})
70
+ valid_cols <- columns(org.Ce.eg.db)
71
+ if (!all(requested_cols %in% valid_cols)) {{{{
72
+ invalid_ones <- requested_cols[!(requested_cols %in% valid_cols)]
73
+ stop(paste("Invalid columns:", paste(invalid_ones, collapse=", ")))
74
+ }}}}
75
+
76
+ res <- tryCatch({{{{
77
+ AnnotationDbi::select(
78
+ org.Ce.eg.db,
79
+ keys = c({keys_str}),
80
+ columns = requested_cols,
81
+ keytype = "{keytype}"
82
+ )
83
+ }}}}, error = function(e) {{{{
84
+ stop(paste("Select failed:", e$message))
85
+ }}}})
86
+
87
+ if ("{output_format}" == "tsv") {{{{
88
+ write.table(res, file = "{out_path}", sep = "\\t", quote = FALSE, row.names = FALSE)
89
+ }}}} else {{{{
90
+ write.csv(res, file = "{out_path}", quote = FALSE, row.names = FALSE)
91
+ }}}}
92
+ """)
93
+
94
+ command_str = f"Rscript -e \"{r_script}\""
95
+ try:
96
+ process = subprocess.run(
97
+ ["Rscript", "-e", r_script],
98
+ capture_output=True,
99
+ text=True,
100
+ check=True,
101
+ encoding='utf-8'
102
+ )
103
+ return {
104
+ "command_executed": "Rscript -e 'AnnotationDbi::select(...)'",
105
+ "stdout": process.stdout,
106
+ "stderr": process.stderr,
107
+ "output_files": [str(out_path)]
108
+ }
109
+ except subprocess.CalledProcessError as e:
110
+ return {
111
+ "command_executed": "Rscript -e 'AnnotationDbi::select(...)'",
112
+ "stdout": e.stdout,
113
+ "stderr": e.stderr,
114
+ "error": f"R script execution failed. Stderr: {e.stderr}"
115
+ }
116
+
117
+ @mcp.tool()
118
+ def map_ids(
119
+ keys: List[str],
120
+ keytype: str,
121
+ column: str,
122
+ multi_vals: str = "first",
123
+ ) -> Dict[str, Any]:
124
+ """
125
+ Map identifiers from one type to another using the mapIds() method.
126
+ This is optimized for 1-to-1 or 1-to-many mapping.
127
+
128
+ Args:
129
+ keys: List of identifiers to map.
130
+ keytype: The type of the input keys (e.g., "WORMBASE").
131
+ column: The type of identifier to map to (e.g., "SYMBOL").
132
+ multi_vals: How to handle multiple matches. Options: "first", "list", "filter", "asNA", "CharacterList".
133
+ """
134
+ if not keys:
135
+ raise ValueError("The 'keys' list cannot be empty.")
136
+
137
+ valid_multi = ["first", "list", "filter", "asNA", "CharacterList"]
138
+ if multi_vals not in valid_multi:
139
+ raise ValueError(f"multi_vals must be one of {valid_multi}")
140
+
141
+ keys_str = ", ".join([f'"{k}"' for k in keys])
142
+
143
+ r_script = textwrap.dedent(f"""
144
+ suppressPackageStartupMessages(library(org.Ce.eg.db))
145
+ suppressPackageStartupMessages(library(AnnotationDbi))
146
+
147
+ res <- mapIds(
148
+ org.Ce.eg.db,
149
+ keys = c({keys_str}),
150
+ column = "{column}",
151
+ keytype = "{keytype}",
152
+ multiVals = "{multi_vals}"
153
+ )
154
+
155
+ # Convert to a structured key-value output for easy parsing
156
+ cat("---RESULT_START---\\n")
157
+ if (is.list(res) || is(res, "CharacterList")) {{{{
158
+ for (n in names(res)) {{{{
159
+ # Handle NULLs which represent no mapping
160
+ value <- if (is.null(res[[n]])) "NA" else paste(res[[n]], collapse=",")
161
+ cat(n, ":", value, "\\n")
162
+ }}}}
163
+ }}}} else {{{{
164
+ for (n in names(res)) {{{{
165
+ value <- if (is.na(res[n])) "NA" else res[n]
166
+ cat(n, ":", value, "\\n")
167
+ }}}}
168
+ }}}}
169
+ cat("---RESULT_END---\\n")
170
+ """)
171
+
172
+ command_str = "Rscript -e 'AnnotationDbi::mapIds(...)'"
173
+ try:
174
+ process = subprocess.run(
175
+ ["Rscript", "-e", r_script],
176
+ capture_output=True,
177
+ text=True,
178
+ check=True,
179
+ encoding='utf-8'
180
+ )
181
+
182
+ # Parse the structured output from R
183
+ output_str = process.stdout
184
+ result_block = output_str.split("---RESULT_START---\n")[-1].split("---RESULT_END---")[0].strip()
185
+ mappings = {}
186
+ if result_block:
187
+ for line in result_block.split('\n'):
188
+ parts = line.split(':', 1)
189
+ if len(parts) == 2:
190
+ key = parts[0].strip()
191
+ val_str = parts[1].strip()
192
+ if val_str == "NA":
193
+ mappings[key] = None
194
+ else:
195
+ values = [v.strip() for v in val_str.split(',')]
196
+ if multi_vals in ["first", "asNA"] and len(values) == 1:
197
+ mappings[key] = values[0]
198
+ else:
199
+ mappings[key] = values
200
+
201
+ return {
202
+ "command_executed": command_str,
203
+ "stdout": process.stdout,
204
+ "stderr": process.stderr,
205
+ "mappings": mappings
206
+ }
207
+ except subprocess.CalledProcessError as e:
208
+ return {
209
+ "command_executed": command_str,
210
+ "stdout": e.stdout,
211
+ "stderr": e.stderr,
212
+ "error": f"R script execution failed. Stderr: {e.stderr}"
213
+ }
214
+
215
+ @mcp.tool()
216
+ def list_resources() -> Dict[str, Any]:
217
+ """
218
+ List all available keytypes and columns (annotation types) in the org.Ce.eg.db package.
219
+ """
220
+ r_script = textwrap.dedent("""
221
+ suppressPackageStartupMessages(library(org.Ce.eg.db))
222
+ cat("---COLUMNS_START---\\n")
223
+ cat(paste(columns(org.Ce.eg.db), collapse=", "), "\\n")
224
+ cat("---COLUMNS_END---\\n")
225
+ cat("---KEYTYPES_START---\\n")
226
+ cat(paste(keytypes(org.Ce.eg.db), collapse=", "), "\\n")
227
+ cat("---KEYTYPES_END---\\n")
228
+ """)
229
+
230
+ command_str = "Rscript -e 'columns(...); keytypes(...)'"
231
+ try:
232
+ process = subprocess.run(
233
+ ["Rscript", "-e", r_script],
234
+ capture_output=True,
235
+ text=True,
236
+ check=True,
237
+ encoding='utf-8'
238
+ )
239
+
240
+ # Parse structured output
241
+ stdout_str = process.stdout
242
+ columns_str = stdout_str.split("---COLUMNS_START---\n")[1].split("---COLUMNS_END---")[0].strip()
243
+ keytypes_str = stdout_str.split("---KEYTYPES_START---\n")[1].split("---KEYTYPES_END---")[0].strip()
244
+
245
+ return {
246
+ "command_executed": command_str,
247
+ "stdout": process.stdout,
248
+ "stderr": process.stderr,
249
+ "columns": columns_str.split(", ") if columns_str else [],
250
+ "keytypes": keytypes_str.split(", ") if keytypes_str else []
251
+ }
252
+ except subprocess.CalledProcessError as e:
253
+ return {
254
+ "command_executed": command_str,
255
+ "stdout": e.stdout,
256
+ "stderr": e.stderr,
257
+ "error": f"R script execution failed. Stderr: {e.stderr}"
258
+ }
259
+
260
+ @mcp.tool()
261
+ def get_db_metadata() -> Dict[str, Any]:
262
+ """
263
+ Retrieve metadata about the org.Ce.eg.db package, such as version, species, and data sources.
264
+ """
265
+ r_script = "suppressPackageStartupMessages(library(org.Ce.eg.db)); print(metadata(org.Ce.eg.db))"
266
+ command_str = f"Rscript -e \"{r_script}\""
267
+ try:
268
+ process = subprocess.run(
269
+ ["Rscript", "-e", r_script],
270
+ capture_output=True,
271
+ text=True,
272
+ check=True,
273
+ encoding='utf-8'
274
+ )
275
+ return {
276
+ "command_executed": command_str,
277
+ "stdout": process.stdout,
278
+ "stderr": process.stderr,
279
+ "metadata": process.stdout.strip().split('\n')
280
+ }
281
+ except subprocess.CalledProcessError as e:
282
+ return {
283
+ "command_executed": command_str,
284
+ "stdout": e.stdout,
285
+ "stderr": e.stderr,
286
+ "error": f"R script execution failed. Stderr: {e.stderr}"
287
+ }
288
+
289
+ @mcp.tool()
290
+ def get_available_keys(
291
+ keytype: str,
292
+ pattern: Optional[str] = None,
293
+ limit: int = 100
294
+ ) -> Dict[str, Any]:
295
+ """
296
+ Retrieve a list of all available keys for a specific keytype, optionally filtered by a pattern.
297
+
298
+ Args:
299
+ keytype: The type of keys to retrieve (e.g., "SYMBOL").
300
+ pattern: An optional regular expression pattern to filter keys.
301
+ limit: Maximum number of keys to return. Defaults to 100.
302
+ """
303
+ if limit <= 0:
304
+ raise ValueError("Limit must be a positive integer.")
305
+
306
+ pattern_arg = f', pattern = "{pattern}"' if pattern else ""
307
+
308
+ r_script = textwrap.dedent(f"""
309
+ suppressPackageStartupMessages(library(org.Ce.eg.db))
310
+ all_keys <- keys(org.Ce.eg.db, keytype = "{keytype}"{pattern_arg})
311
+ cat(head(all_keys, {limit}), sep="\\n")
312
+ cat("\\n---TOTAL_COUNT:", length(all_keys), "---\\n")
313
+ """)
314
+
315
+ command_str = f"Rscript -e 'keys(..., keytype=\"{keytype}\")'"
316
+ try:
317
+ process = subprocess.run(
318
+ ["Rscript", "-e", r_script],
319
+ capture_output=True,
320
+ text=True,
321
+ check=True,
322
+ encoding='utf-8'
323
+ )
324
+
325
+ # Robust parsing
326
+ output_str = process.stdout
327
+ if "---TOTAL_COUNT:" in output_str:
328
+ keys_part, total_part = output_str.split("---TOTAL_COUNT:")
329
+ keys_list = keys_part.strip().split("\n") if keys_part.strip() else []
330
+ total_count = int(total_part.replace("---", "").strip())
331
+ else:
332
+ keys_list = output_str.strip().split("\n") if output_str.strip() else []
333
+ total_count = len(keys_list)
334
+
335
+ return {
336
+ "command_executed": command_str,
337
+ "stdout": process.stdout,
338
+ "stderr": process.stderr,
339
+ "keys": keys_list,
340
+ "total_available": total_count
341
+ }
342
+ except subprocess.CalledProcessError as e:
343
+ return {
344
+ "command_executed": command_str,
345
+ "stdout": e.stdout,
346
+ "stderr": e.stderr,
347
+ "error": f"R script execution failed. Stderr: {e.stderr}"
348
+ }
349
+
350
+ @mcp.tool()
351
+ def get_species_info() -> Dict[str, Any]:
352
+ """
353
+ Get the species name for which this annotation package was designed.
354
+ """
355
+ r_script = "suppressPackageStartupMessages(library(org.Ce.eg.db)); cat(species(org.Ce.eg.db))"
356
+ command_str = f"Rscript -e \"{r_script}\""
357
+ try:
358
+ process = subprocess.run(
359
+ ["Rscript", "-e", r_script],
360
+ capture_output=True,
361
+ text=True,
362
+ check=True,
363
+ encoding='utf-8'
364
+ )
365
+ return {
366
+ "command_executed": command_str,
367
+ "stdout": process.stdout,
368
+ "stderr": process.stderr,
369
+ "species": process.stdout.strip()
370
+ }
371
+ except subprocess.CalledProcessError as e:
372
+ return {
373
+ "command_executed": command_str,
374
+ "stdout": e.stdout,
375
+ "stderr": e.stderr,
376
+ "error": f"R script execution failed. Stderr: {e.stderr}"
377
+ }
378
+
379
+ if __name__ == "__main__":
380
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_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-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_org_ce_eg_db'
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")