czty commited on
Commit
337a1c8
·
verified ·
1 Parent(s): d5c1882

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_bcbio-nextgen/Dockerfile +40 -0
  2. Biomni/mcp_generated/mcp_bcbio-nextgen/app/bcbio-nextgen_server.py +276 -0
  3. Biomni/mcp_generated/mcp_bcbio-nextgen/app/bcbio-nextgen_shim_server.py +55 -0
  4. Biomni/mcp_generated/mcp_bcbio-nextgen/docker-compose.yml +22 -0
  5. Biomni/mcp_generated/mcp_bcbio-nextgen/environment.yaml +10 -0
  6. Biomni/mcp_generated/mcp_bcbio-nextgen/requirements.txt +2 -0
  7. Biomni/mcp_generated/mcp_bioconductor-banksy/Dockerfile +40 -0
  8. Biomni/mcp_generated/mcp_bioconductor-banksy/app/bioconductor-banksy_server.py +667 -0
  9. Biomni/mcp_generated/mcp_bioconductor-banksy/app/bioconductor-banksy_shim_server.py +55 -0
  10. Biomni/mcp_generated/mcp_bioconductor-banksy/app/requirements.txt +1 -0
  11. Biomni/mcp_generated/mcp_bioconductor-banksy/docker-compose.yml +22 -0
  12. Biomni/mcp_generated/mcp_bioconductor-banksy/environment.yaml +10 -0
  13. Biomni/mcp_generated/mcp_bioconductor-banksy/requirements.txt +2 -0
  14. Biomni/mcp_generated/mcp_bioconductor-benchdamic/Dockerfile +40 -0
  15. Biomni/mcp_generated/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_server.py +464 -0
  16. Biomni/mcp_generated/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_shim_server.py +55 -0
  17. Biomni/mcp_generated/mcp_bioconductor-benchdamic/docker-compose.yml +22 -0
  18. Biomni/mcp_generated/mcp_bioconductor-benchdamic/environment.yaml +10 -0
  19. Biomni/mcp_generated/mcp_bioconductor-benchdamic/requirements.txt +2 -0
  20. Biomni/mcp_generated/mcp_bioconductor-catscradle/Dockerfile +40 -0
  21. Biomni/mcp_generated/mcp_bioconductor-catscradle/app/bioconductor-catscradle_server.py +312 -0
  22. Biomni/mcp_generated/mcp_bioconductor-catscradle/app/bioconductor-catscradle_shim_server.py +55 -0
  23. Biomni/mcp_generated/mcp_bioconductor-catscradle/app/requirements.txt +1 -0
  24. Biomni/mcp_generated/mcp_bioconductor-catscradle/docker-compose.yml +22 -0
  25. Biomni/mcp_generated/mcp_bioconductor-catscradle/environment.yaml +10 -0
  26. Biomni/mcp_generated/mcp_bioconductor-catscradle/requirements.txt +2 -0
  27. Biomni/mcp_generated/mcp_bioconductor-clustifyr/Dockerfile +40 -0
  28. Biomni/mcp_generated/mcp_bioconductor-clustifyr/app/bioconductor-clustifyr_server.py +127 -0
  29. Biomni/mcp_generated/mcp_bioconductor-clustifyr/app/bioconductor-clustifyr_shim_server.py +55 -0
  30. Biomni/mcp_generated/mcp_bioconductor-clustifyr/docker-compose.yml +22 -0
  31. Biomni/mcp_generated/mcp_bioconductor-clustifyr/environment.yaml +10 -0
  32. Biomni/mcp_generated/mcp_bioconductor-clustifyr/requirements.txt +2 -0
  33. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/Dockerfile +40 -0
  34. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/app/bioconductor-genomicfeatures_server.py +178 -0
  35. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/app/bioconductor-genomicfeatures_shim_server.py +55 -0
  36. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/app/requirements.txt +1 -0
  37. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/docker-compose.yml +22 -0
  38. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/environment.yaml +10 -0
  39. Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/requirements.txt +2 -0
  40. Biomni/mcp_generated/mcp_bioconductor-hdf5array/Dockerfile +40 -0
  41. Biomni/mcp_generated/mcp_bioconductor-hdf5array/app/bioconductor-hdf5array_server.py +460 -0
  42. Biomni/mcp_generated/mcp_bioconductor-hdf5array/app/bioconductor-hdf5array_shim_server.py +55 -0
  43. Biomni/mcp_generated/mcp_bioconductor-hdf5array/app/requirements.txt +1 -0
  44. Biomni/mcp_generated/mcp_bioconductor-hdf5array/docker-compose.yml +22 -0
  45. Biomni/mcp_generated/mcp_bioconductor-hdf5array/environment.yaml +10 -0
  46. Biomni/mcp_generated/mcp_bioconductor-hdf5array/requirements.txt +2 -0
  47. Biomni/mcp_generated/mcp_bioconductor-irisfgm/Dockerfile +40 -0
  48. Biomni/mcp_generated/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_server.py +183 -0
  49. Biomni/mcp_generated/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_shim_server.py +55 -0
  50. Biomni/mcp_generated/mcp_bioconductor-irisfgm/docker-compose.yml +22 -0
Biomni/mcp_generated/mcp_bcbio-nextgen/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 bcbio-nextgen via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bcbio-nextgen -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/bcbio-nextgen_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/bcbio-nextgen_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/bcbio-nextgen_server.py"]
40
+
Biomni/mcp_generated/mcp_bcbio-nextgen/app/bcbio-nextgen_server.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional, Dict, Any
2
+ import subprocess
3
+ from pathlib import Path
4
+ import os
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ SERVER_NAME = 'local_bcbio_nextgen'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def bcbio_nextgen_run(
13
+ config_file: str,
14
+ num_cores: int = 1,
15
+ parallel_type: str = "local",
16
+ scheduler: Optional[str] = None,
17
+ queue: Optional[str] = None,
18
+ resources: Optional[str] = None,
19
+ tag: Optional[str] = None,
20
+ workdir: Optional[str] = None,
21
+ timeout: int = 15,
22
+ retries: int = 0,
23
+ ) -> Dict[str, Any]:
24
+ """
25
+ Run a bcbio-nextgen analysis pipeline using a provided configuration file.
26
+
27
+ Args:
28
+ config_file: Path to the YAML configuration file defining the analysis.
29
+ num_cores: Number of local cores to use for parallel execution.
30
+ parallel_type: Type of parallel execution (local, ipython, cluster, etc.).
31
+ scheduler: Scheduler for cluster execution (e.g., sge, slurm, torque, pbspro, lsf).
32
+ queue: Queue to submit jobs to on a cluster.
33
+ resources: Specific resource requirements for the scheduler (e.g., 'mem=16,vmem=20').
34
+ tag: Optional tag to identify this specific run.
35
+ workdir: Directory to use for processing (defaults to current directory).
36
+ timeout: Time in minutes to wait for ipython cluster startup.
37
+ retries: Number of times to retry failed steps.
38
+ """
39
+ config_path = Path(config_file)
40
+ if not config_path.exists():
41
+ return {"error": f"Configuration file not found: {config_file}"}
42
+
43
+ cmd = ["bcbio_nextgen.py", str(config_path.absolute())]
44
+
45
+ cmd.extend(["-n", str(num_cores)])
46
+ cmd.extend(["-t", parallel_type])
47
+
48
+ if scheduler:
49
+ cmd.extend(["-s", scheduler])
50
+ if queue:
51
+ cmd.extend(["-q", queue])
52
+ if resources:
53
+ cmd.extend(["-r", resources])
54
+ if tag:
55
+ cmd.extend(["--tag", tag])
56
+ if timeout != 15:
57
+ cmd.extend(["--timeout", str(timeout)])
58
+ if retries > 0:
59
+ cmd.extend(["--retries", str(retries)])
60
+
61
+ # Handle working directory
62
+ original_dir = os.getcwd()
63
+ if workdir:
64
+ work_path = Path(workdir)
65
+ if not work_path.exists():
66
+ work_path.mkdir(parents=True, exist_ok=True)
67
+ os.chdir(work_path)
68
+
69
+ try:
70
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
71
+ return {
72
+ "command_executed": " ".join(cmd),
73
+ "stdout": result.stdout,
74
+ "stderr": result.stderr,
75
+ "status": "success"
76
+ }
77
+ except subprocess.CalledProcessError as e:
78
+ return {
79
+ "command_executed": " ".join(cmd),
80
+ "stdout": e.stdout,
81
+ "stderr": e.stderr,
82
+ "error": str(e),
83
+ "status": "failed"
84
+ }
85
+ finally:
86
+ os.chdir(original_dir)
87
+
88
+ @mcp.tool()
89
+ def bcbio_nextgen_template(
90
+ template_name: str,
91
+ metadata_csv: str,
92
+ input_files: List[str],
93
+ out_dir: Optional[str] = None,
94
+ ) -> Dict[str, Any]:
95
+ """
96
+ Create a bcbio-nextgen processing description from a template and input files.
97
+
98
+ Args:
99
+ template_name: Name of the template to use (e.g., 'freebayes-variant', 'gatk-variant').
100
+ metadata_csv: Path to a CSV file containing sample metadata.
101
+ input_files: List of paths to input BAM or FASTQ files.
102
+ out_dir: Directory to write the generated configuration (defaults to current directory).
103
+ """
104
+ metadata_path = Path(metadata_csv)
105
+ if not metadata_path.exists():
106
+ return {"error": f"Metadata file not found: {metadata_csv}"}
107
+
108
+ # Validate input files
109
+ valid_inputs = []
110
+ for f in input_files:
111
+ p = Path(f)
112
+ if p.exists():
113
+ valid_inputs.append(str(p.absolute()))
114
+ else:
115
+ return {"error": f"Input file not found: {f}"}
116
+
117
+ cmd = ["bcbio_nextgen.py", "-w", "template", template_name, str(metadata_path.absolute())]
118
+ cmd.extend(valid_inputs)
119
+
120
+ # Handle output directory
121
+ original_dir = os.getcwd()
122
+ if out_dir:
123
+ out_path = Path(out_dir)
124
+ if not out_path.exists():
125
+ out_path.mkdir(parents=True, exist_ok=True)
126
+ os.chdir(out_path)
127
+
128
+ try:
129
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
130
+ return {
131
+ "command_executed": " ".join(cmd),
132
+ "stdout": result.stdout,
133
+ "stderr": result.stderr,
134
+ "status": "success",
135
+ "info": "Configuration files generated in the output directory."
136
+ }
137
+ except subprocess.CalledProcessError as e:
138
+ return {
139
+ "command_executed": " ".join(cmd),
140
+ "stdout": e.stdout,
141
+ "stderr": e.stderr,
142
+ "error": str(e),
143
+ "status": "failed"
144
+ }
145
+ finally:
146
+ os.chdir(original_dir)
147
+
148
+ @mcp.tool()
149
+ def bcbio_nextgen_upgrade(
150
+ tooldir: Optional[str] = None,
151
+ tools: bool = False,
152
+ data: bool = False,
153
+ genomes: Optional[List[str]] = None,
154
+ aligners: Optional[List[str]] = None,
155
+ cores: int = 1,
156
+ ) -> Dict[str, Any]:
157
+ """
158
+ Upgrade bcbio-nextgen software, third-party tools, or genome data.
159
+
160
+ Args:
161
+ tooldir: Directory where tools are installed.
162
+ tools: If True, upgrade third-party software tools.
163
+ data: If True, upgrade/install genome data.
164
+ genomes: List of genome builds to install/upgrade (e.g., ['hg38', 'mm10']).
165
+ aligners: List of aligners to install data for (e.g., ['bwa', 'bowtie2']).
166
+ cores: Number of cores to use for data downloads and indexing.
167
+ """
168
+ cmd = ["bcbio_nextgen.py", "upgrade"]
169
+
170
+ if tooldir:
171
+ cmd.extend(["--tooldir", tooldir])
172
+ if tools:
173
+ cmd.append("--tools")
174
+ if data:
175
+ cmd.append("--data")
176
+
177
+ if genomes:
178
+ for g in genomes:
179
+ cmd.extend(["--genomes", g])
180
+
181
+ if aligners:
182
+ for a in aligners:
183
+ cmd.extend(["--aligners", a])
184
+
185
+ cmd.extend(["--cores", str(cores)])
186
+
187
+ try:
188
+ # Upgrades can take a long time, but we capture output
189
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
190
+ return {
191
+ "command_executed": " ".join(cmd),
192
+ "stdout": result.stdout,
193
+ "stderr": result.stderr,
194
+ "status": "success"
195
+ }
196
+ except subprocess.CalledProcessError as e:
197
+ return {
198
+ "command_executed": " ".join(cmd),
199
+ "stdout": e.stdout,
200
+ "stderr": e.stderr,
201
+ "error": str(e),
202
+ "status": "failed"
203
+ }
204
+
205
+ @mcp.tool()
206
+ def bcbio_nextgen_install(
207
+ install_path: str,
208
+ tooldir: str,
209
+ genomes: List[str],
210
+ aligners: List[str],
211
+ nodata: bool = False,
212
+ isolate: bool = False,
213
+ ) -> Dict[str, Any]:
214
+ """
215
+ Run the bcbio-nextgen installer script to set up the environment.
216
+
217
+ Args:
218
+ install_path: Path to install bcbio-nextgen data and code.
219
+ tooldir: Path to install third-party software tools.
220
+ genomes: List of genome builds to install (e.g., ['hg38']).
221
+ aligners: List of aligners to prepare (e.g., ['bwa']).
222
+ nodata: If True, do not install genome data.
223
+ isolate: If True, install into an isolated environment.
224
+ """
225
+ # Note: This assumes bcbio_nextgen_install.py is in the PATH or current directory
226
+ # In a real environment, users might need to download it first.
227
+ cmd = ["python", "bcbio_nextgen_install.py", install_path, "--tooldir=" + tooldir]
228
+
229
+ for g in genomes:
230
+ cmd.extend(["--genomes", g])
231
+ for a in aligners:
232
+ cmd.extend(["--aligners", a])
233
+
234
+ if nodata:
235
+ cmd.append("--nodata")
236
+ if isolate:
237
+ cmd.append("--isolate")
238
+
239
+ try:
240
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
241
+ return {
242
+ "command_executed": " ".join(cmd),
243
+ "stdout": result.stdout,
244
+ "stderr": result.stderr,
245
+ "status": "success"
246
+ }
247
+ except subprocess.CalledProcessError as e:
248
+ return {
249
+ "command_executed": " ".join(cmd),
250
+ "stdout": e.stdout,
251
+ "stderr": e.stderr,
252
+ "error": str(e),
253
+ "status": "failed"
254
+ }
255
+
256
+ @mcp.tool()
257
+ def bcbio_nextgen_version() -> Dict[str, Any]:
258
+ """
259
+ Check the installed version of bcbio-nextgen.
260
+ """
261
+ cmd = ["bcbio_nextgen.py", "--version"]
262
+ try:
263
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
264
+ return {
265
+ "command_executed": " ".join(cmd),
266
+ "stdout": result.stdout.strip(),
267
+ "status": "success"
268
+ }
269
+ except subprocess.CalledProcessError as e:
270
+ return {
271
+ "error": str(e),
272
+ "status": "failed"
273
+ }
274
+
275
+ if __name__ == "__main__":
276
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bcbio-nextgen/app/bcbio-nextgen_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_bcbio-nextgen/app/bcbio-nextgen_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bcbio_nextgen'
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_bcbio-nextgen/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bcbio-nextgen:
5
+ build: .
6
+ image: mcp-bcbio-nextgen:latest
7
+ container_name: mcp-bcbio-nextgen
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bcbio-nextgen
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_bcbio-nextgen/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bcbio-nextgen
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bcbio-nextgen/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-banksy/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-banksy via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-banksy -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-banksy_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-banksy_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-banksy_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-banksy/app/bioconductor-banksy_server.py ADDED
@@ -0,0 +1,667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, List, Dict, Any
6
+
7
+ # Note: The @mcp.tool() decorator is assumed to be available in the environment
8
+ # where this code will be run, as per the instructions "NO NEED to import mcp".
9
+
10
+ from mcp.server.fastmcp import FastMCP
11
+
12
+ SERVER_NAME = 'local_bioconductor_banksy'
13
+ mcp = FastMCP(SERVER_NAME)
14
+
15
+ @mcp.tool()
16
+ def banksy_init_object(
17
+ input_seurat_rdata_path: Path,
18
+ output_banksy_rdata_path: Path,
19
+ assay: str = "Spatial",
20
+ verbose: bool = True,
21
+ ) -> Dict[str, Any]:
22
+ """
23
+ Initializes a BanksyObject from an existing Seurat object.
24
+
25
+ This tool takes an RData file containing a Seurat object, converts it
26
+ into a BanksyObject, and saves the new BanksyObject to an RData file.
27
+ This is often the first step before running the BANKSY algorithm.
28
+
29
+ Requires R and the 'banksy' and 'Seurat' R packages to be installed
30
+ and accessible in the environment.
31
+
32
+ Args:
33
+ input_seurat_rdata_path: Path to the input .RData file containing a Seurat object.
34
+ output_banksy_rdata_path: Path where the new BanksyObject will be saved as an .RData file.
35
+ assay: Name of the assay in the Seurat object to use for spatial data.
36
+ verbose: If TRUE, print messages during execution.
37
+
38
+ Returns:
39
+ A dictionary containing execution details:
40
+ - command_executed: The Rscript command and generated R script path.
41
+ - stdout: Standard output from the Rscript execution.
42
+ - stderr: Standard error from the Rscript execution.
43
+ - output_files: A list of paths to generated output files.
44
+ """
45
+ # 1. Input validation
46
+ if not input_seurat_rdata_path.exists():
47
+ raise FileNotFoundError(f"Input Seurat RData file not found: {input_seurat_rdata_path}")
48
+ if not input_seurat_rdata_path.is_file():
49
+ raise ValueError(f"Input Seurat RData path is not a file: {input_seurat_rdata_path}")
50
+ if output_banksy_rdata_path.suffix.lower() != ".rdata":
51
+ raise ValueError(f"Output RData file must have a .RData extension: {output_banksy_rdata_path}")
52
+ if not assay:
53
+ raise ValueError("Assay name cannot be empty.")
54
+
55
+ # Ensure output directory exists
56
+ output_banksy_rdata_path.parent.mkdir(parents=True, exist_ok=True)
57
+
58
+ # 2. Generate R script content
59
+ r_script_content = f"""
60
+ # Load required packages
61
+ library(Seurat)
62
+ library(banksy)
63
+
64
+ # Define input and output paths
65
+ input_obj_path <- "{input_seurat_rdata_path.as_posix()}"
66
+ output_obj_path <- "{output_banksy_rdata_path.as_posix()}"
67
+
68
+ # Check if input file exists
69
+ if (!file.exists(input_obj_path)) {{
70
+ stop(paste("Input RData file not found:", input_obj_path))
71
+ }}
72
+
73
+ # Load the object from RData. This approach handles cases where the object
74
+ # name inside the RData file is not known beforehand.
75
+ loaded_env <- new.env()
76
+ load(input_obj_path, envir = loaded_env)
77
+
78
+ seurat_obj <- NULL
79
+ for (var_name in ls(loaded_env)) {{
80
+ candidate <- get(var_name, envir = loaded_env)
81
+ if (inherits(candidate, "Seurat")) {{
82
+ seurat_obj <- candidate
83
+ break
84
+ }}
85
+ }}
86
+
87
+ if (is.null(seurat_obj)) {{
88
+ stop("No Seurat object found in the input RData file.")
89
+ }}
90
+
91
+ message("Initializing BanksyObject from Seurat object with parameters:")
92
+ message(paste(" assay:", "{assay}"))
93
+ message(paste(" verbose:", {str(verbose).upper()}))
94
+
95
+ # Create BanksyObject
96
+ banksy_obj <- BanksyObject(
97
+ seurat_obj,
98
+ assay = "{assay}",
99
+ verbose = {str(verbose).upper()}
100
+ )
101
+
102
+ # Save the new BanksyObject. Renaming to 'obj' for consistency with other banksy tools.
103
+ obj <- banksy_obj
104
+ save(obj, file = output_obj_path)
105
+
106
+ message(paste("BanksyObject saved to:", output_obj_path))
107
+ """
108
+
109
+ temp_r_script_path: Optional[Path] = None
110
+ try:
111
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".R") as temp_r_script:
112
+ temp_r_script.write(r_script_content)
113
+ temp_r_script_path = Path(temp_r_script.name)
114
+
115
+ command = ["Rscript", str(temp_r_script_path)]
116
+
117
+ # 3. Subprocess execution
118
+ process = subprocess.run(
119
+ command,
120
+ check=True,
121
+ capture_output=True,
122
+ text=True,
123
+ env=os.environ # Pass current environment to Rscript
124
+ )
125
+
126
+ stdout = process.stdout
127
+ stderr = process.stderr
128
+
129
+ # 4. Error handling: Check for R-specific errors in stderr
130
+ if "Error" in stderr or "stop(" in stderr:
131
+ raise RuntimeError(f"R script execution failed. Stderr: {stderr}")
132
+
133
+ if not output_banksy_rdata_path.exists():
134
+ raise RuntimeError(f"Output file was not created by R script: {output_banksy_rdata_path}")
135
+
136
+ return {
137
+ "command_executed": " ".join(command),
138
+ "stdout": stdout,
139
+ "stderr": stderr,
140
+ "output_files": [str(output_banksy_rdata_path)],
141
+ }
142
+
143
+ except FileNotFoundError:
144
+ raise RuntimeError("Rscript command not found. Is R installed and in your PATH?")
145
+ except subprocess.CalledProcessError as e:
146
+ raise RuntimeError(
147
+ f"Rscript execution failed with exit code {e.returncode}.\n"
148
+ f"Command: {' '.join(e.cmd)}\n"
149
+ f"Stdout: {e.stdout}\n"
150
+ f"Stderr: {e.stderr}"
151
+ )
152
+ finally:
153
+ # Clean up temporary R script
154
+ if temp_r_script_path and temp_r_script_path.exists():
155
+ os.remove(temp_r_script_path)
156
+
157
+
158
+ @mcp.tool()
159
+ def banksy_run_banksy(
160
+ input_rdata_path: Path,
161
+ output_rdata_path: Path,
162
+ k_neighbours: int = 10,
163
+ lambda_param: float = 0.1, # Renamed from 'lambda' to avoid Python keyword conflict
164
+ resolution: float = 0.8,
165
+ n_components: int = 2,
166
+ n_cores: int = 1,
167
+ verbose: bool = True,
168
+ seed: int = 123,
169
+ ) -> Dict[str, Any]:
170
+ """
171
+ Runs the core BANKSY algorithm on a Seurat or BanksyObject.
172
+
173
+ This tool takes an RData file containing a Seurat or BanksyObject,
174
+ applies the BANKSY algorithm for spatial transcriptomics analysis,
175
+ and saves the updated object to a new RData file.
176
+
177
+ Requires R and the 'banksy', 'Seurat', and 'future' R packages to be installed
178
+ and accessible in the environment.
179
+
180
+ Args:
181
+ input_rdata_path: Path to the input .RData file containing a Seurat or BanksyObject.
182
+ output_rdata_path: Path where the updated Seurat/BanksyObject will be saved as an .RData file.
183
+ k_neighbours: Number of neighbours for spatial graph construction.
184
+ lambda_param: Weight of spatial information (lambda parameter).
185
+ resolution: Resolution parameter for clustering.
186
+ n_components: Number of components for dimensionality reduction (e.g., UMAP/tSNE).
187
+ n_cores: Number of cores to use for parallel processing.
188
+ verbose: If TRUE, print messages during execution.
189
+ seed: Random seed for reproducibility.
190
+
191
+ Returns:
192
+ A dictionary containing execution details:
193
+ - command_executed: The Rscript command and generated R script path.
194
+ - stdout: Standard output from the Rscript execution.
195
+ - stderr: Standard error from the Rscript execution.
196
+ - output_files: A list of paths to generated output files.
197
+ """
198
+ # 1. Input validation
199
+ if not input_rdata_path.exists():
200
+ raise FileNotFoundError(f"Input RData file not found: {input_rdata_path}")
201
+ if not input_rdata_path.is_file():
202
+ raise ValueError(f"Input RData path is not a file: {input_rdata_path}")
203
+ if output_rdata_path.suffix.lower() != ".rdata":
204
+ raise ValueError(f"Output RData file must have a .RData extension: {output_rdata_path}")
205
+ if k_neighbours <= 0:
206
+ raise ValueError("k_neighbours must be a positive integer.")
207
+ if not (0 <= lambda_param <= 1):
208
+ raise ValueError("lambda_param must be between 0 and 1.")
209
+ if resolution <= 0:
210
+ raise ValueError("resolution must be a positive float.")
211
+ if n_components <= 0:
212
+ raise ValueError("n_components must be a positive integer.")
213
+ if n_cores <= 0:
214
+ raise ValueError("n_cores must be a positive integer.")
215
+
216
+ # Ensure output directory exists
217
+ output_rdata_path.parent.mkdir(parents=True, exist_ok=True)
218
+
219
+ # 2. Generate R script content
220
+ r_script_content = f"""
221
+ # Load required packages
222
+ library(Seurat)
223
+ library(banksy)
224
+ library(future) # For parallel processing
225
+
226
+ # Set up parallel processing if n_cores > 1
227
+ if ({n_cores} > 1) {{
228
+ plan("multisession", workers = {n_cores})
229
+ }} else {{
230
+ plan("sequential")
231
+ }}
232
+
233
+ # Set random seed for reproducibility
234
+ set.seed({seed})
235
+
236
+ # Define input and output paths
237
+ input_obj_path <- "{input_rdata_path.as_posix()}"
238
+ output_obj_path <- "{output_rdata_path.as_posix()}"
239
+
240
+ # Check if input file exists
241
+ if (!file.exists(input_obj_path)) {{
242
+ stop(paste("Input RData file not found:", input_obj_path))
243
+ }}
244
+
245
+ # Load the object
246
+ loaded_env <- new.env()
247
+ load(input_obj_path, envir = loaded_env)
248
+
249
+ obj <- NULL
250
+ for (var_name in ls(loaded_env)) {{
251
+ candidate <- get(var_name, envir = loaded_env)
252
+ if (inherits(candidate, "Seurat") || inherits(candidate, "BanksyObject")) {{
253
+ obj <- candidate
254
+ break
255
+ }}
256
+ }}
257
+
258
+ if (is.null(obj)) {{
259
+ stop("No Seurat or BanksyObject found in the input RData file.")
260
+ }}
261
+
262
+ message("Running banksy with parameters:")
263
+ message(paste(" k_neighbours:", {k_neighbours}))
264
+ message(paste(" lambda:", {lambda_param}))
265
+ message(paste(" resolution:", {resolution}))
266
+ message(paste(" n_components:", {n_components}))
267
+ message(paste(" n_cores:", {n_cores}))
268
+ message(paste(" verbose:", {str(verbose).upper()}))
269
+ message(paste(" seed:", {seed}))
270
+
271
+ # Run BANKSY
272
+ obj <- runBanksy(
273
+ object = obj,
274
+ k_neighbours = {k_neighbours},
275
+ lambda = {lambda_param},
276
+ resolution = {resolution},
277
+ n_components = {n_components},
278
+ verbose = {str(verbose).upper()}
279
+ )
280
+
281
+ # Save the updated object
282
+ save(obj, file = output_obj_path)
283
+
284
+ message(paste("Updated object saved to:", output_obj_path))
285
+ """
286
+
287
+ temp_r_script_path: Optional[Path] = None
288
+ try:
289
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".R") as temp_r_script:
290
+ temp_r_script.write(r_script_content)
291
+ temp_r_script_path = Path(temp_r_script.name)
292
+
293
+ command = ["Rscript", str(temp_r_script_path)]
294
+
295
+ # 3. Subprocess execution
296
+ process = subprocess.run(
297
+ command,
298
+ check=True,
299
+ capture_output=True,
300
+ text=True,
301
+ env=os.environ
302
+ )
303
+
304
+ stdout = process.stdout
305
+ stderr = process.stderr
306
+
307
+ # 4. Error handling: Check for R-specific errors in stderr
308
+ if "Error" in stderr or "stop(" in stderr:
309
+ raise RuntimeError(f"R script execution failed. Stderr: {stderr}")
310
+
311
+ if not output_rdata_path.exists():
312
+ raise RuntimeError(f"Output file was not created by R script: {output_rdata_path}")
313
+
314
+ return {
315
+ "command_executed": " ".join(command),
316
+ "stdout": stdout,
317
+ "stderr": stderr,
318
+ "output_files": [str(output_rdata_path)],
319
+ }
320
+
321
+ except FileNotFoundError:
322
+ raise RuntimeError("Rscript command not found. Is R installed and in your PATH?")
323
+ except subprocess.CalledProcessError as e:
324
+ raise RuntimeError(
325
+ f"Rscript execution failed with exit code {e.returncode}.\n"
326
+ f"Command: {' '.join(e.cmd)}\n"
327
+ f"Stdout: {e.stdout}\n"
328
+ f"Stderr: {e.stderr}"
329
+ )
330
+ finally:
331
+ # Clean up temporary R script
332
+ if temp_r_script_path and temp_r_script_path.exists():
333
+ os.remove(temp_r_script_path)
334
+
335
+
336
+ @mcp.tool()
337
+ def banksy_cluster_banksy(
338
+ input_rdata_path: Path,
339
+ output_rdata_path: Path,
340
+ resolution: float = 0.8,
341
+ method: str = "leiden",
342
+ verbose: bool = True,
343
+ seed: int = 123,
344
+ ) -> Dict[str, Any]:
345
+ """
346
+ Performs clustering on a BanksyObject or Seurat object after BANKSY analysis.
347
+
348
+ This tool takes an RData file containing a Seurat or BanksyObject (typically
349
+ after `runBanksy`), performs clustering using the specified method and resolution,
350
+ and saves the updated object to a new RData file.
351
+
352
+ Requires R and the 'banksy' and 'Seurat' R packages to be installed
353
+ and accessible in the environment.
354
+
355
+ Args:
356
+ input_rdata_path: Path to the input .RData file containing a Seurat or BanksyObject.
357
+ output_rdata_path: Path where the updated Seurat/BanksyObject will be saved as an .RData file.
358
+ resolution: Resolution parameter for clustering.
359
+ method: Clustering method to use (e.g., "leiden", "louvain").
360
+ verbose: If TRUE, print messages during execution.
361
+ seed: Random seed for reproducibility.
362
+
363
+ Returns:
364
+ A dictionary containing execution details:
365
+ - command_executed: The Rscript command and generated R script path.
366
+ - stdout: Standard output from the Rscript execution.
367
+ - stderr: Standard error from the Rscript execution.
368
+ - output_files: A list of paths to generated output files.
369
+ """
370
+ # 1. Input validation
371
+ if not input_rdata_path.exists():
372
+ raise FileNotFoundError(f"Input RData file not found: {input_rdata_path}")
373
+ if not input_rdata_path.is_file():
374
+ raise ValueError(f"Input RData path is not a file: {input_rdata_path}")
375
+ if output_rdata_path.suffix.lower() != ".rdata":
376
+ raise ValueError(f"Output RData file must have a .RData extension: {output_rdata_path}")
377
+ if resolution <= 0:
378
+ raise ValueError("resolution must be a positive float.")
379
+ if method not in ["leiden", "louvain"]:
380
+ raise ValueError(f"Unsupported clustering method: {method}. Choose from 'leiden', 'louvain'.")
381
+
382
+ # Ensure output directory exists
383
+ output_rdata_path.parent.mkdir(parents=True, exist_ok=True)
384
+
385
+ # 2. Generate R script content
386
+ r_script_content = f"""
387
+ # Load required packages
388
+ library(Seurat)
389
+ library(banksy)
390
+
391
+ # Set random seed for reproducibility
392
+ set.seed({seed})
393
+
394
+ # Define input and output paths
395
+ input_obj_path <- "{input_rdata_path.as_posix()}"
396
+ output_obj_path <- "{output_rdata_path.as_posix()}"
397
+
398
+ # Check if input file exists
399
+ if (!file.exists(input_obj_path)) {{
400
+ stop(paste("Input RData file not found:", input_obj_path))
401
+ }}
402
+
403
+ # Load the object
404
+ loaded_env <- new.env()
405
+ load(input_obj_path, envir = loaded_env)
406
+
407
+ obj <- NULL
408
+ for (var_name in ls(loaded_env)) {{
409
+ candidate <- get(var_name, envir = loaded_env)
410
+ if (inherits(candidate, "Seurat") || inherits(candidate, "BanksyObject")) {{
411
+ obj <- candidate
412
+ break
413
+ }}
414
+ }}
415
+
416
+ if (is.null(obj)) {{
417
+ stop("No Seurat or BanksyObject found in the input RData file.")
418
+ }}
419
+
420
+ message("Clustering banksy object with parameters:")
421
+ message(paste(" resolution:", {resolution}))
422
+ message(paste(" method:", "{method}"))
423
+ message(paste(" verbose:", {str(verbose).upper()}))
424
+ message(paste(" seed:", {seed}))
425
+
426
+ # Cluster BANKSY object
427
+ obj <- clusterBanksy(
428
+ object = obj,
429
+ resolution = {resolution},
430
+ method = "{method}",
431
+ verbose = {str(verbose).upper()}
432
+ )
433
+
434
+ # Save the updated object
435
+ save(obj, file = output_obj_path)
436
+
437
+ message(paste("Updated object saved to:", output_obj_path))
438
+ """
439
+
440
+ temp_r_script_path: Optional[Path] = None
441
+ try:
442
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".R") as temp_r_script:
443
+ temp_r_script.write(r_script_content)
444
+ temp_r_script_path = Path(temp_r_script.name)
445
+
446
+ command = ["Rscript", str(temp_r_script_path)]
447
+
448
+ # 3. Subprocess execution
449
+ process = subprocess.run(
450
+ command,
451
+ check=True,
452
+ capture_output=True,
453
+ text=True,
454
+ env=os.environ
455
+ )
456
+
457
+ stdout = process.stdout
458
+ stderr = process.stderr
459
+
460
+ # 4. Error handling: Check for R-specific errors in stderr
461
+ if "Error" in stderr or "stop(" in stderr:
462
+ raise RuntimeError(f"R script execution failed. Stderr: {stderr}")
463
+
464
+ if not output_rdata_path.exists():
465
+ raise RuntimeError(f"Output file was not created by R script: {output_rdata_path}")
466
+
467
+ return {
468
+ "command_executed": " ".join(command),
469
+ "stdout": stdout,
470
+ "stderr": stderr,
471
+ "output_files": [str(output_rdata_path)],
472
+ }
473
+
474
+ except FileNotFoundError:
475
+ raise RuntimeError("Rscript command not found. Is R installed and in your PATH?")
476
+ except subprocess.CalledProcessError as e:
477
+ raise RuntimeError(
478
+ f"Rscript execution failed with exit code {e.returncode}.\n"
479
+ f"Command: {' '.join(e.cmd)}\n"
480
+ f"Stdout: {e.stdout}\n"
481
+ f"Stderr: {e.stderr}"
482
+ )
483
+ finally:
484
+ # Clean up temporary R script
485
+ if temp_r_script_path and temp_r_script_path.exists():
486
+ os.remove(temp_r_script_path)
487
+
488
+
489
+ @mcp.tool()
490
+ def banksy_spatial_dim_plot(
491
+ input_rdata_path: Path,
492
+ output_plot_path: Path,
493
+ reduction: str = "banksy",
494
+ group_by: str = "banksy_clusters",
495
+ label: bool = True,
496
+ pt_size: float = 1.5,
497
+ verbose: bool = True,
498
+ width: float = 7.0,
499
+ height: float = 7.0,
500
+ units: str = "in",
501
+ dpi: int = 300,
502
+ ) -> Dict[str, Any]:
503
+ """
504
+ Generates a spatial dimensionality plot for a BanksyObject.
505
+
506
+ This tool takes an RData file containing a BanksyObject (typically after
507
+ `runBanksy` and `clusterBanksy`), generates a spatial plot, and saves it
508
+ to an image file (e.g., PNG, PDF).
509
+
510
+ Requires R and the 'banksy', 'Seurat', and 'ggplot2' R packages to be installed
511
+ and accessible in the environment.
512
+
513
+ Args:
514
+ input_rdata_path: Path to the input .RData file containing a BanksyObject.
515
+ output_plot_path: Path where the plot will be saved (e.g., .png, .pdf, .jpeg).
516
+ reduction: Dimensionality reduction to use for plotting (e.g., "banksy", "umap").
517
+ group_by: Feature to group cells by for coloring (e.g., "banksy_clusters").
518
+ label: If TRUE, label clusters on the plot.
519
+ pt_size: Size of the points in the plot.
520
+ verbose: If TRUE, print messages during execution.
521
+ width: Width of the output plot.
522
+ height: Height of the output plot.
523
+ units: Units for width and height ("in", "cm", "mm").
524
+ dpi: Resolution for raster plots (e.g., PNG, JPEG).
525
+
526
+ Returns:
527
+ A dictionary containing execution details:
528
+ - command_executed: The Rscript command and generated R script path.
529
+ - stdout: Standard output from the Rscript execution.
530
+ - stderr: Standard error from the Rscript execution.
531
+ - output_files: A list of paths to generated output files.
532
+ """
533
+ # 1. Input validation
534
+ if not input_rdata_path.exists():
535
+ raise FileNotFoundError(f"Input RData file not found: {input_rdata_path}")
536
+ if not input_rdata_path.is_file():
537
+ raise ValueError(f"Input RData path is not a file: {input_rdata_path}")
538
+
539
+ valid_plot_suffixes = [".png", ".pdf", ".jpeg", ".jpg", ".tiff", ".bmp"]
540
+ if output_plot_path.suffix.lower() not in valid_plot_suffixes:
541
+ raise ValueError(f"Output plot file must have one of the following extensions: {', '.join(valid_plot_suffixes)}")
542
+
543
+ if pt_size <= 0:
544
+ raise ValueError("pt_size must be a positive float.")
545
+ if width <= 0 or height <= 0:
546
+ raise ValueError("Width and height must be positive floats.")
547
+ if units not in ["in", "cm", "mm"]:
548
+ raise ValueError(f"Invalid units: {units}. Choose from 'in', 'cm', 'mm'.")
549
+ if dpi <= 0:
550
+ raise ValueError("DPI must be a positive integer.")
551
+
552
+ # Ensure output directory exists
553
+ output_plot_path.parent.mkdir(parents=True, exist_ok=True)
554
+
555
+ # 2. Generate R script content
556
+ r_script_content = f"""
557
+ # Load required packages
558
+ library(Seurat)
559
+ library(banksy)
560
+ library(ggplot2) # For saving plots
561
+
562
+ # Define input and output paths
563
+ input_obj_path <- "{input_rdata_path.as_posix()}"
564
+ output_plot_path <- "{output_plot_path.as_posix()}"
565
+
566
+ # Check if input file exists
567
+ if (!file.exists(input_obj_path)) {{
568
+ stop(paste("Input RData file not found:", input_obj_path))
569
+ }}
570
+
571
+ # Load the object
572
+ loaded_env <- new.env()
573
+ load(input_obj_path, envir = loaded_env)
574
+
575
+ obj <- NULL
576
+ for (var_name in ls(loaded_env)) {{
577
+ candidate <- get(var_name, envir = loaded_env)
578
+ if (inherits(candidate, "Seurat") || inherits(candidate, "BanksyObject")) {{
579
+ obj <- candidate
580
+ break
581
+ }}
582
+ }}
583
+
584
+ if (is.null(obj)) {{
585
+ stop("No Seurat or BanksyObject found in the input RData file.")
586
+ }}
587
+
588
+ message("Generating spatial dimensionality plot with parameters:")
589
+ message(paste(" reduction:", "{reduction}"))
590
+ message(paste(" group_by:", "{group_by}"))
591
+ message(paste(" label:", {str(label).upper()}))
592
+ message(paste(" pt_size:", {pt_size}))
593
+ message(paste(" verbose:", {str(verbose).upper()}))
594
+
595
+ # Generate plot
596
+ p <- spatialDimPlot(
597
+ object = obj,
598
+ reduction = "{reduction}",
599
+ group.by = "{group_by}",
600
+ label = {str(label).upper()},
601
+ pt.size.factor = {pt_size}, # Note: R parameter is pt.size.factor
602
+ verbose = {str(verbose).upper()}
603
+ )
604
+
605
+ # Save the plot
606
+ ggsave(
607
+ filename = output_plot_path,
608
+ plot = p,
609
+ width = {width},
610
+ height = {height},
611
+ units = "{units}",
612
+ dpi = {dpi}
613
+ )
614
+
615
+ message(paste("Plot saved to:", output_plot_path))
616
+ """
617
+
618
+ temp_r_script_path: Optional[Path] = None
619
+ try:
620
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".R") as temp_r_script:
621
+ temp_r_script.write(r_script_content)
622
+ temp_r_script_path = Path(temp_r_script.name)
623
+
624
+ command = ["Rscript", str(temp_r_script_path)]
625
+
626
+ # 3. Subprocess execution
627
+ process = subprocess.run(
628
+ command,
629
+ check=True,
630
+ capture_output=True,
631
+ text=True,
632
+ env=os.environ
633
+ )
634
+
635
+ stdout = process.stdout
636
+ stderr = process.stderr
637
+
638
+ # 4. Error handling: Check for R-specific errors in stderr
639
+ if "Error" in stderr or "stop(" in stderr:
640
+ raise RuntimeError(f"R script execution failed. Stderr: {stderr}")
641
+
642
+ if not output_plot_path.exists():
643
+ raise RuntimeError(f"Output plot file was not created by R script: {output_plot_path}")
644
+
645
+ return {
646
+ "command_executed": " ".join(command),
647
+ "stdout": stdout,
648
+ "stderr": stderr,
649
+ "output_files": [str(output_plot_path)],
650
+ }
651
+
652
+ except FileNotFoundError:
653
+ raise RuntimeError("Rscript command not found. Is R installed and in your PATH?")
654
+ except subprocess.CalledProcessError as e:
655
+ raise RuntimeError(
656
+ f"Rscript execution failed with exit code {e.returncode}.\n"
657
+ f"Command: {' '.join(e.cmd)}\n"
658
+ f"Stdout: {e.stdout}\n"
659
+ f"Stderr: {e.stderr}"
660
+ )
661
+ finally:
662
+ # Clean up temporary R script
663
+ if temp_r_script_path and temp_r_script_path.exists():
664
+ os.remove(temp_r_script_path)
665
+
666
+ if __name__ == "__main__":
667
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-banksy/app/bioconductor-banksy_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-banksy/app/bioconductor-banksy_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_banksy'
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-banksy/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-banksy/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-banksy:
5
+ build: .
6
+ image: mcp-bioconductor-banksy:latest
7
+ container_name: mcp-bioconductor-banksy
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-banksy
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-banksy/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-banksy
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-banksy/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-benchdamic/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-benchdamic via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-benchdamic -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-benchdamic_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-benchdamic_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-benchdamic_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_server.py ADDED
@@ -0,0 +1,464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ import os
5
+ import logging
6
+ from typing import List, Optional
7
+
8
+ # Set up logging
9
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # MCP decorator is assumed to be available in the execution environment.
13
+ # This is a placeholder for the actual decorator.
14
+ class mcp:
15
+ @staticmethod
16
+ def tool(func):
17
+ def wrapper(*args, **kwargs):
18
+ return func(*args, **kwargs)
19
+ return wrapper
20
+
21
+ @mcp.tool
22
+ def run_benchdamic(
23
+ config_file: Path,
24
+ output_dir: Path,
25
+ ):
26
+ """
27
+ Runs the full benchdamic pipeline.
28
+
29
+ This function serves as the main entry point for the benchdamic workflow,
30
+ executing data generation, method application, evaluation, and plotting
31
+ based on a single YAML configuration file. The process is run within the
32
+ specified output directory.
33
+
34
+ Args:
35
+ config_file: Path to the benchdamic YAML configuration file.
36
+ output_dir: Path to the directory where all outputs will be stored.
37
+ It will be created if it doesn't exist.
38
+ """
39
+ # 1. Input validation
40
+ if not config_file.is_file():
41
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
42
+
43
+ # 2. File path handling
44
+ output_dir.mkdir(parents=True, exist_ok=True)
45
+
46
+ # The R function benchdamic() relies on the 'output_dir' key within the config YAML.
47
+ # By running Rscript from the specified output_dir, we ensure that
48
+ # relative paths in the config file are resolved correctly.
49
+ r_script_content = f"""
50
+ if (!requireNamespace("benchdamic", quietly = TRUE)) {{
51
+ stop("The 'benchdamic' R package is not installed. Please install it from Bioconductor.")
52
+ }}
53
+ library(benchdamic)
54
+
55
+ config_path <- "{config_file.resolve()}"
56
+
57
+ cat("Starting benchdamic pipeline...\\n")
58
+ benchdamic::benchdamic(config_file = config_path)
59
+ cat("benchdamic pipeline finished successfully.\\n")
60
+ """
61
+
62
+ # 3. Subprocess execution
63
+ command_to_execute = []
64
+ stdout_str = ""
65
+ stderr_str = ""
66
+ r_script_path = None
67
+
68
+ try:
69
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, dir=output_dir) as r_script_file:
70
+ r_script_file.write(r_script_content)
71
+ r_script_path = Path(r_script_file.name)
72
+
73
+ # Use relative path for the command since we set cwd
74
+ command_to_execute = ["Rscript", str(r_script_path.name)]
75
+
76
+ files_before = set(os.listdir(output_dir))
77
+
78
+ process = subprocess.run(
79
+ command_to_execute,
80
+ capture_output=True,
81
+ text=True,
82
+ check=True,
83
+ cwd=output_dir,
84
+ )
85
+
86
+ stdout_str = process.stdout
87
+ stderr_str = process.stderr
88
+
89
+ files_after = set(os.listdir(output_dir))
90
+ # Find new files and directories
91
+ new_items = files_after - files_before
92
+ output_files = [str(output_dir / item) for item in new_items]
93
+
94
+ except FileNotFoundError:
95
+ err_msg = "Rscript not found. Please ensure R is installed and 'Rscript' is in your system's PATH."
96
+ logger.error(err_msg)
97
+ raise RuntimeError(err_msg) from None
98
+ except subprocess.CalledProcessError as e:
99
+ logger.error(f"R script execution failed with exit code {e.returncode}")
100
+ logger.error(f"Command: {' '.join(command_to_execute)}")
101
+ logger.error(f"Stdout: {e.stdout}")
102
+ logger.error(f"Stderr: {e.stderr}")
103
+ raise
104
+ finally:
105
+ if r_script_path and r_script_path.exists():
106
+ r_script_path.unlink()
107
+
108
+ # 4. Structured result return
109
+ return {
110
+ "command_executed": " ".join(command_to_execute),
111
+ "stdout": stdout_str,
112
+ "stderr": stderr_str,
113
+ "output_files": output_files
114
+ }
115
+
116
+ @mcp.tool
117
+ def generate_data(
118
+ config_file: Path,
119
+ output_dir: Path,
120
+ ):
121
+ """
122
+ Generates simulated data using the benchdamic configuration.
123
+
124
+ This function corresponds to the `generate_data` step in the benchdamic
125
+ workflow. It reads simulation parameters from the config file and writes
126
+ the generated datasets to the specified output directory.
127
+
128
+ Args:
129
+ config_file: Path to the benchdamic YAML configuration file.
130
+ output_dir: Path to the directory where generated data will be stored.
131
+ It will be created if it doesn't exist.
132
+ """
133
+ # 1. Input validation
134
+ if not config_file.is_file():
135
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
136
+
137
+ # 2. File path handling
138
+ output_dir.mkdir(parents=True, exist_ok=True)
139
+
140
+ r_script_content = f"""
141
+ if (!requireNamespace("benchdamic", quietly = TRUE)) {{
142
+ stop("The 'benchdamic' R package is not installed.")
143
+ }}
144
+ library(benchdamic)
145
+
146
+ config_path <- "{config_file.resolve()}"
147
+ output_path <- "{output_dir.resolve()}"
148
+
149
+ cat("Starting data generation...\\n")
150
+ benchdamic::generate_data(config_file = config_path, output_dir = output_path)
151
+ cat("Data generation finished successfully.\\n")
152
+ """
153
+
154
+ # 3. Subprocess execution
155
+ command_to_execute = []
156
+ stdout_str = ""
157
+ stderr_str = ""
158
+ r_script_path = None
159
+
160
+ try:
161
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file:
162
+ r_script_file.write(r_script_content)
163
+ r_script_path = Path(r_script_file.name)
164
+
165
+ command_to_execute = ["Rscript", str(r_script_path)]
166
+
167
+ files_before = set(p.resolve() for p in output_dir.glob('**/*'))
168
+
169
+ process = subprocess.run(
170
+ command_to_execute,
171
+ capture_output=True,
172
+ text=True,
173
+ check=True,
174
+ )
175
+
176
+ stdout_str = process.stdout
177
+ stderr_str = process.stderr
178
+
179
+ files_after = set(p.resolve() for p in output_dir.glob('**/*'))
180
+ new_files = [str(p) for p in files_after - files_before]
181
+
182
+ except FileNotFoundError:
183
+ err_msg = "Rscript not found. Please ensure R is installed and 'Rscript' is in your system's PATH."
184
+ logger.error(err_msg)
185
+ raise RuntimeError(err_msg) from None
186
+ except subprocess.CalledProcessError as e:
187
+ logger.error(f"R script execution failed with exit code {e.returncode}")
188
+ logger.error(f"Command: {' '.join(command_to_execute)}")
189
+ logger.error(f"Stdout: {e.stdout}")
190
+ logger.error(f"Stderr: {e.stderr}")
191
+ raise
192
+ finally:
193
+ if r_script_path and r_script_path.exists():
194
+ r_script_path.unlink()
195
+
196
+ # 4. Structured result return
197
+ return {
198
+ "command_executed": " ".join(command_to_execute),
199
+ "stdout": stdout_str,
200
+ "stderr": stderr_str,
201
+ "output_files": new_files
202
+ }
203
+
204
+ @mcp.tool
205
+ def run_methods(
206
+ config_file: Path,
207
+ output_dir: Path,
208
+ ):
209
+ """
210
+ Runs differential abundance methods on generated data.
211
+
212
+ This function corresponds to the `run_methods` step in the benchdamic
213
+ workflow. It applies the specified methods to the datasets found in the
214
+ output directory and saves their results.
215
+
216
+ Args:
217
+ config_file: Path to the benchdamic YAML configuration file.
218
+ output_dir: Path to the directory containing the generated data and
219
+ where method results will be stored. It must exist.
220
+ """
221
+ # 1. Input validation
222
+ if not config_file.is_file():
223
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
224
+ if not output_dir.is_dir():
225
+ raise FileNotFoundError(f"Output directory not found: {output_dir}. Please run generate_data first.")
226
+
227
+ # 2. R script content
228
+ r_script_content = f"""
229
+ if (!requireNamespace("benchdamic", quietly = TRUE)) {{
230
+ stop("The 'benchdamic' R package is not installed.")
231
+ }}
232
+ library(benchdamic)
233
+
234
+ config_path <- "{config_file.resolve()}"
235
+ output_path <- "{output_dir.resolve()}"
236
+
237
+ cat("Running differential abundance methods...\\n")
238
+ benchdamic::run_methods(config_file = config_path, output_dir = output_path)
239
+ cat("Method execution finished successfully.\\n")
240
+ """
241
+
242
+ # 3. Subprocess execution
243
+ command_to_execute = []
244
+ stdout_str = ""
245
+ stderr_str = ""
246
+ r_script_path = None
247
+
248
+ try:
249
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file:
250
+ r_script_file.write(r_script_content)
251
+ r_script_path = Path(r_script_file.name)
252
+
253
+ command_to_execute = ["Rscript", str(r_script_path)]
254
+
255
+ files_before = set(p.resolve() for p in output_dir.glob('**/*'))
256
+
257
+ process = subprocess.run(
258
+ command_to_execute,
259
+ capture_output=True,
260
+ text=True,
261
+ check=True,
262
+ )
263
+
264
+ stdout_str = process.stdout
265
+ stderr_str = process.stderr
266
+
267
+ files_after = set(p.resolve() for p in output_dir.glob('**/*'))
268
+ new_files = [str(p) for p in files_after - files_before]
269
+
270
+ except FileNotFoundError:
271
+ err_msg = "Rscript not found. Please ensure R is installed and 'Rscript' is in your system's PATH."
272
+ logger.error(err_msg)
273
+ raise RuntimeError(err_msg) from None
274
+ except subprocess.CalledProcessError as e:
275
+ logger.error(f"R script execution failed with exit code {e.returncode}")
276
+ logger.error(f"Command: {' '.join(command_to_execute)}")
277
+ logger.error(f"Stdout: {e.stdout}")
278
+ logger.error(f"Stderr: {e.stderr}")
279
+ raise
280
+ finally:
281
+ if r_script_path and r_script_path.exists():
282
+ r_script_path.unlink()
283
+
284
+ # 4. Structured result return
285
+ return {
286
+ "command_executed": " ".join(command_to_execute),
287
+ "stdout": stdout_str,
288
+ "stderr": stderr_str,
289
+ "output_files": new_files
290
+ }
291
+
292
+ @mcp.tool
293
+ def evaluate_methods(
294
+ config_file: Path,
295
+ output_dir: Path,
296
+ ):
297
+ """
298
+ Evaluates the performance of differential abundance methods.
299
+
300
+ This function corresponds to the `evaluate_methods` step in the benchdamic
301
+ workflow. It computes performance metrics based on the method results
302
+ and ground truth, saving the evaluation to the output directory.
303
+
304
+ Args:
305
+ config_file: Path to the benchdamic YAML configuration file.
306
+ output_dir: Path to the directory containing method results. It must exist.
307
+ """
308
+ # 1. Input validation
309
+ if not config_file.is_file():
310
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
311
+ if not output_dir.is_dir():
312
+ raise FileNotFoundError(f"Output directory not found: {output_dir}.")
313
+
314
+ # 2. R script content
315
+ r_script_content = f"""
316
+ if (!requireNamespace("benchdamic", quietly = TRUE)) {{
317
+ stop("The 'benchdamic' R package is not installed.")
318
+ }}
319
+ library(benchdamic)
320
+
321
+ config_path <- "{config_file.resolve()}"
322
+ output_path <- "{output_dir.resolve()}"
323
+
324
+ cat("Evaluating method performance...\\n")
325
+ benchdamic::evaluate_methods(config_file = config_path, output_dir = output_path)
326
+ cat("Evaluation finished successfully.\\n")
327
+ """
328
+
329
+ # 3. Subprocess execution
330
+ command_to_execute = []
331
+ stdout_str = ""
332
+ stderr_str = ""
333
+ r_script_path = None
334
+
335
+ try:
336
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file:
337
+ r_script_file.write(r_script_content)
338
+ r_script_path = Path(r_script_file.name)
339
+
340
+ command_to_execute = ["Rscript", str(r_script_path)]
341
+
342
+ files_before = set(p.resolve() for p in output_dir.glob('**/*'))
343
+
344
+ process = subprocess.run(
345
+ command_to_execute,
346
+ capture_output=True,
347
+ text=True,
348
+ check=True,
349
+ )
350
+
351
+ stdout_str = process.stdout
352
+ stderr_str = process.stderr
353
+
354
+ files_after = set(p.resolve() for p in output_dir.glob('**/*'))
355
+ new_files = [str(p) for p in files_after - files_before]
356
+
357
+ except FileNotFoundError:
358
+ err_msg = "Rscript not found. Please ensure R is installed and 'Rscript' is in your system's PATH."
359
+ logger.error(err_msg)
360
+ raise RuntimeError(err_msg) from None
361
+ except subprocess.CalledProcessError as e:
362
+ logger.error(f"R script execution failed with exit code {e.returncode}")
363
+ logger.error(f"Command: {' '.join(command_to_execute)}")
364
+ logger.error(f"Stdout: {e.stdout}")
365
+ logger.error(f"Stderr: {e.stderr}")
366
+ raise
367
+ finally:
368
+ if r_script_path and r_script_path.exists():
369
+ r_script_path.unlink()
370
+
371
+ # 4. Structured result return
372
+ return {
373
+ "command_executed": " ".join(command_to_execute),
374
+ "stdout": stdout_str,
375
+ "stderr": stderr_str,
376
+ "output_files": new_files
377
+ }
378
+
379
+ @mcp.tool
380
+ def plot_results(
381
+ config_file: Path,
382
+ output_dir: Path,
383
+ ):
384
+ """
385
+ Generates plots from the evaluation results.
386
+
387
+ This function corresponds to the `plot_results` step in the benchdamic
388
+ workflow. It creates various visualizations of the performance metrics
389
+ and saves them as files in the output directory.
390
+
391
+ Args:
392
+ config_file: Path to the benchdamic YAML configuration file.
393
+ output_dir: Path to the directory containing evaluation results. It must exist.
394
+ """
395
+ # 1. Input validation
396
+ if not config_file.is_file():
397
+ raise FileNotFoundError(f"Configuration file not found: {config_file}")
398
+ if not output_dir.is_dir():
399
+ raise FileNotFoundError(f"Output directory not found: {output_dir}.")
400
+
401
+ # 2. R script content
402
+ r_script_content = f"""
403
+ if (!requireNamespace("benchdamic", quietly = TRUE)) {{
404
+ stop("The 'benchdamic' R package is not installed.")
405
+ }}
406
+ library(benchdamic)
407
+
408
+ config_path <- "{config_file.resolve()}"
409
+ output_path <- "{output_dir.resolve()}"
410
+
411
+ cat("Generating plots...\\n")
412
+ benchdamic::plot_results(config_file = config_path, output_dir = output_path)
413
+ cat("Plot generation finished successfully.\\n")
414
+ """
415
+
416
+ # 3. Subprocess execution
417
+ command_to_execute = []
418
+ stdout_str = ""
419
+ stderr_str = ""
420
+ r_script_path = None
421
+
422
+ try:
423
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file:
424
+ r_script_file.write(r_script_content)
425
+ r_script_path = Path(r_script_file.name)
426
+
427
+ command_to_execute = ["Rscript", str(r_script_path)]
428
+
429
+ files_before = set(p.resolve() for p in output_dir.glob('**/*'))
430
+
431
+ process = subprocess.run(
432
+ command_to_execute,
433
+ capture_output=True,
434
+ text=True,
435
+ check=True,
436
+ )
437
+
438
+ stdout_str = process.stdout
439
+ stderr_str = process.stderr
440
+
441
+ files_after = set(p.resolve() for p in output_dir.glob('**/*'))
442
+ new_files = [str(p) for p in files_after - files_before]
443
+
444
+ except FileNotFoundError:
445
+ err_msg = "Rscript not found. Please ensure R is installed and 'Rscript' is in your system's PATH."
446
+ logger.error(err_msg)
447
+ raise RuntimeError(err_msg) from None
448
+ except subprocess.CalledProcessError as e:
449
+ logger.error(f"R script execution failed with exit code {e.returncode}")
450
+ logger.error(f"Command: {' '.join(command_to_execute)}")
451
+ logger.error(f"Stdout: {e.stdout}")
452
+ logger.error(f"Stderr: {e.stderr}")
453
+ raise
454
+ finally:
455
+ if r_script_path and r_script_path.exists():
456
+ r_script_path.unlink()
457
+
458
+ # 4. Structured result return
459
+ return {
460
+ "command_executed": " ".join(command_to_execute),
461
+ "stdout": stdout_str,
462
+ "stderr": stderr_str,
463
+ "output_files": new_files
464
+ }
Biomni/mcp_generated/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_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-benchdamic/app/bioconductor-benchdamic_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_benchdamic'
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-benchdamic/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-benchdamic:
5
+ build: .
6
+ image: mcp-bioconductor-benchdamic:latest
7
+ container_name: mcp-bioconductor-benchdamic
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-benchdamic
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-benchdamic/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-benchdamic
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-benchdamic/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-catscradle/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-catscradle via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-catscradle -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-catscradle_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-catscradle_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-catscradle_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-catscradle/app/bioconductor-catscradle_server.py ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Union
4
+ import tempfile
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ SERVER_NAME = 'local_bioconductor_catscradle'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def catscradle_build_neighborhoods(
13
+ input_rds: str,
14
+ output_rds: str,
15
+ radius: float = 50.0,
16
+ cell_type_col: str = "cell_type",
17
+ assay: str = "Spatial",
18
+ slot: str = "counts",
19
+ min_cells: int = 1
20
+ ):
21
+ """
22
+ Identifies tissue neighborhoods in spatial transcriptomics data and produces a Seurat object
23
+ where individual elements are neighborhoods rather than cells.
24
+
25
+ Args:
26
+ input_rds: Path to the input Seurat or SpatialExperiment object (RDS format).
27
+ output_rds: Path where the resulting neighborhood Seurat object will be saved.
28
+ radius: The radius (in spatial units) defining the contiguous region surrounding individual cells.
29
+ cell_type_col: Metadata column name containing cell type annotations.
30
+ assay: Assay to use from the input object.
31
+ slot: Slot to use (e.g., counts, data).
32
+ min_cells: Minimum number of cells required to define a neighborhood.
33
+ """
34
+ input_path = Path(input_rds)
35
+ output_path = Path(output_rds)
36
+
37
+ if not input_path.exists():
38
+ return {"error": f"Input file not found: {input_rds}"}
39
+
40
+ if radius <= 0:
41
+ return {"error": "Radius must be a positive float."}
42
+
43
+ # R script to perform neighborhood construction
44
+ r_code = f"""
45
+ library(CatsCradle)
46
+ library(Seurat)
47
+ library(SpatialExperiment)
48
+
49
+ # Load data
50
+ input_data <- readRDS("{input_path}")
51
+
52
+ # Check if it's SpatialExperiment and convert if necessary
53
+ if (inherits(input_data, "SpatialExperiment")) {{
54
+ # CatsCradle handles SE/SPE, but we ensure Seurat compatibility if needed
55
+ # Internal CatsCradle functions typically take these objects
56
+ }}
57
+
58
+ # Core CatsCradle logic for neighborhood construction
59
+ # Based on package description: "produces Seurat objects whose individual elements are neighborhoods"
60
+ # We use the primary constructor function (inferred from documentation)
61
+ neighborhood_obj <- CatsCradle::spatialToNeighborhoods(
62
+ object = input_data,
63
+ radius = {radius},
64
+ cell_type_col = "{cell_type_col}",
65
+ assay = "{assay}",
66
+ slot = "{slot}",
67
+ min_cells = {min_cells}
68
+ )
69
+
70
+ saveRDS(neighborhood_obj, "{output_path}")
71
+ """
72
+
73
+ try:
74
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
75
+ tmp.write(r_code)
76
+ tmp_path = tmp.name
77
+
78
+ cmd = ["Rscript", tmp_path]
79
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
80
+
81
+ return {
82
+ "command_executed": " ".join(cmd),
83
+ "stdout": result.stdout,
84
+ "stderr": result.stderr,
85
+ "output_files": [str(output_path)]
86
+ }
87
+ except subprocess.CalledProcessError as e:
88
+ return {
89
+ "error": "R execution failed",
90
+ "stdout": e.stdout,
91
+ "stderr": e.stderr,
92
+ "command_executed": " ".join(e.cmd)
93
+ }
94
+ finally:
95
+ if 'tmp_path' in locals() and Path(tmp_path).exists():
96
+ Path(tmp_path).unlink()
97
+
98
+ @mcp.tool()
99
+ def catscradle_gene_centric_analysis(
100
+ input_rds: str,
101
+ output_rds: str,
102
+ assay: str = "RNA",
103
+ slot: str = "data",
104
+ variable_features_only: bool = True,
105
+ n_features: int = 2000
106
+ ):
107
+ """
108
+ Enables the categorisation and annotation of genes by producing Seurat objects
109
+ whose elements are genes rather than cells.
110
+
111
+ Args:
112
+ input_rds: Path to the input Seurat object (RDS format).
113
+ output_rds: Path where the gene-centric Seurat object will be saved.
114
+ assay: Assay to use for gene expression data.
115
+ slot: Slot to use (e.g., data, scale.data).
116
+ variable_features_only: Whether to only include highly variable features.
117
+ n_features: Number of features to select if variable_features_only is True.
118
+ """
119
+ input_path = Path(input_rds)
120
+ output_path = Path(output_rds)
121
+
122
+ if not input_path.exists():
123
+ return {"error": f"Input file not found: {input_rds}"}
124
+
125
+ r_code = f"""
126
+ library(CatsCradle)
127
+ library(Seurat)
128
+
129
+ input_data <- readRDS("{input_path}")
130
+
131
+ # Core CatsCradle logic for gene-centric Seurat object creation
132
+ gene_obj <- CatsCradle::geneCentricSeurat(
133
+ object = input_data,
134
+ assay = "{assay}",
135
+ slot = "{slot}",
136
+ variable_features_only = {"TRUE" if variable_features_only else "FALSE"},
137
+ n_features = {n_features}
138
+ )
139
+
140
+ saveRDS(gene_obj, "{output_path}")
141
+ """
142
+
143
+ try:
144
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
145
+ tmp.write(r_code)
146
+ tmp_path = tmp.name
147
+
148
+ cmd = ["Rscript", tmp_path]
149
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
150
+
151
+ return {
152
+ "command_executed": " ".join(cmd),
153
+ "stdout": result.stdout,
154
+ "stderr": result.stderr,
155
+ "output_files": [str(output_path)]
156
+ }
157
+ except subprocess.CalledProcessError as e:
158
+ return {
159
+ "error": "R execution failed",
160
+ "stdout": e.stdout,
161
+ "stderr": e.stderr,
162
+ "command_executed": " ".join(e.cmd)
163
+ }
164
+ finally:
165
+ if 'tmp_path' in locals() and Path(tmp_path).exists():
166
+ Path(tmp_path).unlink()
167
+
168
+ @mcp.tool()
169
+ def catscradle_tag_neighborhoods(
170
+ neighborhood_rds: str,
171
+ output_rds: str,
172
+ tag_by: str = "cell_type",
173
+ threshold: float = 0.1
174
+ ):
175
+ """
176
+ Categorizes neighborhoods by the cell types contained in them or the genes expressed in them.
177
+
178
+ Args:
179
+ neighborhood_rds: Path to the neighborhood Seurat object created by catscradle_build_neighborhoods.
180
+ output_rds: Path to save the tagged neighborhood object.
181
+ tag_by: Attribute to tag by ('cell_type' or 'gene_expression').
182
+ threshold: Minimum proportion or expression level to consider a tag present in a neighborhood.
183
+ """
184
+ input_path = Path(neighborhood_rds)
185
+ output_path = Path(output_rds)
186
+
187
+ if not input_path.exists():
188
+ return {"error": f"Input file not found: {neighborhood_rds}"}
189
+
190
+ r_code = f"""
191
+ library(CatsCradle)
192
+ library(Seurat)
193
+
194
+ nb_obj <- readRDS("{input_path}")
195
+
196
+ # Categorize neighborhoods
197
+ nb_obj <- CatsCradle::tagNeighborhoods(
198
+ object = nb_obj,
199
+ tag_by = "{tag_by}",
200
+ threshold = {threshold}
201
+ )
202
+
203
+ saveRDS(nb_obj, "{output_path}")
204
+ """
205
+
206
+ try:
207
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
208
+ tmp.write(r_code)
209
+ tmp_path = tmp.name
210
+
211
+ cmd = ["Rscript", tmp_path]
212
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
213
+
214
+ return {
215
+ "command_executed": " ".join(cmd),
216
+ "stdout": result.stdout,
217
+ "stderr": result.stderr,
218
+ "output_files": [str(output_path)]
219
+ }
220
+ except subprocess.CalledProcessError as e:
221
+ return {
222
+ "error": "R execution failed",
223
+ "stdout": e.stdout,
224
+ "stderr": e.stderr,
225
+ "command_executed": " ".join(e.cmd)
226
+ }
227
+ finally:
228
+ if 'tmp_path' in locals() and Path(tmp_path).exists():
229
+ Path(tmp_path).unlink()
230
+
231
+ @mcp.tool()
232
+ def catscradle_spatial_vignette_pipeline(
233
+ input_rds: str,
234
+ output_prefix: str,
235
+ radius: float = 50.0,
236
+ cell_type_col: str = "cell_type"
237
+ ):
238
+ """
239
+ Runs a standard CatsCradle spatial analysis pipeline, including neighborhood construction
240
+ and gene clustering, as described in the package vignettes.
241
+
242
+ Args:
243
+ input_rds: Path to the input spatial transcriptomics RDS file.
244
+ output_prefix: Prefix for output files (e.g., 'results/sample1').
245
+ radius: Radius for neighborhood definition.
246
+ cell_type_col: Metadata column for cell types.
247
+ """
248
+ input_path = Path(input_rds)
249
+ out_dir = Path(output_prefix).parent
250
+ if not out_dir.exists():
251
+ out_dir.mkdir(parents=True, exist_ok=True)
252
+
253
+ r_code = f"""
254
+ library(CatsCradle)
255
+ library(Seurat)
256
+
257
+ # 1. Load Data
258
+ spatial_data <- readRDS("{input_path}")
259
+
260
+ # 2. Build Neighborhoods
261
+ nb_obj <- CatsCradle::spatialToNeighborhoods(
262
+ object = spatial_data,
263
+ radius = {radius},
264
+ cell_type_col = "{cell_type_col}"
265
+ )
266
+ saveRDS(nb_obj, paste0("{output_prefix}", "_neighborhoods.rds"))
267
+
268
+ # 3. Gene Centric Analysis
269
+ gene_obj <- CatsCradle::geneCentricSeurat(spatial_data)
270
+ saveRDS(gene_obj, paste0("{output_prefix}", "_gene_centric.rds"))
271
+
272
+ # 4. Discover Gene Clusters (Internal CatsCradle method)
273
+ # This typically involves standard Seurat clustering on the gene-centric object
274
+ gene_obj <- Seurat::FindVariableFeatures(gene_obj)
275
+ gene_obj <- Seurat::ScaleData(gene_obj)
276
+ gene_obj <- Seurat::RunPCA(gene_obj)
277
+ gene_obj <- Seurat::FindNeighbors(gene_obj)
278
+ gene_obj <- Seurat::FindClusters(gene_obj)
279
+ saveRDS(gene_obj, paste0("{output_prefix}", "_gene_clusters.rds"))
280
+ """
281
+
282
+ try:
283
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
284
+ tmp.write(r_code)
285
+ tmp_path = tmp.name
286
+
287
+ cmd = ["Rscript", tmp_path]
288
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
289
+
290
+ return {
291
+ "command_executed": " ".join(cmd),
292
+ "stdout": result.stdout,
293
+ "stderr": result.stderr,
294
+ "output_files": [
295
+ f"{output_prefix}_neighborhoods.rds",
296
+ f"{output_prefix}_gene_centric.rds",
297
+ f"{output_prefix}_gene_clusters.rds"
298
+ ]
299
+ }
300
+ except subprocess.CalledProcessError as e:
301
+ return {
302
+ "error": "R execution failed",
303
+ "stdout": e.stdout,
304
+ "stderr": e.stderr,
305
+ "command_executed": " ".join(e.cmd)
306
+ }
307
+ finally:
308
+ if 'tmp_path' in locals() and Path(tmp_path).exists():
309
+ Path(tmp_path).unlink()
310
+
311
+ if __name__ == "__main__":
312
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-catscradle/app/bioconductor-catscradle_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-catscradle/app/bioconductor-catscradle_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_catscradle'
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-catscradle/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-catscradle/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-catscradle:
5
+ build: .
6
+ image: mcp-bioconductor-catscradle:latest
7
+ container_name: mcp-bioconductor-catscradle
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-catscradle
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-catscradle/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-catscradle
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-catscradle/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-clustifyr/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-clustifyr via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-clustifyr -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY bioconductor-clustifyr_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-clustifyr_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-clustifyr_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-clustifyr/app/bioconductor-clustifyr_server.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import shlex
3
+ from pathlib import Path
4
+ from typing import Optional, List, Dict, Any
5
+
6
+ # @mcp.tool() is a placeholder for the actual decorator in the MCP framework.
7
+ # The code is written to be compatible with it.
8
+
9
+ from mcp.server.fastmcp import FastMCP
10
+
11
+ SERVER_NAME = 'local_bioconductor_clustifyr'
12
+ mcp = FastMCP(SERVER_NAME)
13
+
14
+ @mcp.tool()
15
+ def clustifyr_rscript(
16
+ script_file: Optional[Path] = None,
17
+ expressions: Optional[List[str]] = None,
18
+ script_args: Optional[List[str]] = None,
19
+ verbose: bool = False,
20
+ default_packages: Optional[str] = None,
21
+ save: bool = False,
22
+ no_environ: bool = False,
23
+ no_site_file: bool = False,
24
+ no_init_file: bool = False,
25
+ restore: bool = False,
26
+ vanilla: bool = False,
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Executes an R script using Rscript, for running Bioconductor packages like clustifyr.
30
+
31
+ This tool serves as a wrapper around the Rscript command-line interpreter,
32
+ allowing the execution of R scripts that utilize the clustifyr library or other
33
+ Bioconductor packages. You can either provide an R script file or a list of
34
+ R expressions to execute.
35
+
36
+ Args:
37
+ script_file: Path to the R script file to be executed. Mutually exclusive with 'expressions'.
38
+ expressions: A list of R expressions to execute. Mutually exclusive with 'script_file'.
39
+ script_args: A list of arguments to be passed to the R script itself.
40
+ verbose: If True, print information on progress.
41
+ default_packages: A comma-separated list of package names to be loaded by default (e.g., "clustifyr,Seurat").
42
+ save: If True, save the workspace at the end of the session. Ignored if 'vanilla' is True.
43
+ no_environ: If True, do not read the site and user environment files. Ignored if 'vanilla' is True.
44
+ no_site_file: If True, do not read the site-wide Rprofile. Ignored if 'vanilla' is True.
45
+ no_init_file: If True, do not read the user R profile. Ignored if 'vanilla' is True.
46
+ restore: If True, restore previously saved objects at startup. Ignored if 'vanilla' is True.
47
+ vanilla: If True, combines --no-save, --no-restore, --no-site-file, --no-init-file, and --no-environ.
48
+
49
+ Returns:
50
+ A dictionary containing the executed command, stdout, stderr, and a list of output files.
51
+ Note: Output files are not automatically detected and must be known from the R script's logic.
52
+ """
53
+ # 1. Input Validation
54
+ if not script_file and not expressions:
55
+ raise ValueError("Either 'script_file' or 'expressions' must be provided.")
56
+ if script_file and expressions:
57
+ raise ValueError("'script_file' and 'expressions' are mutually exclusive and cannot be used together.")
58
+ if script_file:
59
+ if not script_file.is_file():
60
+ raise FileNotFoundError(f"The specified script file does not exist: {script_file}")
61
+
62
+ # 2. Command Construction
63
+ cmd = ["Rscript"]
64
+
65
+ # The --vanilla option is a shortcut for several other flags.
66
+ if vanilla:
67
+ cmd.append("--vanilla")
68
+ else:
69
+ if save:
70
+ cmd.append("--save")
71
+ if no_environ:
72
+ cmd.append("--no-environ")
73
+ if no_site_file:
74
+ cmd.append("--no-site-file")
75
+ if no_init_file:
76
+ cmd.append("--no-init-file")
77
+ if restore:
78
+ cmd.append("--restore")
79
+
80
+ if verbose:
81
+ cmd.append("--verbose")
82
+
83
+ if default_packages:
84
+ cmd.extend(["--default-packages", default_packages])
85
+
86
+ # Add the script file or expressions to execute
87
+ if script_file:
88
+ cmd.append(str(script_file))
89
+ elif expressions:
90
+ for expr in expressions:
91
+ cmd.extend(["-e", expr])
92
+
93
+ # Add any additional arguments for the R script itself
94
+ if script_args:
95
+ cmd.extend(script_args)
96
+
97
+ command_executed = shlex.join(cmd)
98
+
99
+ # 3. Subprocess Execution
100
+ try:
101
+ result = subprocess.run(
102
+ cmd,
103
+ check=True,
104
+ capture_output=True,
105
+ text=True,
106
+ )
107
+ # 4. Structured Result Return
108
+ return {
109
+ "command_executed": command_executed,
110
+ "stdout": result.stdout,
111
+ "stderr": result.stderr,
112
+ "output_files": [] # Rscript does not have a defined output file parameter.
113
+ }
114
+ except FileNotFoundError:
115
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
116
+ except subprocess.CalledProcessError as e:
117
+ # Handle cases where the R script fails
118
+ return {
119
+ "command_executed": command_executed,
120
+ "stdout": e.stdout,
121
+ "stderr": e.stderr,
122
+ "error": f"Rscript execution failed with return code {e.returncode}",
123
+ "output_files": []
124
+ }
125
+
126
+ if __name__ == "__main__":
127
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-clustifyr/app/bioconductor-clustifyr_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-clustifyr/app/bioconductor-clustifyr_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_clustifyr'
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-clustifyr/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-clustifyr:
5
+ build: .
6
+ image: mcp-bioconductor-clustifyr:latest
7
+ container_name: mcp-bioconductor-clustifyr
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-clustifyr
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-clustifyr/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-clustifyr
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-clustifyr/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/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-genomicfeatures via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-genomicfeatures -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-genomicfeatures_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-genomicfeatures_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-genomicfeatures_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/app/bioconductor-genomicfeatures_server.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shlex
4
+ from pathlib import Path
5
+ from typing import Optional, Dict, List
6
+
7
+ # @mcp.tool() is a placeholder for the actual decorator.
8
+ # The code is written to be compatible with the MCP framework.
9
+
10
+ from mcp.server.fastmcp import FastMCP
11
+
12
+ SERVER_NAME = 'local_bioconductor_genomicfeatures'
13
+ mcp = FastMCP(SERVER_NAME)
14
+
15
+ @mcp.tool()
16
+ def make_txdb_from_gff(
17
+ gff_file: Path,
18
+ output_db_path: Path,
19
+ format: Optional[str] = None,
20
+ data_source: Optional[str] = None,
21
+ organism: Optional[str] = None,
22
+ taxonomy_id: Optional[int] = None,
23
+ chrominfo_file: Optional[Path] = None,
24
+ metadata_file: Optional[Path] = None,
25
+ pruning_mode: str = "coarse",
26
+ on_foreign_chrom: str = "abort",
27
+ infer_cds: bool = False,
28
+ infer_utr: bool = True,
29
+ ) -> Dict:
30
+ """
31
+ Creates a TranscriptDb (TxDb) SQLite database from a GFF or GTF file.
32
+
33
+ This tool serves as a wrapper for the `makeTxDbFromGFF` function from the
34
+ R/Bioconductor GenomicFeatures package. It parses a gene annotation file
35
+ (GFF3 or GTF) and stores the transcript metadata in a structured SQLite
36
+ database, which is highly efficient for downstream analysis.
37
+
38
+ Args:
39
+ gff_file: Path to the input GFF3 or GTF file.
40
+ output_db_path: Path where the output SQLite TxDb file will be saved.
41
+ format: The format of the input file. Can be 'gff3' or 'gtf'.
42
+ If not provided, the tool attempts to infer it from the file extension.
43
+ data_source: A string describing the source of the data (e.g., "Ensembl v104").
44
+ organism: The scientific name of the organism (e.g., "Homo sapiens").
45
+ taxonomy_id: The NCBI Taxonomy ID for the organism.
46
+ chrominfo_file: Optional path to a two-column, tab-separated file containing
47
+ chromosome names and their lengths.
48
+ metadata_file: Optional path to a two-column, tab-separated file containing
49
+ metadata as name-value pairs to be stored in the database.
50
+ pruning_mode: Sets the strictness for handling problematic transcripts.
51
+ Must be one of 'coarse', 'fine', or 'strict'.
52
+ on_foreign_chrom: Action to take for features on chromosomes not listed in
53
+ `chrominfo_file`. Must be one of 'abort', 'drop', or 'as.is'.
54
+ infer_cds: If TRUE, infer CDS features from annotated stop codons.
55
+ infer_utr: If TRUE, infer UTR features (5' and 3') when not explicitly provided.
56
+
57
+ Returns:
58
+ A dictionary containing the executed command, stdout, stderr, and a
59
+ mapping to the generated output database file.
60
+ """
61
+ # 1. Input Validation
62
+ if not gff_file.is_file():
63
+ raise FileNotFoundError(f"Input GFF/GTF file not found: {gff_file}")
64
+
65
+ if chrominfo_file and not chrominfo_file.is_file():
66
+ raise FileNotFoundError(f"Chrominfo file not found: {chrominfo_file}")
67
+
68
+ if metadata_file and not metadata_file.is_file():
69
+ raise FileNotFoundError(f"Metadata file not found: {metadata_file}")
70
+
71
+ if format and format.lower() not in ["gff3", "gtf"]:
72
+ raise ValueError("Parameter 'format' must be either 'gff3' or 'gtf'.")
73
+
74
+ allowed_pruning = ["coarse", "fine", "strict"]
75
+ if pruning_mode not in allowed_pruning:
76
+ raise ValueError(f"Parameter 'pruning_mode' must be one of {allowed_pruning}.")
77
+
78
+ allowed_foreign_chrom = ["abort", "drop", "as.is"]
79
+ if on_foreign_chrom not in allowed_foreign_chrom:
80
+ raise ValueError(f"Parameter 'on_foreign_chrom' must be one of {allowed_foreign_chrom}.")
81
+
82
+ output_db_path.parent.mkdir(parents=True, exist_ok=True)
83
+
84
+ # 2. R Script Generation
85
+ # Use R-friendly paths (forward slashes) for cross-platform compatibility
86
+ gff_file_r = str(gff_file).replace('\\', '/')
87
+ output_db_path_r = str(output_db_path).replace('\\', '/')
88
+
89
+ r_script_lines = [
90
+ "suppressPackageStartupMessages(library(GenomicFeatures))",
91
+ "",
92
+ "# Build the argument list for makeTxDbFromGFF",
93
+ f'txdb_args <- list(file = "{gff_file_r}")',
94
+ ]
95
+
96
+ # Add optional arguments to the list
97
+ if format:
98
+ r_script_lines.append(f'txdb_args$format <- "{format.lower()}"')
99
+ if data_source:
100
+ r_script_lines.append(f'txdb_args$dataSource <- "{data_source}"')
101
+ if organism:
102
+ r_script_lines.append(f'txdb_args$organism <- "{organism}"')
103
+ if taxonomy_id is not None:
104
+ r_script_lines.append(f'txdb_args$taxonomyId <- {taxonomy_id}')
105
+
106
+ r_script_lines.append(f'txdb_args$pruning.mode <- "{pruning_mode}"')
107
+ r_script_lines.append(f'txdb_args$on.foreign.chrom <- "{on_foreign_chrom}"')
108
+ r_script_lines.append(f'txdb_args$infer_cds <- {"TRUE" if infer_cds else "FALSE"}')
109
+ r_script_lines.append(f'txdb_args$infer_utr <- {"TRUE" if infer_utr else "FALSE"}')
110
+
111
+ if chrominfo_file:
112
+ chrominfo_file_r = str(chrominfo_file).replace('\\', '/')
113
+ r_script_lines.extend([
114
+ f'chrominfo_df <- read.table("{chrominfo_file_r}", sep="\\t", header=FALSE, col.names=c("chrom", "length"), stringsAsFactors=FALSE)',
115
+ 'txdb_args$chrominfo <- chrominfo_df'
116
+ ])
117
+
118
+ if metadata_file:
119
+ metadata_file_r = str(metadata_file).replace('\\', '/')
120
+ r_script_lines.extend([
121
+ f'metadata_df <- read.table("{metadata_file_r}", sep="\\t", header=FALSE, col.names=c("name", "value"), stringsAsFactors=FALSE)',
122
+ 'txdb_args$metadata <- metadata_df'
123
+ ])
124
+
125
+ r_script_lines.extend([
126
+ "",
127
+ "# Execute the function with the constructed arguments",
128
+ "txdb <- do.call(makeTxDbFromGFF, txdb_args)",
129
+ "",
130
+ "# Save the resulting database",
131
+ f'saveDb(txdb, file="{output_db_path_r}")',
132
+ 'message("TxDb database created successfully.")'
133
+ ])
134
+
135
+ r_script_content = "\n".join(r_script_lines)
136
+
137
+ script_path = None
138
+ try:
139
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R", encoding='utf-8') as r_script_file:
140
+ r_script_file.write(r_script_content)
141
+ script_path = r_script_file.name
142
+
143
+ # 3. Subprocess Execution
144
+ cmd = ["Rscript", script_path]
145
+ command_executed = shlex.join(cmd)
146
+
147
+ result = subprocess.run(
148
+ cmd,
149
+ check=True,
150
+ capture_output=True,
151
+ text=True,
152
+ encoding='utf-8'
153
+ )
154
+
155
+ # 4. Structured Result Return
156
+ return {
157
+ "command_executed": command_executed,
158
+ "stdout": result.stdout,
159
+ "stderr": result.stderr,
160
+ "output_files": {"txdb_database": str(output_db_path)}
161
+ }
162
+
163
+ except subprocess.CalledProcessError as e:
164
+ # R often prints errors to stdout, so combine them for a comprehensive message
165
+ error_message = (
166
+ f"R script execution failed with exit code {e.returncode}.\n"
167
+ f"STDOUT:\n{e.stdout}\n"
168
+ f"STDERR:\n{e.stderr}\n"
169
+ )
170
+ raise RuntimeError(error_message) from e
171
+
172
+ finally:
173
+ # Clean up the temporary script file
174
+ if script_path and Path(script_path).exists():
175
+ Path(script_path).unlink()
176
+
177
+ if __name__ == "__main__":
178
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/app/bioconductor-genomicfeatures_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-genomicfeatures/app/bioconductor-genomicfeatures_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_genomicfeatures'
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-genomicfeatures/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-genomicfeatures:
5
+ build: .
6
+ image: mcp-bioconductor-genomicfeatures:latest
7
+ container_name: mcp-bioconductor-genomicfeatures
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-genomicfeatures
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-genomicfeatures/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-genomicfeatures
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-genomicfeatures/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-hdf5array/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-hdf5array via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-hdf5array -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-hdf5array_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-hdf5array_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-hdf5array_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-hdf5array/app/bioconductor-hdf5array_server.py ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import textwrap
4
+ from pathlib import Path
5
+ from typing import Dict, List, Optional, Union
6
+
7
+ # MCP decorator is not defined in this context, so we'll use a placeholder.
8
+ # In a real MCP environment, this would be: from mcp import tool
9
+ class mcp:
10
+ def tool(func):
11
+ return func
12
+
13
+ @mcp.tool
14
+ def write_hdf5_array(
15
+ input_file: Path,
16
+ output_hdf5: Path,
17
+ dataset_name: str,
18
+ input_format: str = "csv",
19
+ chunk_dim: Optional[str] = None,
20
+ compression_level: int = 6,
21
+ as_integer: bool = False,
22
+ csv_header: bool = True,
23
+ csv_row_names_col: Optional[int] = 1,
24
+ ) -> Dict[str, Union[str, Dict[str, str]]]:
25
+ """
26
+ Converts a data file (CSV, TSV, or RDS) into an HDF5 file using HDF5Array.
27
+
28
+ This tool is a wrapper around the HDF5Array::writeHDF5Array R function.
29
+ It requires R and the Bioconductor 'HDF5Array' package to be installed.
30
+ """
31
+ # 1. Input validation
32
+ if not input_file.exists():
33
+ raise FileNotFoundError(f"Input file not found: {input_file}")
34
+ if not output_hdf5.parent.exists():
35
+ raise FileNotFoundError(f"Output directory does not exist: {output_hdf5.parent}")
36
+ if input_format not in ["csv", "tsv", "rds"]:
37
+ raise ValueError("input_format must be one of 'csv', 'tsv', or 'rds'.")
38
+ if not (0 <= compression_level <= 9):
39
+ raise ValueError("compression_level must be between 0 and 9.")
40
+ if chunk_dim:
41
+ try:
42
+ [int(d) for d in chunk_dim.split(',')]
43
+ except ValueError:
44
+ raise ValueError("chunk_dim must be a comma-separated string of integers (e.g., '100,1000').")
45
+
46
+ # 2. R script for execution
47
+ r_script_content = textwrap.dedent("""
48
+ suppressPackageStartupMessages({
49
+ library(optparse)
50
+ library(HDF5Array)
51
+ })
52
+
53
+ option_list <- list(
54
+ make_option("--input", type="character", help="Input file path (CSV, TSV, or RDS)."),
55
+ make_option("--output", type="character", help="Output HDF5 file path."),
56
+ make_option("--name", type="character", help="Name for the dataset within the HDF5 file."),
57
+ make_option("--format", type="character", default="csv", help="Input file format [csv, tsv, rds]."),
58
+ make_option("--chunkdim", type="character", default=NULL, help="Comma-separated chunk dimensions."),
59
+ make_option("--level", type="integer", default=6, help="Compression level (0-9)."),
60
+ make_option("--as_integer", action="store_true", default=FALSE, help="Coerce data to integer."),
61
+ make_option("--header", action="store_true", default=TRUE, help="Does the CSV/TSV have a header?"),
62
+ make_option("--rownames", type="integer", default=NULL, help="Column number for row names in CSV/TSV.")
63
+ )
64
+
65
+ args <- parse_args(OptionParser(option_list=option_list))
66
+
67
+ # Read input data
68
+ message("Reading input file: ", args$input)
69
+ if (args$format == "rds") {
70
+ data_matrix <- readRDS(args$input)
71
+ } else if (args$format == "csv") {
72
+ data_matrix <- read.csv(args$input, header=args$header, row.names=args$rownames, check.names=FALSE)
73
+ } else if (args$format == "tsv") {
74
+ data_matrix <- read.delim(args$input, header=args$header, row.names=args$rownames, check.names=FALSE)
75
+ } else {
76
+ stop("Unsupported format: ", args$format)
77
+ }
78
+
79
+ if (!is.matrix(data_matrix)) {
80
+ message("Input data is not a matrix, coercing...")
81
+ data_matrix <- as.matrix(data_matrix)
82
+ }
83
+
84
+ if (args$as_integer) {
85
+ message("Coercing data to integer...")
86
+ storage.mode(data_matrix) <- "integer"
87
+ }
88
+
89
+ # Parse chunk dimensions
90
+ chunk_dims <- NULL
91
+ if (!is.null(args$chunkdim)) {
92
+ chunk_dims <- as.integer(strsplit(args$chunkdim, ",")[[1]])
93
+ message("Using chunk dimensions: ", paste(chunk_dims, collapse=", "))
94
+ }
95
+
96
+ # Write HDF5 array
97
+ message("Writing HDF5 array to: ", args$output)
98
+ writeHDF5Array(
99
+ x = data_matrix,
100
+ filepath = args$output,
101
+ name = args$name,
102
+ chunkdim = chunk_dims,
103
+ level = args$level,
104
+ with.dimnames = TRUE,
105
+ verbose = TRUE
106
+ )
107
+ message("Successfully created HDF5 file.")
108
+ """)
109
+
110
+ # 3. Subprocess execution
111
+ cmd = [
112
+ "Rscript", "-",
113
+ "--input", str(input_file),
114
+ "--output", str(output_hdf5),
115
+ "--name", dataset_name,
116
+ "--format", input_format,
117
+ "--level", str(compression_level),
118
+ ]
119
+ if chunk_dim:
120
+ cmd.extend(["--chunkdim", chunk_dim])
121
+ if as_integer:
122
+ cmd.append("--as_integer")
123
+ if csv_header:
124
+ cmd.append("--header")
125
+ if csv_row_names_col is not None:
126
+ cmd.extend(["--rownames", str(csv_row_names_col)])
127
+
128
+ try:
129
+ with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix=".R") as r_script_file:
130
+ r_script_file.write(r_script_content)
131
+ r_script_file.flush()
132
+
133
+ # Rebuild command to use the script file
134
+ cmd[1] = r_script_file.name
135
+
136
+ process = subprocess.run(
137
+ cmd,
138
+ capture_output=True,
139
+ text=True,
140
+ check=True,
141
+ )
142
+
143
+ return {
144
+ "command_executed": " ".join(cmd),
145
+ "stdout": process.stdout,
146
+ "stderr": process.stderr,
147
+ "output_files": {"output_hdf5": str(output_hdf5)},
148
+ }
149
+ except FileNotFoundError:
150
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
151
+ except subprocess.CalledProcessError as e:
152
+ error_message = (
153
+ f"HDF5Array R script failed with exit code {e.returncode}.\n"
154
+ f"This may be due to missing R packages ('optparse', 'HDF5Array').\n"
155
+ f"Please install them in R using:\n"
156
+ f"install.packages('optparse')\n"
157
+ f"if (!requireNamespace('BiocManager', quietly = TRUE)) install.packages('BiocManager')\n"
158
+ f"BiocManager::install('HDF5Array')\n\n"
159
+ f"STDOUT:\n{e.stdout}\n"
160
+ f"STDERR:\n{e.stderr}"
161
+ )
162
+ raise RuntimeError(error_message) from e
163
+
164
+
165
+ @mcp.tool
166
+ def save_hdf5_summarized_experiment(
167
+ input_rds: Path,
168
+ output_dir: Path,
169
+ prefix: Optional[str] = None,
170
+ replace: bool = False,
171
+ ) -> Dict[str, Union[str, Dict[str, str]]]:
172
+ """
173
+ Saves a SummarizedExperiment object from an RDS file to an HDF5-backed format.
174
+
175
+ This tool wraps the HDF5Array::saveHDF5SummarizedExperiment R function.
176
+ It requires R and the Bioconductor packages 'HDF5Array' and 'SummarizedExperiment'.
177
+ """
178
+ # 1. Input validation
179
+ if not input_rds.exists():
180
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds}")
181
+ if output_dir.exists() and not output_dir.is_dir():
182
+ raise ValueError(f"Output path exists but is not a directory: {output_dir}")
183
+ if output_dir.exists() and any(output_dir.iterdir()) and not replace:
184
+ raise FileExistsError(f"Output directory {output_dir} is not empty. Use replace=True to overwrite.")
185
+
186
+ output_dir.mkdir(parents=True, exist_ok=True)
187
+
188
+ # 2. R script for execution
189
+ r_script_content = textwrap.dedent("""
190
+ suppressPackageStartupMessages({
191
+ library(optparse)
192
+ library(HDF5Array)
193
+ library(SummarizedExperiment)
194
+ })
195
+
196
+ option_list <- list(
197
+ make_option("--input", type="character", help="Input RDS file containing a SummarizedExperiment object."),
198
+ make_option("--outdir", type="character", help="Output directory to save the HDF5SummarizedExperiment."),
199
+ make_option("--prefix", type="character", default=NULL, help="Optional prefix for file names."),
200
+ make_option("--replace", action="store_true", default=FALSE, help="Replace existing directory content.")
201
+ )
202
+
203
+ args <- parse_args(OptionParser(option_list=option_list))
204
+
205
+ message("Reading SummarizedExperiment object from: ", args$input)
206
+ se <- readRDS(args$input)
207
+
208
+ if (!is(se, "SummarizedExperiment")) {
209
+ stop("The object in the RDS file is not a SummarizedExperiment.")
210
+ }
211
+
212
+ message("Saving HDF5SummarizedExperiment to: ", args$outdir)
213
+ saveHDF5SummarizedExperiment(
214
+ x = se,
215
+ dir = args$outdir,
216
+ prefix = args$prefix,
217
+ replace = args$replace,
218
+ verbose = TRUE
219
+ )
220
+ message("Successfully saved HDF5SummarizedExperiment.")
221
+ """)
222
+
223
+ # 3. Subprocess execution
224
+ cmd = [
225
+ "Rscript", "-",
226
+ "--input", str(input_rds),
227
+ "--outdir", str(output_dir),
228
+ ]
229
+ if prefix:
230
+ cmd.extend(["--prefix", prefix])
231
+ if replace:
232
+ cmd.append("--replace")
233
+
234
+ try:
235
+ with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix=".R") as r_script_file:
236
+ r_script_file.write(r_script_content)
237
+ r_script_file.flush()
238
+
239
+ cmd[1] = r_script_file.name
240
+
241
+ process = subprocess.run(
242
+ cmd,
243
+ capture_output=True,
244
+ text=True,
245
+ check=True,
246
+ )
247
+
248
+ return {
249
+ "command_executed": " ".join(cmd),
250
+ "stdout": process.stdout,
251
+ "stderr": process.stderr,
252
+ "output_files": {"output_directory": str(output_dir)},
253
+ }
254
+ except FileNotFoundError:
255
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
256
+ except subprocess.CalledProcessError as e:
257
+ error_message = (
258
+ f"HDF5Array R script failed with exit code {e.returncode}.\n"
259
+ f"This may be due to missing R packages ('optparse', 'HDF5Array', 'SummarizedExperiment').\n"
260
+ f"Please install them in R using BiocManager.\n\n"
261
+ f"STDOUT:\n{e.stdout}\n"
262
+ f"STDERR:\n{e.stderr}"
263
+ )
264
+ raise RuntimeError(error_message) from e
265
+
266
+
267
+ @mcp.tool
268
+ def export_hdf5_summarized_experiment(
269
+ input_dir: Path,
270
+ output_dir: Path,
271
+ assay_names: Optional[str] = None,
272
+ export_coldata: bool = True,
273
+ export_rowdata: bool = True,
274
+ ) -> Dict[str, Union[str, Dict[str, str]]]:
275
+ """
276
+ Loads an HDF5SummarizedExperiment and exports its components to CSV files.
277
+
278
+ This tool wraps HDF5Array::loadHDF5SummarizedExperiment and exports data.
279
+ Requires R and the Bioconductor packages 'HDF5Array' and 'SummarizedExperiment'.
280
+ """
281
+ # 1. Input validation
282
+ if not input_dir.is_dir():
283
+ raise FileNotFoundError(f"Input directory not found: {input_dir}")
284
+ if output_dir.exists() and not output_dir.is_dir():
285
+ raise ValueError(f"Output path exists but is not a directory: {output_dir}")
286
+
287
+ output_dir.mkdir(parents=True, exist_ok=True)
288
+
289
+ # 2. R script for execution
290
+ r_script_content = textwrap.dedent("""
291
+ suppressPackageStartupMessages({
292
+ library(optparse)
293
+ library(HDF5Array)
294
+ library(SummarizedExperiment)
295
+ })
296
+
297
+ option_list <- list(
298
+ make_option("--indir", type="character", help="Input directory of the HDF5SummarizedExperiment."),
299
+ make_option("--outdir", type="character", help="Output directory for exported CSV files."),
300
+ make_option("--assays", type="character", default=NULL, help="Comma-separated list of assays to export (default: all)."),
301
+ make_option("--coldata", action="store_true", default=FALSE, help="Export colData to CSV."),
302
+ make_option("--rowdata", action="store_true", default=FALSE, help="Export rowData to CSV.")
303
+ )
304
+
305
+ args <- parse_args(OptionParser(option_list=option_list))
306
+
307
+ message("Loading HDF5SummarizedExperiment from: ", args$indir)
308
+ se <- loadHDF5SummarizedExperiment(dir = args$indir)
309
+
310
+ assays_to_export <- assayNames(se)
311
+ if (!is.null(args$assays)) {
312
+ assays_to_export <- intersect(assays_to_export, strsplit(args$assays, ",")[[1]])
313
+ }
314
+
315
+ if (length(assays_to_export) > 0) {
316
+ for (aname in assays_to_export) {
317
+ message("Exporting assay: ", aname)
318
+ out_path <- file.path(args$outdir, paste0("assay_", aname, ".csv"))
319
+ mat <- as.matrix(assay(se, aname))
320
+ write.csv(mat, file=out_path, quote=FALSE, row.names=TRUE)
321
+ }
322
+ }
323
+
324
+ if (args$coldata) {
325
+ message("Exporting colData")
326
+ out_path <- file.path(args$outdir, "coldata.csv")
327
+ write.csv(as.data.frame(colData(se)), file=out_path, quote=FALSE, row.names=TRUE)
328
+ }
329
+
330
+ if (args$rowdata) {
331
+ message("Exporting rowData")
332
+ out_path <- file.path(args$outdir, "rowdata.csv")
333
+ write.csv(as.data.frame(rowData(se)), file=out_path, quote=FALSE, row.names=TRUE)
334
+ }
335
+
336
+ message("Export complete.")
337
+ """)
338
+
339
+ # 3. Subprocess execution
340
+ cmd = [
341
+ "Rscript", "-",
342
+ "--indir", str(input_dir),
343
+ "--outdir", str(output_dir),
344
+ ]
345
+ if assay_names:
346
+ cmd.extend(["--assays", assay_names])
347
+ if export_coldata:
348
+ cmd.append("--coldata")
349
+ if export_rowdata:
350
+ cmd.append("--rowdata")
351
+
352
+ try:
353
+ with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix=".R") as r_script_file:
354
+ r_script_file.write(r_script_content)
355
+ r_script_file.flush()
356
+
357
+ cmd[1] = r_script_file.name
358
+
359
+ process = subprocess.run(
360
+ cmd,
361
+ capture_output=True,
362
+ text=True,
363
+ check=True,
364
+ )
365
+
366
+ output_files = [str(p) for p in output_dir.glob("*.csv")]
367
+ return {
368
+ "command_executed": " ".join(cmd),
369
+ "stdout": process.stdout,
370
+ "stderr": process.stderr,
371
+ "output_files": {"output_directory": str(output_dir), "exported_files": output_files},
372
+ }
373
+ except FileNotFoundError:
374
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
375
+ except subprocess.CalledProcessError as e:
376
+ error_message = (
377
+ f"HDF5Array R script failed with exit code {e.returncode}.\n"
378
+ f"This may be due to missing R packages or an invalid HDF5SummarizedExperiment directory.\n\n"
379
+ f"STDOUT:\n{e.stdout}\n"
380
+ f"STDERR:\n{e.stderr}"
381
+ )
382
+ raise RuntimeError(error_message) from e
383
+
384
+
385
+ @mcp.tool
386
+ def inspect_hdf5_array(
387
+ input_hdf5: Path,
388
+ dataset_name: str,
389
+ ) -> Dict[str, str]:
390
+ """
391
+ Inspects an HDF5 file and prints properties of a specific HDF5Array dataset.
392
+
393
+ This tool provides information like dimensions and chunking without loading data.
394
+ Requires R and the Bioconductor 'HDF5Array' package.
395
+ """
396
+ # 1. Input validation
397
+ if not input_hdf5.exists():
398
+ raise FileNotFoundError(f"Input HDF5 file not found: {input_hdf5}")
399
+
400
+ # 2. R script for execution
401
+ r_script_content = textwrap.dedent("""
402
+ suppressPackageStartupMessages({
403
+ library(optparse)
404
+ library(HDF5Array)
405
+ })
406
+
407
+ option_list <- list(
408
+ make_option("--file", type="character", help="Input HDF5 file path."),
409
+ make_option("--name", type="character", help="Name of the dataset to inspect.")
410
+ )
411
+
412
+ args <- parse_args(OptionParser(option_list=option_list))
413
+
414
+ # Load the HDF5 array seed
415
+ HDF5_dataset <- HDF5Array(args$file, args$name)
416
+
417
+ # Print properties
418
+ cat("Dataset:", name(HDF5_dataset), "\\n")
419
+ cat("Filepath:", path(HDF5_dataset), "\\n")
420
+ cat("Dimensions:", paste(dim(HDF5_dataset), collapse=" x "), "\\n")
421
+ cat("Datatype:", class(HDF5_dataset@seed@first_val), "\\n")
422
+ cat("Chunking:", paste(chunkdim(HDF5_dataset), collapse=" x "), "\\n")
423
+ """)
424
+
425
+ # 3. Subprocess execution
426
+ cmd = [
427
+ "Rscript", "-",
428
+ "--file", str(input_hdf5),
429
+ "--name", dataset_name,
430
+ ]
431
+
432
+ try:
433
+ with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix=".R") as r_script_file:
434
+ r_script_file.write(r_script_content)
435
+ r_script_file.flush()
436
+
437
+ cmd[1] = r_script_file.name
438
+
439
+ process = subprocess.run(
440
+ cmd,
441
+ capture_output=True,
442
+ text=True,
443
+ check=True,
444
+ )
445
+
446
+ return {
447
+ "command_executed": " ".join(cmd),
448
+ "stdout": process.stdout,
449
+ "stderr": process.stderr,
450
+ }
451
+ except FileNotFoundError:
452
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
453
+ except subprocess.CalledProcessError as e:
454
+ error_message = (
455
+ f"HDF5Array R script failed with exit code {e.returncode}.\n"
456
+ f"This may be due to a missing R package, an incorrect file path, or a non-existent dataset name ('{dataset_name}').\n\n"
457
+ f"STDOUT:\n{e.stdout}\n"
458
+ f"STDERR:\n{e.stderr}"
459
+ )
460
+ raise RuntimeError(error_message) from e
Biomni/mcp_generated/mcp_bioconductor-hdf5array/app/bioconductor-hdf5array_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-hdf5array/app/bioconductor-hdf5array_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_hdf5array'
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-hdf5array/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-hdf5array/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-hdf5array:
5
+ build: .
6
+ image: mcp-bioconductor-hdf5array:latest
7
+ container_name: mcp-bioconductor-hdf5array
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-hdf5array
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-hdf5array/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-hdf5array
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-hdf5array/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-irisfgm/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-irisfgm via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-irisfgm -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-irisfgm_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-irisfgm_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-irisfgm_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_server.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_irisfgm'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def run_irisfgm_script(
13
+ r_script_content: str,
14
+ output_dir: Path = Path("./"),
15
+ r_environment_setup_script: Optional[str] = None,
16
+ ) -> Dict[str, Any]:
17
+ """
18
+ Executes an R script that utilizes the bioconductor-irisfgm package.
19
+
20
+ The bioconductor-irisfgm tool is an R package designed for comprehensive
21
+ analysis of gene interactivity networks based on single-cell RNA-Seq data.
22
+ As the provided documentation does not expose a direct command-line
23
+ interface with specific subcommands and parameters, this MCP tool provides
24
+ a generic entry point to execute arbitrary R code that leverages the IRISFGM
25
+ package.
26
+
27
+ Users are expected to provide an R script that loads the IRISFGM package
28
+ and calls its internal functions as needed. The R script should be written
29
+ to save any desired output files to the directory specified by the
30
+ 'OUTPUT_DIR' environment variable, which corresponds to the `output_dir`
31
+ parameter.
32
+
33
+ Example R script content:
34
+ ```R
35
+ # Load the IRISFGM package
36
+ library(IRISFGM)
37
+
38
+ # --- Placeholder for actual IRISFGM function calls ---
39
+ # In a real scenario, you would load your actual scRNA-seq data
40
+ # (e.g., from a CSV, TSV, or a Seurat object) and then apply IRISFGM functions.
41
+ #
42
+ # Example: Create a dummy SingleCellExperiment object for demonstration
43
+ # library(SingleCellExperiment)
44
+ # counts <- matrix(rnbinom(1000, mu=10, size=1), ncol=10)
45
+ # rownames(counts) <- paste0("gene", 1:100)
46
+ # colnames(counts) <- paste0("cell", 1:10)
47
+ # sce <- SingleCellExperiment(assays=list(counts=counts))
48
+ #
49
+ # Example of a hypothetical IRISFGM function call (replace with actual functions):
50
+ # fgms_result <- IRISFGM::identifyFGMs(sce_object, ...)
51
+ #
52
+ # Print some output to stdout
53
+ message("IRISFGM R script executed successfully.")
54
+ #
55
+ # Save results to a file in the output directory
56
+ # output_dir <- Sys.getenv("OUTPUT_DIR")
57
+ # if (!is.null(output_dir) && dir.exists(output_dir)) {
58
+ # write.csv(data.frame(gene="gene1", value=10), file = file.path(output_dir, "example_output.csv"))
59
+ # message(paste("Saved example_output.csv to", output_dir))
60
+ # } else {
61
+ # warning("OUTPUT_DIR environment variable not set or directory does not exist.")
62
+ # }
63
+ ```
64
+
65
+ Args:
66
+ r_script_content: A string containing the R script to be executed.
67
+ This script should include calls to IRISFGM functions.
68
+ output_dir: The directory where any output files generated by the R script
69
+ should be stored. The R script should be written to save
70
+ files to this directory (e.g., using `file.path(Sys.getenv("OUTPUT_DIR"), "filename")`).
71
+ Defaults to the current working directory.
72
+ r_environment_setup_script: Optional R script content to run before the main
73
+ `r_script_content`. This can be used for
74
+ environment setup, installing packages, etc.,
75
+ if not handled by the container.
76
+
77
+ Returns:
78
+ A dictionary containing the command executed, stdout, stderr, and a list
79
+ of any output files generated.
80
+ """
81
+ if not r_script_content:
82
+ raise ValueError("R script content cannot be empty.")
83
+
84
+ # Ensure the output directory exists
85
+ output_dir.mkdir(parents=True, exist_ok=True)
86
+
87
+ command_executed: List[str] = []
88
+ stdout: str = ""
89
+ stderr: str = ""
90
+ output_files: List[Path] = []
91
+
92
+ with tempfile.TemporaryDirectory() as tmpdir:
93
+ temp_dir_path = Path(tmpdir)
94
+ r_script_path = temp_dir_path / "main_script.R"
95
+ setup_script_path: Optional[Path] = None
96
+
97
+ # Write the main R script content to a temporary file
98
+ with open(r_script_path, "w") as f:
99
+ f.write(r_script_content)
100
+
101
+ # Prepare environment variables for the R process
102
+ env_vars = {"OUTPUT_DIR": str(output_dir)}
103
+
104
+ # If an R environment setup script is provided, execute it first
105
+ if r_environment_setup_script:
106
+ setup_script_path = temp_dir_path / "setup_script.R"
107
+ with open(setup_script_path, "w") as f:
108
+ f.write(r_environment_setup_script)
109
+ setup_command = ["Rscript", str(setup_script_path)]
110
+ command_executed.append(" ".join(setup_command))
111
+ try:
112
+ setup_result = subprocess.run(
113
+ setup_command,
114
+ capture_output=True,
115
+ text=True,
116
+ check=True,
117
+ env={**subprocess.os.environ, **env_vars} # Merge with current environment
118
+ )
119
+ stdout += f"Setup script stdout:\n{setup_result.stdout}\n"
120
+ stderr += f"Setup script stderr:\n{setup_result.stderr}\n"
121
+ except subprocess.CalledProcessError as e:
122
+ return {
123
+ "command_executed": " ".join(setup_command),
124
+ "stdout": e.stdout,
125
+ "stderr": e.stderr,
126
+ "error": f"R setup script failed with exit code {e.returncode}",
127
+ "output_files": [],
128
+ }
129
+ except FileNotFoundError:
130
+ return {
131
+ "command_executed": " ".join(setup_command),
132
+ "stdout": "",
133
+ "stderr": "Error: Rscript command not found. Is R installed and in PATH?",
134
+ "error": "Rscript not found for setup script",
135
+ "output_files": [],
136
+ }
137
+
138
+ # Execute the main R script
139
+ r_command = ["Rscript", str(r_script_path)]
140
+ command_executed.append(" ".join(r_command))
141
+
142
+ try:
143
+ result = subprocess.run(
144
+ r_command,
145
+ capture_output=True,
146
+ text=True,
147
+ check=True,
148
+ env={**subprocess.os.environ, **env_vars} # Merge with current environment
149
+ )
150
+ stdout += result.stdout
151
+ stderr += result.stderr
152
+
153
+ # Collect output files from the specified output_dir
154
+ for f in output_dir.iterdir():
155
+ if f.is_file():
156
+ output_files.append(f)
157
+
158
+ except subprocess.CalledProcessError as e:
159
+ return {
160
+ "command_executed": "\n".join(command_executed),
161
+ "stdout": e.stdout,
162
+ "stderr": e.stderr,
163
+ "error": f"R script failed with exit code {e.returncode}",
164
+ "output_files": [],
165
+ }
166
+ except FileNotFoundError:
167
+ return {
168
+ "command_executed": "\n".join(command_executed),
169
+ "stdout": "",
170
+ "stderr": "Error: Rscript command not found. Is R installed and in PATH?",
171
+ "error": "Rscript not found for main script",
172
+ "output_files": [],
173
+ }
174
+
175
+ return {
176
+ "command_executed": "\n".join(command_executed),
177
+ "stdout": stdout,
178
+ "stderr": stderr,
179
+ "output_files": output_files,
180
+ }
181
+
182
+ if __name__ == "__main__":
183
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_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-irisfgm/app/bioconductor-irisfgm_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_irisfgm'
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-irisfgm/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-irisfgm:
5
+ build: .
6
+ image: mcp-bioconductor-irisfgm:latest
7
+ container_name: mcp-bioconductor-irisfgm
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-irisfgm
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
+