czty commited on
Commit
e2c866b
·
verified ·
1 Parent(s): efbdcfc

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_anarci/Dockerfile +40 -0
  2. Biomni/mcp_generated/mcp_anarci/app/anarci_server.py +191 -0
  3. Biomni/mcp_generated/mcp_anarci/docker-compose.yml +22 -0
  4. Biomni/mcp_generated/mcp_anarci/environment.yaml +10 -0
  5. Biomni/mcp_generated/mcp_anarci/requirements.txt +2 -0
  6. Biomni/mcp_generated/mcp_bioconductor-cellmigration/Dockerfile +40 -0
  7. Biomni/mcp_generated/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py +214 -0
  8. Biomni/mcp_generated/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_shim_server.py +55 -0
  9. Biomni/mcp_generated/mcp_bioconductor-cellmigration/app/requirements.txt +1 -0
  10. Biomni/mcp_generated/mcp_bioconductor-cellmigration/docker-compose.yml +22 -0
  11. Biomni/mcp_generated/mcp_bioconductor-cellmigration/environment.yaml +10 -0
  12. Biomni/mcp_generated/mcp_bioconductor-cellmigration/requirements.txt +2 -0
  13. Biomni/mcp_generated/mcp_bioconductor-geneplotter/requirements.txt +2 -0
  14. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/Dockerfile +40 -0
  15. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/app/bioconductor-glmgampoi_server.py +270 -0
  16. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/app/bioconductor-glmgampoi_shim_server.py +55 -0
  17. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/app/requirements.txt +1 -0
  18. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/environment.yaml +10 -0
  19. Biomni/mcp_generated/mcp_bioconductor-keggrest/Dockerfile +40 -0
  20. Biomni/mcp_generated/mcp_bioconductor-keggrest/app/bioconductor-keggrest_shim_server.py +55 -0
  21. Biomni/mcp_generated/mcp_bioconductor-keggrest/docker-compose.yml +22 -0
  22. Biomni/mcp_generated/mcp_bioconductor-keggrest/environment.yaml +10 -0
  23. Biomni/mcp_generated/mcp_bioconductor-keggrest/requirements.txt +2 -0
  24. Biomni/mcp_generated/mcp_bioconductor-scran/Dockerfile +40 -0
  25. Biomni/mcp_generated/mcp_bioconductor-scran/app/bioconductor-scran_server.py +115 -0
  26. Biomni/mcp_generated/mcp_bioconductor-scran/app/bioconductor-scran_shim_server.py +55 -0
  27. Biomni/mcp_generated/mcp_bioconductor-scran/app/requirements.txt +1 -0
  28. Biomni/mcp_generated/mcp_bioconductor-scran/docker-compose.yml +22 -0
  29. Biomni/mcp_generated/mcp_bioconductor-scran/environment.yaml +10 -0
  30. Biomni/mcp_generated/mcp_bioconductor-scran/requirements.txt +2 -0
  31. Biomni/mcp_generated/mcp_bioconductor-spari/Dockerfile +40 -0
  32. Biomni/mcp_generated/mcp_bioconductor-spari/docker-compose.yml +22 -0
  33. Biomni/mcp_generated/mcp_bioconductor-spari/environment.yaml +10 -0
  34. Biomni/mcp_generated/mcp_bioconductor-spari/requirements.txt +2 -0
  35. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/Dockerfile +40 -0
  36. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/app/bioconductor-spatialcpie_server.py +158 -0
  37. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/app/bioconductor-spatialcpie_shim_server.py +55 -0
  38. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/docker-compose.yml +22 -0
  39. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/environment.yaml +10 -0
  40. Biomni/mcp_generated/mcp_bioconductor-spatialcpie/requirements.txt +2 -0
  41. Biomni/mcp_generated/mcp_bioconductor-spoon/app/bioconductor-spoon_server.py +160 -0
  42. Biomni/mcp_generated/mcp_bioconductor-spoon/environment.yaml +10 -0
  43. Biomni/mcp_generated/mcp_bioconductor-statial/Dockerfile +40 -0
  44. Biomni/mcp_generated/mcp_bioconductor-statial/app/bioconductor-statial_server.py +321 -0
  45. Biomni/mcp_generated/mcp_bioconductor-statial/app/bioconductor-statial_shim_server.py +55 -0
  46. Biomni/mcp_generated/mcp_bioconductor-statial/app/requirements.txt +1 -0
  47. Biomni/mcp_generated/mcp_bioconductor-statial/docker-compose.yml +22 -0
  48. Biomni/mcp_generated/mcp_bioconductor-statial/environment.yaml +10 -0
  49. Biomni/mcp_generated/mcp_bioconductor-statial/requirements.txt +2 -0
  50. Biomni/mcp_generated/mcp_bioconductor-svp/app/bioconductor-svp_server.py +250 -0
Biomni/mcp_generated/mcp_anarci/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 anarci via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda anarci -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/anarci_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/anarci_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/anarci_server.py"]
40
+
Biomni/mcp_generated/mcp_anarci/app/anarci_server.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import subprocess
3
+ from typing import Optional, List, Union
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ SERVER_NAME = 'local_anarci'
8
+ mcp = FastMCP(SERVER_NAME)
9
+
10
+ @mcp.tool()
11
+ def run_anarci(
12
+ sequence: Optional[str] = None,
13
+ input_file: Optional[str] = None,
14
+ output_file: Optional[str] = None,
15
+ scheme: str = "imgt",
16
+ restrict: Optional[str] = None,
17
+ species: Optional[str] = None,
18
+ csv: bool = False,
19
+ ncpu: int = 1,
20
+ bit_score_threshold: float = 80.0,
21
+ assign_germline: bool = False,
22
+ name: Optional[str] = None,
23
+ verbose: bool = False
24
+ ):
25
+ """
26
+ ANARCI: Antibody Numbering and Antigen Receptor ClassIfication.
27
+ Numbers amino-acid sequences of antibody and T-cell receptor variable domains.
28
+
29
+ Args:
30
+ sequence: A single amino acid sequence to number.
31
+ input_file: Path to a FASTA file containing sequences to number.
32
+ output_file: Path to the output file. If not provided, results are printed to stdout.
33
+ scheme: Numbering scheme to use. Options: 'imgt', 'kabat', 'chothia', 'martin', 'aho'. (Default: 'imgt')
34
+ restrict: Restrict numbering to specific domain types. Options: 'ig' (immunoglobulins), 'tr' (T-cell receptors).
35
+ species: Restrict germline search to specific species (e.g., 'human', 'mouse', 'rat', 'rabbit', 'rhesus', 'pig', 'alpaca').
36
+ csv: If True, output results in CSV format (horizontal). If False, uses plain text (vertical).
37
+ ncpu: Number of CPUs to use for parallel processing. (Default: 1)
38
+ bit_score_threshold: HMMER bit score threshold for domain identification. (Default: 80.0)
39
+ assign_germline: If True, assign germline species and gene information.
40
+ name: Name to assign to the single sequence (used with 'sequence' parameter).
41
+ verbose: If True, produce verbose output.
42
+ """
43
+
44
+ # Input validation
45
+ if not sequence and not input_file:
46
+ raise ValueError("Either 'sequence' or 'input_file' must be provided.")
47
+
48
+ if sequence and input_file:
49
+ raise ValueError("Provide either 'sequence' or 'input_file', not both.")
50
+
51
+ valid_schemes = ["imgt", "kabat", "chothia", "martin", "aho"]
52
+ if scheme.lower() not in valid_schemes:
53
+ raise ValueError(f"Invalid scheme '{scheme}'. Must be one of {valid_schemes}")
54
+
55
+ if restrict and restrict.lower() not in ["ig", "tr"]:
56
+ raise ValueError("Restrict must be either 'ig' or 'tr'.")
57
+
58
+ # Build command
59
+ # The CLI tool is typically named 'ANARCI' (uppercase)
60
+ cmd = ["ANARCI"]
61
+
62
+ if sequence:
63
+ cmd.extend(["--sequence", sequence])
64
+ if name:
65
+ cmd.extend(["--name", name])
66
+
67
+ if input_file:
68
+ input_path = Path(input_file)
69
+ if not input_path.exists():
70
+ raise FileNotFoundError(f"Input file not found: {input_file}")
71
+ cmd.extend(["--input", str(input_path)])
72
+
73
+ if output_file:
74
+ cmd.extend(["--outfile", output_file])
75
+
76
+ cmd.extend(["--scheme", scheme.lower()])
77
+
78
+ if restrict:
79
+ cmd.extend(["--restrict", restrict.lower()])
80
+
81
+ if species:
82
+ cmd.extend(["--species", species.lower()])
83
+
84
+ if csv:
85
+ cmd.append("--csv")
86
+
87
+ if assign_germline:
88
+ cmd.append("--assign_germline")
89
+
90
+ if verbose:
91
+ cmd.append("--verbose")
92
+
93
+ cmd.extend(["--ncpu", str(ncpu)])
94
+ cmd.extend(["--bit_score_threshold", str(bit_score_threshold)])
95
+
96
+ try:
97
+ # Execute the tool
98
+ result = subprocess.run(
99
+ cmd,
100
+ capture_output=True,
101
+ text=True,
102
+ check=True
103
+ )
104
+
105
+ output_files = []
106
+ if output_file:
107
+ # ANARCI often appends suffixes depending on the format
108
+ # e.g., .csv or .txt
109
+ actual_output = Path(output_file)
110
+ if actual_output.exists():
111
+ output_files.append(str(actual_output))
112
+ else:
113
+ # Check for common suffixes if the exact file isn't found
114
+ for suffix in [".csv", ".txt"]:
115
+ p = Path(f"{output_file}{suffix}")
116
+ if p.exists():
117
+ output_files.append(str(p))
118
+
119
+ return {
120
+ "command_executed": " ".join(cmd),
121
+ "stdout": result.stdout,
122
+ "stderr": result.stderr,
123
+ "output_files": output_files
124
+ }
125
+
126
+ except subprocess.CalledProcessError as e:
127
+ return {
128
+ "command_executed": " ".join(cmd),
129
+ "stdout": e.stdout,
130
+ "stderr": e.stderr,
131
+ "error": f"ANARCI failed with return code {e.returncode}"
132
+ }
133
+ except Exception as e:
134
+ return {
135
+ "command_executed": " ".join(cmd),
136
+ "error": str(e)
137
+ }
138
+
139
+ @mcp.tool()
140
+ def list_anarci_species():
141
+ """
142
+ Returns a list of species supported by ANARCI for germline alignment.
143
+ Note: This is an informational tool based on ANARCI's standard database.
144
+ """
145
+ # Standard species supported by ANARCI
146
+ species_list = [
147
+ "human", "mouse", "rat", "rabbit", "rhesus", "pig", "alpaca",
148
+ "dog", "chicken", "cow", "horse", "monkey", "macaque"
149
+ ]
150
+
151
+ return {
152
+ "supported_species": species_list,
153
+ "note": "Availability may depend on the specific ANARCI version and installed HMM databases."
154
+ }
155
+
156
+ @mcp.tool()
157
+ def get_anarci_version():
158
+ """
159
+ Retrieves the version information for the installed ANARCI tool.
160
+ """
161
+ try:
162
+ # ANARCI doesn't always have a simple --version flag in older versions,
163
+ # but we can try to run it to see the help header or use pip/conda info.
164
+ # Most modern versions support --version.
165
+ result = subprocess.run(
166
+ ["ANARCI", "--version"],
167
+ capture_output=True,
168
+ text=True,
169
+ check=False
170
+ )
171
+
172
+ # If --version fails, try to get it from the help output
173
+ if result.returncode != 0:
174
+ result = subprocess.run(
175
+ ["ANARCI", "--help"],
176
+ capture_output=True,
177
+ text=True,
178
+ check=False
179
+ )
180
+
181
+ return {
182
+ "stdout": result.stdout,
183
+ "stderr": result.stderr
184
+ }
185
+ except Exception as e:
186
+ return {
187
+ "error": f"Could not determine ANARCI version: {str(e)}"
188
+ }
189
+
190
+ if __name__ == "__main__":
191
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_anarci/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-anarci:
5
+ build: .
6
+ image: mcp-anarci:latest
7
+ container_name: mcp-anarci
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=anarci
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_anarci/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - anarci
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_anarci/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-cellmigration/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-cellmigration via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-cellmigration -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-cellmigration_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-cellmigration_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-cellmigration_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import List, Optional, Dict, Any
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # In a real MCP environment, this would be provided by the MCP framework.
8
+ class mcp:
9
+ @staticmethod
10
+ def tool():
11
+ def decorator(func):
12
+ return func
13
+ return decorator
14
+
15
+ from mcp.server.fastmcp import FastMCP
16
+
17
+ SERVER_NAME = 'local_bioconductor_cellmigration'
18
+ mcp = FastMCP(SERVER_NAME)
19
+
20
+ @mcp.tool()
21
+ def run_cellmigration_analysis(
22
+ input_file: Path,
23
+ output_dir: Path,
24
+ delta_t: float = 1.0,
25
+ x_microns: float = 1.0,
26
+ y_microns: float = 1.0,
27
+ plot_format: str = "png",
28
+ plot_width: int = 7,
29
+ plot_height: int = 7,
30
+ plot_dpi: int = 300,
31
+ generate_displacement_plot: bool = True,
32
+ generate_speed_plot: bool = True,
33
+ generate_rose_plot: bool = True,
34
+ generate_angle_plot: bool = True,
35
+ generate_straighness_plot: bool = True,
36
+ generate_persistence_ratio_plot: bool = True,
37
+ generate_displacement_ratio_plot: bool = True,
38
+ generate_persistence_plot: bool = True,
39
+ generate_cumulative_plot: bool = True,
40
+ generate_cumulative_hist_plot: bool = True,
41
+ generate_single_cell_plot: bool = True,
42
+ generate_all_cells_plot: bool = True,
43
+ ) -> Dict[str, Any]:
44
+ """
45
+ Performs a complete cell migration analysis using the Bioconductor cellmigration package.
46
+
47
+ This tool reads ImageJ trajectory data, performs a comprehensive analysis,
48
+ and generates a variety of plots and a summary statistics CSV file.
49
+ It requires R and the 'cellmigration' Bioconductor package to be installed.
50
+
51
+ Args:
52
+ input_file: Path to the input data file from ImageJ's manual tracking plugin.
53
+ output_dir: Path to the directory where output files will be saved.
54
+ delta_t: Time interval between frames (dt).
55
+ x_microns: Pixel to micron conversion factor for the x-axis (x.scale).
56
+ y_microns: Pixel to micron conversion factor for the y-axis (y.scale).
57
+ plot_format: The format for the output plots ('png' or 'pdf').
58
+ plot_width: Width of the plots in inches.
59
+ plot_height: Height of the plots in inches.
60
+ plot_dpi: Resolution for PNG plots in dots per inch.
61
+ generate_displacement_plot: Generate a displacement plot.
62
+ generate_speed_plot: Generate a speed plot.
63
+ generate_rose_plot: Generate a rose plot of cell turning angles.
64
+ generate_angle_plot: Generate a plot of cell turning angles.
65
+ generate_straighness_plot: Generate a plot of cell trajectory straightness.
66
+ generate_persistence_ratio_plot: Generate a persistence ratio plot.
67
+ generate_displacement_ratio_plot: Generate a displacement ratio plot.
68
+ generate_persistence_plot: Generate a persistence time plot.
69
+ generate_cumulative_plot: Generate a cumulative plot.
70
+ generate_cumulative_hist_plot: Generate a cumulative plot with a histogram.
71
+ generate_single_cell_plot: Generate a plot for each single cell trajectory.
72
+ generate_all_cells_plot: Generate a plot showing all cell trajectories.
73
+
74
+ Returns:
75
+ A dictionary containing the executed command, stdout, stderr, and a list of output files.
76
+ """
77
+ # --- Input Validation ---
78
+ if not input_file.is_file():
79
+ raise FileNotFoundError(f"Input file not found: {input_file}")
80
+
81
+ if delta_t <= 0:
82
+ raise ValueError("delta_t must be a positive number.")
83
+ if x_microns <= 0 or y_microns <= 0:
84
+ raise ValueError("x_microns and y_microns must be positive numbers.")
85
+ if plot_format not in ["png", "pdf"]:
86
+ raise ValueError("plot_format must be either 'png' or 'pdf'.")
87
+ if plot_width <= 0 or plot_height <= 0 or plot_dpi <= 0:
88
+ raise ValueError("Plot dimensions and DPI must be positive integers.")
89
+
90
+ # --- File Path Handling ---
91
+ try:
92
+ output_dir.mkdir(parents=True, exist_ok=True)
93
+ except OSError as e:
94
+ raise OSError(f"Could not create output directory: {output_dir}. Error: {e}")
95
+
96
+ # --- R Script Generation ---
97
+ # Using absolute paths to avoid issues with working directories
98
+ abs_input_file = input_file.resolve()
99
+ abs_output_dir = output_dir.resolve()
100
+
101
+ # Helper function to generate R plotting code
102
+ def get_plot_code(plot_name: str, plot_function: str) -> str:
103
+ return f"""
104
+ cat("Generating {plot_name} plot...\\n")
105
+ if (plot_format == "png") {{
106
+ png(file.path("{abs_output_dir}", "{plot_name}.png"), width=plot_width, height=plot_height, units="in", res=plot_dpi)
107
+ }} else {{
108
+ pdf(file.path("{abs_output_dir}", "{plot_name}.pdf"), width=plot_width, height=plot_height)
109
+ }}
110
+ {plot_function}(analysis_results)
111
+ dev.off()
112
+ """
113
+
114
+ r_script_content = f"""
115
+ # Load the library, with error handling
116
+ if (!requireNamespace("cellmigration", quietly = TRUE)) {{
117
+ stop("The 'cellmigration' package is not installed. Please install it from Bioconductor.", call. = FALSE)
118
+ }}
119
+ library(cellmigration)
120
+
121
+ # --- Parameters ---
122
+ inputFile <- "{abs_input_file}"
123
+ outputDir <- "{abs_output_dir}"
124
+ dt <- {delta_t}
125
+ x_scale <- {x_microns}
126
+ y_scale <- {y_microns}
127
+ plot_format <- "{plot_format}"
128
+ plot_width <- {plot_width}
129
+ plot_height <- {plot_height}
130
+ plot_dpi <- {plot_dpi}
131
+
132
+ # --- Main Analysis ---
133
+ cat("Reading ImageJ data from:", inputFile, "\\n")
134
+ ij_data <- read.ijdata(inputFile, dt=dt, x.scale=x_scale, y.scale=y_scale)
135
+
136
+ cat("Analyzing trajectories...\\n")
137
+ analysis_results <- analyze.trajectories(ij_data)
138
+
139
+ cat("Saving analysis summary...\\n")
140
+ write.csv(analysis_results$results, file=file.path(outputDir, "analysis_summary.csv"), row.names = FALSE)
141
+
142
+ # --- Plotting ---
143
+ if ({str(generate_displacement_plot).upper()}) {{{get_plot_code('displacement', 'plot.displacement')}}}
144
+ if ({str(generate_speed_plot).upper()}) {{{get_plot_code('speed', 'plot.speed')}}}
145
+ if ({str(generate_rose_plot).upper()}) {{{get_plot_code('rose', 'plot.rose')}}}
146
+ if ({str(generate_angle_plot).upper()}) {{{get_plot_code('angle', 'plot.angle')}}}
147
+ if ({str(generate_straighness_plot).upper()}) {{{get_plot_code('straighness', 'plot.straighness')}}}
148
+ if ({str(generate_persistence_ratio_plot).upper()}) {{{get_plot_code('persistence_ratio', 'plot.persistence.ratio')}}}
149
+ if ({str(generate_displacement_ratio_plot).upper()}) {{{get_plot_code('displacement_ratio', 'plot.displacement.ratio')}}}
150
+ if ({str(generate_persistence_plot).upper()}) {{{get_plot_code('persistence', 'plot.persistence')}}}
151
+ if ({str(generate_cumulative_plot).upper()}) {{{get_plot_code('cumulative', 'plot.cummulative.plot')}}}
152
+ if ({str(generate_cumulative_hist_plot).upper()}) {{{get_plot_code('cumulative_hist', 'plot.cummulative.plot.and.hist')}}}
153
+ if ({str(generate_single_cell_plot).upper()}) {{{get_plot_code('single_cell', 'plot.single.cell')}}}
154
+ if ({str(generate_all_cells_plot).upper()}) {{{get_plot_code('all_cells', 'plot.all.cells')}}}
155
+
156
+ cat("Analysis complete.\\n")
157
+ """
158
+
159
+ # --- Subprocess Execution ---
160
+ command = []
161
+ stdout_str, stderr_str = "", ""
162
+ output_files = []
163
+
164
+ try:
165
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file:
166
+ r_script_file.write(r_script_content)
167
+ r_script_path = r_script_file.name
168
+
169
+ command = ["Rscript", r_script_path]
170
+ process = subprocess.run(
171
+ command,
172
+ capture_output=True,
173
+ text=True,
174
+ check=True,
175
+ )
176
+ stdout_str = process.stdout
177
+ stderr_str = process.stderr
178
+
179
+ # Collect output files
180
+ output_files = [str(p) for p in abs_output_dir.glob('*')]
181
+
182
+ except FileNotFoundError:
183
+ stderr_str = "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH."
184
+ return {
185
+ "command_executed": " ".join(command),
186
+ "stdout": stdout_str,
187
+ "stderr": stderr_str,
188
+ "output_files": []
189
+ }
190
+ except subprocess.CalledProcessError as e:
191
+ stderr_str = f"Error executing R script. Return code: {e.returncode}\n"
192
+ stderr_str += f"STDOUT:\n{e.stdout}\n"
193
+ stderr_str += f"STDERR:\n{e.stderr}\n"
194
+ return {
195
+ "command_executed": " ".join(command),
196
+ "stdout": e.stdout,
197
+ "stderr": stderr_str,
198
+ "output_files": []
199
+ }
200
+ finally:
201
+ # Clean up the temporary R script
202
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
203
+ Path(r_script_path).unlink()
204
+
205
+ # --- Structured Result Return ---
206
+ return {
207
+ "command_executed": " ".join(command),
208
+ "stdout": stdout_str,
209
+ "stderr": stderr_str,
210
+ "output_files": output_files
211
+ }
212
+
213
+ if __name__ == "__main__":
214
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_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-cellmigration/app/bioconductor-cellmigration_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_cellmigration'
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-cellmigration/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-cellmigration/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-cellmigration:
5
+ build: .
6
+ image: mcp-bioconductor-cellmigration:latest
7
+ container_name: mcp-bioconductor-cellmigration
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-cellmigration
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-cellmigration/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-cellmigration
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-cellmigration/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-geneplotter/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-glmgampoi/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-glmgampoi via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-glmgampoi -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-glmgampoi_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-glmgampoi_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-glmgampoi_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-glmgampoi/app/bioconductor-glmgampoi_server.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_glmgampoi'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def fit_glm_gp(
13
+ data_path: str,
14
+ output_rds: str,
15
+ design_formula: str = "~1",
16
+ col_data_path: Optional[str] = None,
17
+ size_factors: str = "normed_sum",
18
+ overdispersion_shrinkage: bool = True,
19
+ on_disk: bool = False,
20
+ ) -> Dict[str, Any]:
21
+ """
22
+ Fit a Gamma-Poisson Generalized Linear Model (GLM) to count data.
23
+ Designed for large datasets like single-cell RNA-seq.
24
+
25
+ Args:
26
+ data_path: Path to the input count matrix (CSV or TSV). Rows are genes, columns are samples.
27
+ output_rds: Path where the fitted model object (RDS format) will be saved.
28
+ design_formula: R-style formula string (e.g., '~ condition').
29
+ col_data_path: Optional path to sample metadata (CSV or TSV). Rows must match columns in data_path.
30
+ size_factors: Method for normalization. Options: 'normed_sum', 'poscounts', 'median', or 'none'.
31
+ overdispersion_shrinkage: Whether to shrink overdispersion estimates towards a global trend.
32
+ on_disk: If True, uses HDF5-backed matrices to minimize memory usage for very large datasets.
33
+ """
34
+ # Input validation
35
+ input_p = Path(data_path)
36
+ if not input_p.exists():
37
+ raise FileNotFoundError(f"Input data file not found: {data_path}")
38
+
39
+ output_p = Path(output_rds)
40
+ output_p.parent.mkdir(parents=True, exist_ok=True)
41
+
42
+ col_data_arg = "NULL"
43
+ if col_data_path:
44
+ col_p = Path(col_data_path)
45
+ if not col_p.exists():
46
+ raise FileNotFoundError(f"Metadata file not found: {col_data_path}")
47
+ col_data_arg = f"read_data('{col_data_path}')"
48
+
49
+ # Map size_factors string to R logic
50
+ sf_val = f"'{size_factors}'"
51
+ if size_factors.lower() == "none":
52
+ sf_val = "FALSE"
53
+
54
+ # Construct R script
55
+ r_script = f"""
56
+ library(glmGamPoi)
57
+
58
+ read_data <- function(path) {{
59
+ if (grepl('\\\\.tsv$', path, ignore.case = TRUE)) {{
60
+ return(read.delim(path, row.names = 1, check.names = FALSE))
61
+ }} else {{
62
+ return(read.csv(path, row.names = 1, check.names = FALSE))
63
+ }}
64
+ }}
65
+
66
+ counts <- as.matrix(read_data('{data_path}'))
67
+ col_data <- {col_data_arg}
68
+
69
+ fit <- glm_gp(
70
+ data = counts,
71
+ design = as.formula('{design_formula}'),
72
+ col_data = col_data,
73
+ size_factors = {sf_val},
74
+ overdispersion_shrinkage = {str(overdispersion_shrinkage).upper()},
75
+ on_disk = {str(on_disk).upper()}
76
+ )
77
+
78
+ saveRDS(fit, '{output_rds}')
79
+ """
80
+
81
+ try:
82
+ process = subprocess.run(
83
+ ["Rscript", "-e", r_script],
84
+ capture_output=True,
85
+ text=True,
86
+ check=True
87
+ )
88
+ return {
89
+ "command_executed": f"glm_gp on {data_path}",
90
+ "stdout": process.stdout,
91
+ "stderr": process.stderr,
92
+ "output_files": [output_rds]
93
+ }
94
+ except subprocess.CalledProcessError as e:
95
+ return {
96
+ "error": "R execution failed",
97
+ "stdout": e.stdout,
98
+ "stderr": e.stderr,
99
+ "command_executed": e.cmd
100
+ }
101
+
102
+ @mcp.tool()
103
+ def test_differential_expression(
104
+ fit_rds_path: str,
105
+ output_csv: str,
106
+ contrast: Optional[str] = None,
107
+ reduced_design: Optional[str] = None,
108
+ ) -> Dict[str, Any]:
109
+ """
110
+ Perform differential expression (DE) testing using a fitted glmGamPoi model.
111
+
112
+ Args:
113
+ fit_rds_path: Path to the RDS file generated by fit_glm_gp.
114
+ output_csv: Path where the DE results (CSV) will be saved.
115
+ contrast: String specifying the contrast to test (e.g., 'condition_B - condition_A').
116
+ reduced_design: Optional R-style formula for a reduced model to perform a Likelihood Ratio Test.
117
+ """
118
+ fit_p = Path(fit_rds_path)
119
+ if not fit_p.exists():
120
+ raise FileNotFoundError(f"Fitted model file not found: {fit_rds_path}")
121
+
122
+ output_p = Path(output_csv)
123
+ output_p.parent.mkdir(parents=True, exist_ok=True)
124
+
125
+ contrast_arg = "NULL"
126
+ if contrast:
127
+ # If it looks like a simple string, wrap in quotes; if it's complex, R handles it
128
+ contrast_arg = f"'{contrast}'"
129
+
130
+ reduced_arg = "NULL"
131
+ if reduced_design:
132
+ reduced_arg = f"as.formula('{reduced_design}')"
133
+
134
+ r_script = f"""
135
+ library(glmGamPoi)
136
+ fit <- readRDS('{fit_rds_path}')
137
+ res <- test_de(
138
+ fit,
139
+ contrast = {contrast_arg},
140
+ reduced_design = {reduced_arg}
141
+ )
142
+ write.csv(res, '{output_csv}', row.names = TRUE)
143
+ """
144
+
145
+ try:
146
+ process = subprocess.run(
147
+ ["Rscript", "-e", r_script],
148
+ capture_output=True,
149
+ text=True,
150
+ check=True
151
+ )
152
+ return {
153
+ "command_executed": f"test_de on {fit_rds_path}",
154
+ "stdout": process.stdout,
155
+ "stderr": process.stderr,
156
+ "output_files": [output_csv]
157
+ }
158
+ except subprocess.CalledProcessError as e:
159
+ return {
160
+ "error": "R execution failed",
161
+ "stdout": e.stdout,
162
+ "stderr": e.stderr,
163
+ "command_executed": e.cmd
164
+ }
165
+
166
+ @mcp.tool()
167
+ def get_overdispersion_estimates(
168
+ fit_rds_path: str,
169
+ output_csv: str
170
+ ) -> Dict[str, Any]:
171
+ """
172
+ Extract overdispersion estimates for each gene from a fitted glmGamPoi model.
173
+
174
+ Args:
175
+ fit_rds_path: Path to the RDS file generated by fit_glm_gp.
176
+ output_csv: Path where the overdispersion estimates (CSV) will be saved.
177
+ """
178
+ fit_p = Path(fit_rds_path)
179
+ if not fit_p.exists():
180
+ raise FileNotFoundError(f"Fitted model file not found: {fit_rds_path}")
181
+
182
+ r_script = f"""
183
+ library(glmGamPoi)
184
+ fit <- readRDS('{fit_rds_path}')
185
+ # Extract overdispersion and gene names
186
+ df <- data.frame(
187
+ gene = rownames(fit$Beta),
188
+ overdispersion = fit$overdispersions
189
+ )
190
+ write.csv(df, '{output_csv}', row.names = FALSE)
191
+ """
192
+
193
+ try:
194
+ process = subprocess.run(
195
+ ["Rscript", "-e", r_script],
196
+ capture_output=True,
197
+ text=True,
198
+ check=True
199
+ )
200
+ return {
201
+ "command_executed": f"Extract overdispersion from {fit_rds_path}",
202
+ "stdout": process.stdout,
203
+ "stderr": process.stderr,
204
+ "output_files": [output_csv]
205
+ }
206
+ except subprocess.CalledProcessError as e:
207
+ return {
208
+ "error": "R execution failed",
209
+ "stdout": e.stdout,
210
+ "stderr": e.stderr,
211
+ "command_executed": e.cmd
212
+ }
213
+
214
+ @mcp.tool()
215
+ def predict_glm_gp(
216
+ fit_rds_path: str,
217
+ output_csv: str,
218
+ newdata_path: Optional[str] = None,
219
+ type: str = "link"
220
+ ) -> Dict[str, Any]:
221
+ """
222
+ Predict values (e.g., log-counts or counts) from a fitted glmGamPoi model.
223
+
224
+ Args:
225
+ fit_rds_path: Path to the RDS file generated by fit_glm_gp.
226
+ output_csv: Path where the predictions (CSV) will be saved.
227
+ newdata_path: Optional path to new sample metadata (CSV/TSV). If NULL, uses original data.
228
+ type: Type of prediction. 'link' (log-scale) or 'response' (original count scale).
229
+ """
230
+ fit_p = Path(fit_rds_path)
231
+ if not fit_p.exists():
232
+ raise FileNotFoundError(f"Fitted model file not found: {fit_rds_path}")
233
+
234
+ newdata_arg = "NULL"
235
+ if newdata_path:
236
+ if not Path(newdata_path).exists():
237
+ raise FileNotFoundError(f"New data file not found: {newdata_path}")
238
+ newdata_arg = f"read.csv('{newdata_path}', row.names = 1)"
239
+
240
+ r_script = f"""
241
+ library(glmGamPoi)
242
+ fit <- readRDS('{fit_rds_path}')
243
+ newdata <- {newdata_arg}
244
+ preds <- predict(fit, newdata = newdata, type = '{type}')
245
+ write.csv(preds, '{output_csv}')
246
+ """
247
+
248
+ try:
249
+ process = subprocess.run(
250
+ ["Rscript", "-e", r_script],
251
+ capture_output=True,
252
+ text=True,
253
+ check=True
254
+ )
255
+ return {
256
+ "command_executed": f"predict on {fit_rds_path}",
257
+ "stdout": process.stdout,
258
+ "stderr": process.stderr,
259
+ "output_files": [output_csv]
260
+ }
261
+ except subprocess.CalledProcessError as e:
262
+ return {
263
+ "error": "R execution failed",
264
+ "stdout": e.stdout,
265
+ "stderr": e.stderr,
266
+ "command_executed": e.cmd
267
+ }
268
+
269
+ if __name__ == "__main__":
270
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-glmgampoi/app/bioconductor-glmgampoi_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-glmgampoi/app/bioconductor-glmgampoi_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_glmgampoi'
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-glmgampoi/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-glmgampoi/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-glmgampoi
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-keggrest/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-keggrest via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-keggrest -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-keggrest_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-keggrest_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-keggrest_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-keggrest/app/bioconductor-keggrest_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-keggrest/app/bioconductor-keggrest_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_keggrest'
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-keggrest/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-keggrest:
5
+ build: .
6
+ image: mcp-bioconductor-keggrest:latest
7
+ container_name: mcp-bioconductor-keggrest
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-keggrest
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-keggrest/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-keggrest
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-keggrest/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-scran/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-scran via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-scran -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-scran_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-scran_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-scran_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-scran/app/bioconductor-scran_server.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # MCP decorator is omitted as per instructions.
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_bioconductor_scran'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+
14
+ def scran_normalize(
15
+ input_sce_rds: Path,
16
+ output_sce_rds: Path,
17
+ clusters_csv: Optional[Path] = None,
18
+ min_mean: float = 0.1,
19
+ bpparam_workers: int = 1,
20
+ ) -> dict:
21
+ """
22
+ Performs normalization of single-cell RNA-seq data using scran's deconvolution method.
23
+
24
+ This tool serves as a wrapper for a hypothetical R script that utilizes the
25
+ 'scran' Bioconductor package. 'scran' itself is an R library without a native
26
+ command-line interface. This function assumes an R script ('run_scran.R')
27
+ exists that can take command-line arguments to perform normalization.
28
+
29
+ Args:
30
+ input_sce_rds (Path): Path to the input SingleCellExperiment object saved as an RDS file.
31
+ This object should contain the raw count matrix.
32
+ output_sce_rds (Path): Path to save the output SingleCellExperiment object (in RDS format)
33
+ with the calculated size factors added.
34
+ clusters_csv (Optional[Path]): Path to a CSV file containing cell cluster assignments.
35
+ If provided, normalization is performed per cluster.
36
+ The CSV should have two columns: cell_barcode, cluster_id.
37
+ min_mean (float): The minimum mean expression for a gene to be used for normalization.
38
+ Corresponds to the 'min.mean' parameter in scran's computeSumFactors.
39
+ Defaults to 0.1.
40
+ bpparam_workers (int): Number of worker processes to use for parallel computation.
41
+ Defaults to 1 (no parallelization).
42
+
43
+ Returns:
44
+ dict: A dictionary containing the executed command, stdout, stderr, and a list
45
+ of output files.
46
+ """
47
+ # --- Input Validation ---
48
+ if not input_sce_rds.is_file():
49
+ raise FileNotFoundError(f"Input RDS file not found: {input_sce_rds}")
50
+ if clusters_csv and not clusters_csv.is_file():
51
+ raise FileNotFoundError(f"Clusters CSV file not found: {clusters_csv}")
52
+ if min_mean < 0:
53
+ raise ValueError("min_mean must be a non-negative number.")
54
+ if bpparam_workers < 1:
55
+ raise ValueError("bpparam_workers must be at least 1.")
56
+
57
+ # --- Command Construction ---
58
+ # This assumes a hypothetical R script 'run_scran.R' that we have created
59
+ # to expose scran functionality to the command line.
60
+ cmd = [
61
+ "Rscript",
62
+ "run_scran.R",
63
+ "normalize",
64
+ "--input",
65
+ str(input_sce_rds),
66
+ "--output",
67
+ str(output_sce_rds),
68
+ "--min-mean",
69
+ str(min_mean),
70
+ "--workers",
71
+ str(bpparam_workers),
72
+ ]
73
+
74
+ if clusters_csv:
75
+ cmd.extend(["--clusters", str(clusters_csv)])
76
+
77
+ # --- Subprocess Execution ---
78
+ command_executed = " ".join(cmd)
79
+ try:
80
+ # In a real scenario, the R script would need to be in the PATH or specified with its full path.
81
+ # For this example, we assume it's available.
82
+ result = subprocess.run(
83
+ cmd,
84
+ capture_output=True,
85
+ text=True,
86
+ check=True,
87
+ )
88
+ stdout = result.stdout
89
+ stderr = result.stderr
90
+ except FileNotFoundError:
91
+ # This error would be raised if 'Rscript' or 'run_scran.R' is not found.
92
+ return {
93
+ "command_executed": command_executed,
94
+ "stdout": "",
95
+ "stderr": "Error: 'Rscript' or the wrapper 'run_scran.R' not found. Please ensure R and the necessary wrapper script are in the system's PATH.",
96
+ "output_files": [],
97
+ }
98
+ except subprocess.CalledProcessError as e:
99
+ return {
100
+ "command_executed": command_executed,
101
+ "stdout": e.stdout,
102
+ "stderr": e.stderr,
103
+ "output_files": [],
104
+ }
105
+
106
+ # --- Return Structured Output ---
107
+ return {
108
+ "command_executed": command_executed,
109
+ "stdout": stdout,
110
+ "stderr": stderr,
111
+ "output_files": [str(output_sce_rds)],
112
+ }
113
+
114
+ if __name__ == "__main__":
115
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-scran/app/bioconductor-scran_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-scran/app/bioconductor-scran_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_scran'
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-scran/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-scran/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-scran:
5
+ build: .
6
+ image: mcp-bioconductor-scran:latest
7
+ container_name: mcp-bioconductor-scran
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-scran
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-scran/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-scran
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-scran/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-spari/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-spari via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-spari -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-spari_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-spari_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-spari_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-spari/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-spari:
5
+ build: .
6
+ image: mcp-bioconductor-spari:latest
7
+ container_name: mcp-bioconductor-spari
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-spari
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-spari/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-spari
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-spari/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-spatialcpie/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-spatialcpie via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-spatialcpie -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-spatialcpie_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-spatialcpie_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-spatialcpie_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-spatialcpie/app/bioconductor-spatialcpie_server.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import List, Optional, Dict, Any
4
+
5
+ # Assume @mcp.tool is defined elsewhere or imported.
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_bioconductor_spatialcpie'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def run_rscript(
14
+ script_file: Optional[Path] = None,
15
+ expressions: Optional[List[str]] = None,
16
+ script_args: Optional[List[str]] = None,
17
+ help_flag: bool = False,
18
+ version_flag: bool = False,
19
+ verbose: bool = False,
20
+ default_packages: Optional[str] = None,
21
+ save_workspace: bool = False,
22
+ no_environ: bool = False,
23
+ no_site_file: bool = False,
24
+ no_init_file: bool = False,
25
+ restore_workspace: bool = False,
26
+ vanilla: bool = False,
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Executes an R script or R expressions using the Rscript command-line tool.
30
+
31
+ This tool provides an interface to the Rscript command, allowing users to run
32
+ R code from the command line. It supports running either a specified R script
33
+ file or direct R expressions. Various options are available to control the
34
+ R session environment, such as package loading, workspace saving/restoring,
35
+ and environment file handling.
36
+
37
+ Args:
38
+ script_file: Path to an R script file to be executed. Mutually exclusive with 'expressions'.
39
+ expressions: A list of R expressions (strings) to be executed. Mutually exclusive with 'script_file'.
40
+ script_args: A list of arguments (strings) to be passed to the R script itself.
41
+ help_flag: If True, print usage information and exit.
42
+ version_flag: If True, print Rscript version information and exit.
43
+ verbose: If True, print information on progress during execution.
44
+ default_packages: A comma-separated list of package names to load by default, or 'NULL'.
45
+ save_workspace: If True, save the workspace at the end of the session.
46
+ no_environ: If True, do not read the site and user environment files.
47
+ no_site_file: If True, do not read the site-wide Rprofile.
48
+ no_init_file: If True, do not read the user R profile.
49
+ restore_workspace: If True, restore previously saved objects at startup.
50
+ vanilla: If True, combine --no-save, --no-restore, --no-site-file,
51
+ --no-init-file, and --no-environ.
52
+
53
+ Returns:
54
+ A dictionary containing the command executed, stdout, stderr, and any
55
+ output files (though Rscript itself doesn't typically produce named
56
+ output files directly from the command line).
57
+ """
58
+ # Input validation
59
+ if script_file and expressions:
60
+ raise ValueError("Cannot specify both 'script_file' and 'expressions'. Please choose one.")
61
+ if not script_file and not expressions and not help_flag and not version_flag:
62
+ raise ValueError(
63
+ "Either 'script_file' or 'expressions' must be provided, "
64
+ "unless '--help' or '--version' is requested."
65
+ )
66
+
67
+ if script_file:
68
+ if not script_file.exists():
69
+ raise FileNotFoundError(f"R script file not found: {script_file}")
70
+ if not script_file.is_file():
71
+ raise ValueError(f"Provided path is not a file: {script_file}")
72
+
73
+ command = ["Rscript"]
74
+
75
+ # Add Rscript options
76
+ if help_flag:
77
+ command.append("--help")
78
+ if version_flag:
79
+ command.append("--version")
80
+ if verbose:
81
+ command.append("--verbose")
82
+ if default_packages is not None:
83
+ command.append(f"--default-packages={default_packages}")
84
+ if save_workspace:
85
+ command.append("--save")
86
+ if no_environ:
87
+ command.append("--no-environ")
88
+ if no_site_file:
89
+ command.append("--no-site-file")
90
+ if no_init_file:
91
+ command.append("--no-init-file")
92
+ if restore_workspace:
93
+ command.append("--restore")
94
+ if vanilla:
95
+ command.append("--vanilla")
96
+
97
+ # Add expressions or script file
98
+ if expressions:
99
+ for expr in expressions:
100
+ command.extend(["-e", expr])
101
+ elif script_file:
102
+ command.append(str(script_file))
103
+
104
+ # Add arguments to the R script itself
105
+ if script_args:
106
+ command.extend(script_args)
107
+
108
+ command_executed = " ".join(command)
109
+ stdout = ""
110
+ stderr = ""
111
+ output_files: List[Path] = []
112
+
113
+ try:
114
+ process = subprocess.run(
115
+ command,
116
+ capture_output=True,
117
+ text=True,
118
+ check=True
119
+ )
120
+ stdout = process.stdout
121
+ stderr = process.stderr
122
+ except subprocess.CalledProcessError as e:
123
+ return {
124
+ "command_executed": command_executed,
125
+ "stdout": e.stdout,
126
+ "stderr": e.stderr,
127
+ "error": f"Rscript execution failed with exit code {e.returncode}: {e}",
128
+ "returncode": e.returncode,
129
+ "output_files": output_files,
130
+ }
131
+ except FileNotFoundError:
132
+ return {
133
+ "command_executed": command_executed,
134
+ "stdout": "",
135
+ "stderr": "Rscript command not found. Please ensure R is installed and in your system's PATH.",
136
+ "error": "Rscript executable not found.",
137
+ "returncode": 127,
138
+ "output_files": output_files,
139
+ }
140
+ except Exception as e:
141
+ return {
142
+ "command_executed": command_executed,
143
+ "stdout": stdout,
144
+ "stderr": stderr,
145
+ "error": f"An unexpected error occurred: {e}",
146
+ "returncode": 1, # Generic error code
147
+ "output_files": output_files,
148
+ }
149
+
150
+ return {
151
+ "command_executed": command_executed,
152
+ "stdout": stdout,
153
+ "stderr": stderr,
154
+ "output_files": output_files,
155
+ }
156
+
157
+ if __name__ == "__main__":
158
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-spatialcpie/app/bioconductor-spatialcpie_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-spatialcpie/app/bioconductor-spatialcpie_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_spatialcpie'
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-spatialcpie/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-spatialcpie:
5
+ build: .
6
+ image: mcp-bioconductor-spatialcpie:latest
7
+ container_name: mcp-bioconductor-spatialcpie
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-spatialcpie
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-spatialcpie/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-spatialcpie
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-spatialcpie/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-spoon/app/bioconductor-spoon_server.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import List
6
+
7
+ # MCP decorator would be here in a real implementation
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ SERVER_NAME = 'local_bioconductor_spoon'
11
+ mcp = FastMCP(SERVER_NAME)
12
+
13
+ @mcp.tool()
14
+ def run_spoon(
15
+ input_spe: Path,
16
+ output_spe: Path,
17
+ assay_name: str = "counts",
18
+ n_threads: int = 1,
19
+ ) -> dict:
20
+ """
21
+ Runs the spoon workflow to address the mean-variance relationship in spatial transcriptomics data.
22
+
23
+ This tool takes a SpatialExperiment object (in .rds format), computes precision weights
24
+ and deviance residuals using Empirical Bayes techniques, and adds them to the object.
25
+ The output is the modified SpatialExperiment object, saved to a new .rds file.
26
+
27
+ Args:
28
+ input_spe: Path to the input SpatialExperiment object saved as an RDS file.
29
+ output_spe: Path to save the output SpatialExperiment object as an RDS file.
30
+ assay_name: The name of the assay to use from the input object.
31
+ n_threads: The number of threads to use for parallel processing.
32
+
33
+ Returns:
34
+ A dictionary containing the command executed, stdout, stderr, and a list of output files.
35
+ """
36
+ # 1. Input Validation
37
+ if not input_spe.is_file():
38
+ raise FileNotFoundError(f"Input file not found: {input_spe}")
39
+ if n_threads < 1:
40
+ raise ValueError("n_threads must be a positive integer.")
41
+
42
+ # Ensure output directory exists
43
+ output_spe.parent.mkdir(parents=True, exist_ok=True)
44
+
45
+ # 2. R Script Generation
46
+ # This R script acts as a command-line wrapper for the spoon::run_spoon function.
47
+ r_script_content = f"""
48
+ # Ensure required R packages are installed and load them
49
+ if (!requireNamespace("optparse", quietly = TRUE)) stop("R package 'optparse' is required.")
50
+ if (!requireNamespace("spoon", quietly = TRUE)) stop("R package 'spoon' is required.")
51
+ if (!requireNamespace("SpatialExperiment", quietly = TRUE)) stop("R package 'SpatialExperiment' is required.")
52
+ if (!requireNamespace("BiocParallel", quietly = TRUE)) stop("R package 'BiocParallel' is required.")
53
+
54
+ library(optparse)
55
+ library(spoon)
56
+ library(SpatialExperiment)
57
+ library(BiocParallel)
58
+
59
+ # Define and parse command-line options
60
+ option_list <- list(
61
+ make_option(c("-i", "--input"), type="character", default=NULL,
62
+ help="Input SpatialExperiment RDS file", metavar="character"),
63
+ make_option(c("-o", "--output"), type="character", default=NULL,
64
+ help="Output SpatialExperiment RDS file", metavar="character"),
65
+ make_option(c("-a", "--assay_name"), type="character", default="counts",
66
+ help="Name of the assay to use [default= %default]", metavar="character"),
67
+ make_option(c("-n", "--n_threads"), type="integer", default=1,
68
+ help="Number of threads for parallel processing [default= %default]", metavar="integer")
69
+ )
70
+ opt_parser <- OptionParser(option_list=option_list)
71
+ opt <- parse_args(opt_parser)
72
+
73
+ # Validate required arguments
74
+ if (is.null(opt$input) || is.null(opt$output)){{
75
+ print_help(opt_parser)
76
+ stop("Input and output files must be supplied.", call.=FALSE)
77
+ }}
78
+
79
+ # Set up parallel processing backend
80
+ if (opt$n_threads > 1) {{
81
+ bpparam <- MulticoreParam(workers = opt$n_threads)
82
+ }} else {{
83
+ bpparam <- SerialParam()
84
+ }}
85
+
86
+ # Main workflow
87
+ message("Reading input SpatialExperiment object from: ", opt$input)
88
+ spe <- readRDS(opt$input)
89
+
90
+ message("Running spoon workflow with assay '", opt$assay_name, "' and ", opt$n_threads, " thread(s)...")
91
+ spe_processed <- tryCatch({{
92
+ run_spoon(
93
+ spe,
94
+ assay_name = opt$assay_name,
95
+ BPPARAM = bpparam
96
+ )
97
+ }}, error = function(e) {{
98
+ stop("Error during spoon::run_spoon execution: ", e$message)
99
+ }})
100
+ message("Spoon workflow completed successfully.")
101
+
102
+ message("Saving processed SpatialExperiment object to: ", opt$output)
103
+ saveRDS(spe_processed, file = opt$output)
104
+
105
+ message("Done.")
106
+ """
107
+
108
+ # 3. Subprocess Execution
109
+ # Use a temporary file for the R script
110
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script:
111
+ tmp_r_script.write(r_script_content)
112
+ r_script_path = Path(tmp_r_script.name)
113
+
114
+ # Check for Rscript executable
115
+ if not shutil.which("Rscript"):
116
+ r_script_path.unlink()
117
+ raise RuntimeError("Rscript executable not found in PATH. R is required to run this tool.")
118
+
119
+ cmd = [
120
+ "Rscript",
121
+ str(r_script_path),
122
+ "--input", str(input_spe),
123
+ "--output", str(output_spe),
124
+ "--assay_name", assay_name,
125
+ "--n_threads", str(n_threads),
126
+ ]
127
+
128
+ command_executed = " ".join(cmd)
129
+
130
+ try:
131
+ process = subprocess.run(
132
+ cmd,
133
+ capture_output=True,
134
+ text=True,
135
+ check=True,
136
+ )
137
+ stdout = process.stdout
138
+ stderr = process.stderr
139
+ except subprocess.CalledProcessError as e:
140
+ raise RuntimeError(
141
+ f"R script execution failed with exit code {e.returncode}.\n"
142
+ f"Command: {command_executed}\n"
143
+ f"Stdout: {e.stdout}\n"
144
+ f"Stderr: {e.stderr}"
145
+ ) from e
146
+ finally:
147
+ # Ensure the temporary script is always cleaned up
148
+ if r_script_path.exists():
149
+ r_script_path.unlink()
150
+
151
+ # 4. Structured Result Return
152
+ return {
153
+ "command_executed": command_executed,
154
+ "stdout": stdout,
155
+ "stderr": stderr,
156
+ "output_files": [str(output_spe)]
157
+ }
158
+
159
+ if __name__ == "__main__":
160
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-spoon/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-spoon
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-statial/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-statial via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-statial -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-statial_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-statial_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-statial_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-statial/app/bioconductor-statial_server.py ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Union
4
+ import textwrap
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ SERVER_NAME = 'local_bioconductor_statial'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def statial_calc_interactions(
13
+ input_rds: str,
14
+ output_rds: str,
15
+ image_col: str,
16
+ cell_type_col: str,
17
+ x_col: str = "x",
18
+ y_col: str = "y",
19
+ method: str = "radius",
20
+ radius: float = 100.0,
21
+ n_neighbors: int = 10,
22
+ ):
23
+ """
24
+ Calculates the localization of cell types (interactions) for each image in a SingleCellExperiment object.
25
+
26
+ Args:
27
+ input_rds: Path to the input SingleCellExperiment object saved as an RDS file.
28
+ output_rds: Path where the resulting interactions data frame will be saved as an RDS file.
29
+ image_col: Column name in colData representing unique image identifiers.
30
+ cell_type_col: Column name in colData representing cell types.
31
+ x_col: Column name for the X coordinate.
32
+ y_col: Column name for the Y coordinate.
33
+ method: Method to define proximity ('radius' or 'knn').
34
+ radius: The radius to use if method is 'radius'.
35
+ n_neighbors: The number of neighbors to use if method is 'knn'.
36
+ """
37
+ input_path = Path(input_rds)
38
+ output_path = Path(output_rds)
39
+
40
+ if not input_path.exists():
41
+ return {"error": f"Input file {input_rds} does not exist."}
42
+
43
+ if method not in ["radius", "knn"]:
44
+ return {"error": "Method must be either 'radius' or 'knn'."}
45
+
46
+ r_script = textwrap.dedent(f"""
47
+ library(statial)
48
+ sce <- readRDS('{input_path}')
49
+ interactions <- calcInteractions(
50
+ sce,
51
+ group = '{image_col}',
52
+ cellType = '{cell_type_col}',
53
+ spatialCoords = c('{x_col}', '{y_col}'),
54
+ method = '{method}',
55
+ radius = {radius},
56
+ n_neighbors = {n_neighbors}
57
+ )
58
+ saveRDS(interactions, '{output_path}')
59
+ """)
60
+
61
+ try:
62
+ result = subprocess.run(
63
+ ["Rscript", "-e", r_script],
64
+ capture_output=True,
65
+ text=True,
66
+ check=True
67
+ )
68
+ return {
69
+ "command_executed": f"statial::calcInteractions on {input_rds}",
70
+ "stdout": result.stdout,
71
+ "stderr": result.stderr,
72
+ "output_files": [str(output_path)]
73
+ }
74
+ except subprocess.CalledProcessError as e:
75
+ return {
76
+ "error": "R execution failed",
77
+ "stdout": e.stdout,
78
+ "stderr": e.stderr
79
+ }
80
+
81
+ @mcp.tool()
82
+ def statial_get_spatial_context(
83
+ input_rds: str,
84
+ output_rds: str,
85
+ cell_type_col: str,
86
+ x_col: str = "x",
87
+ y_col: str = "y",
88
+ r: float = 100.0,
89
+ k: int = 3,
90
+ image_col: Optional[str] = None
91
+ ):
92
+ """
93
+ Defines spatial contexts by clustering cell type densities across the tissue.
94
+
95
+ Args:
96
+ input_rds: Path to the input SingleCellExperiment object (RDS).
97
+ output_rds: Path to save the SCE object with spatial context assignments (RDS).
98
+ cell_type_col: Column name in colData representing cell types.
99
+ x_col: Column name for the X coordinate.
100
+ y_col: Column name for the Y coordinate.
101
+ r: Radius for calculating cell density.
102
+ k: Number of spatial contexts (clusters) to identify.
103
+ image_col: Optional column name for image IDs if processing multiple images.
104
+ """
105
+ input_path = Path(input_rds)
106
+ output_path = Path(output_rds)
107
+
108
+ if not input_path.exists():
109
+ return {"error": f"Input file {input_rds} does not exist."}
110
+
111
+ image_arg = f"'{image_col}'" if image_col else "NULL"
112
+
113
+ r_script = textwrap.dedent(f"""
114
+ library(statial)
115
+ sce <- readRDS('{input_path}')
116
+ sce <- getSpatialContext(
117
+ sce,
118
+ cellType = '{cell_type_col}',
119
+ spatialCoords = c('{x_col}', '{y_col}'),
120
+ r = {r},
121
+ k = {k},
122
+ group = {image_arg}
123
+ )
124
+ saveRDS(sce, '{output_path}')
125
+ """)
126
+
127
+ try:
128
+ result = subprocess.run(
129
+ ["Rscript", "-e", r_script],
130
+ capture_output=True,
131
+ text=True,
132
+ check=True
133
+ )
134
+ return {
135
+ "command_executed": f"statial::getSpatialContext on {input_rds}",
136
+ "stdout": result.stdout,
137
+ "stderr": result.stderr,
138
+ "output_files": [str(output_path)]
139
+ }
140
+ except subprocess.CalledProcessError as e:
141
+ return {
142
+ "error": "R execution failed",
143
+ "stdout": e.stdout,
144
+ "stderr": e.stderr
145
+ }
146
+
147
+ @mcp.tool()
148
+ def statial_bivariate_test(
149
+ input_rds: str,
150
+ output_csv: str,
151
+ image_col: str,
152
+ cell_type_col: str,
153
+ spatial_context_col: str,
154
+ x_col: str = "x",
155
+ y_col: str = "y"
156
+ ):
157
+ """
158
+ Performs statistical testing to identify interactions that change significantly across spatial contexts.
159
+
160
+ Args:
161
+ input_rds: Path to the input SingleCellExperiment object (RDS).
162
+ output_csv: Path to save the statistical test results (CSV).
163
+ image_col: Column name for image IDs.
164
+ cell_type_col: Column name for cell types.
165
+ spatial_context_col: Column name for the spatial context (calculated via getSpatialContext).
166
+ x_col: Column name for the X coordinate.
167
+ y_col: Column name for the Y coordinate.
168
+ """
169
+ input_path = Path(input_rds)
170
+ output_path = Path(output_csv)
171
+
172
+ if not input_path.exists():
173
+ return {"error": f"Input file {input_rds} does not exist."}
174
+
175
+ r_script = textwrap.dedent(f"""
176
+ library(statial)
177
+ sce <- readRDS('{input_path}')
178
+ results <- bivariateTest(
179
+ sce,
180
+ group = '{image_col}',
181
+ cellType = '{cell_type_col}',
182
+ spatialContext = '{spatial_context_col}',
183
+ spatialCoords = c('{x_col}', '{y_col}')
184
+ )
185
+ write.csv(results, '{output_path}', row.names = FALSE)
186
+ """)
187
+
188
+ try:
189
+ result = subprocess.run(
190
+ ["Rscript", "-e", r_script],
191
+ capture_output=True,
192
+ text=True,
193
+ check=True
194
+ )
195
+ return {
196
+ "command_executed": f"statial::bivariateTest on {input_rds}",
197
+ "stdout": result.stdout,
198
+ "stderr": result.stderr,
199
+ "output_files": [str(output_path)]
200
+ }
201
+ except subprocess.CalledProcessError as e:
202
+ return {
203
+ "error": "R execution failed",
204
+ "stdout": e.stdout,
205
+ "stderr": e.stderr
206
+ }
207
+
208
+ @mcp.tool()
209
+ def statial_impute_interactions(
210
+ input_rds: str,
211
+ output_rds: str,
212
+ image_col: str,
213
+ cell_type_col: str,
214
+ min_cells: int = 5
215
+ ):
216
+ """
217
+ Imputes interaction values for images where certain cell types might be missing or rare.
218
+
219
+ Args:
220
+ input_rds: Path to the interactions data frame (RDS) generated by calcInteractions.
221
+ output_rds: Path to save the imputed interactions data frame (RDS).
222
+ image_col: Column name for image IDs.
223
+ cell_type_col: Column name for cell types.
224
+ min_cells: Minimum number of cells required to avoid imputation.
225
+ """
226
+ input_path = Path(input_rds)
227
+ output_path = Path(output_rds)
228
+
229
+ if not input_path.exists():
230
+ return {"error": f"Input file {input_rds} does not exist."}
231
+
232
+ r_script = textwrap.dedent(f"""
233
+ library(statial)
234
+ interactions <- readRDS('{input_path}')
235
+ imputed <- imputeInteractions(
236
+ interactions,
237
+ group = '{image_col}',
238
+ cellType = '{cell_type_col}',
239
+ minCells = {min_cells}
240
+ )
241
+ saveRDS(imputed, '{output_path}')
242
+ """)
243
+
244
+ try:
245
+ result = subprocess.run(
246
+ ["Rscript", "-e", r_script],
247
+ capture_output=True,
248
+ text=True,
249
+ check=True
250
+ )
251
+ return {
252
+ "command_executed": f"statial::imputeInteractions on {input_rds}",
253
+ "stdout": result.stdout,
254
+ "stderr": result.stderr,
255
+ "output_files": [str(output_path)]
256
+ }
257
+ except subprocess.CalledProcessError as e:
258
+ return {
259
+ "error": "R execution failed",
260
+ "stdout": e.stdout,
261
+ "stderr": e.stderr
262
+ }
263
+
264
+ @mcp.tool()
265
+ def statial_get_interactions(
266
+ input_rds: str,
267
+ output_csv: str,
268
+ image_id: Optional[str] = None
269
+ ):
270
+ """
271
+ Extracts interaction information from a SingleCellExperiment object or interaction data frame.
272
+
273
+ Args:
274
+ input_rds: Path to the input RDS file.
275
+ output_csv: Path to save the extracted interactions (CSV).
276
+ image_id: Optional specific image ID to extract interactions for.
277
+ """
278
+ input_path = Path(input_rds)
279
+ output_path = Path(output_csv)
280
+
281
+ if not input_path.exists():
282
+ return {"error": f"Input file {input_rds} does not exist."}
283
+
284
+ image_filter = f"subset(interact, group == '{image_id}')" if image_id else "interact"
285
+
286
+ r_script = textwrap.dedent(f"""
287
+ library(statial)
288
+ interact <- readRDS('{input_path}')
289
+ # If input is SCE, extract interactions if they exist
290
+ if(is(interact, 'SingleCellExperiment')) {{
291
+ # Assuming interactions were stored in metadata or colData by user
292
+ # This is a helper to convert the object to a readable table
293
+ interact <- as.data.frame(colData(interact))
294
+ }}
295
+
296
+ result_df <- {image_filter}
297
+ write.csv(result_df, '{output_path}', row.names = FALSE)
298
+ """)
299
+
300
+ try:
301
+ result = subprocess.run(
302
+ ["Rscript", "-e", r_script],
303
+ capture_output=True,
304
+ text=True,
305
+ check=True
306
+ )
307
+ return {
308
+ "command_executed": f"Extracting interactions from {input_rds}",
309
+ "stdout": result.stdout,
310
+ "stderr": result.stderr,
311
+ "output_files": [str(output_path)]
312
+ }
313
+ except subprocess.CalledProcessError as e:
314
+ return {
315
+ "error": "R execution failed",
316
+ "stdout": e.stdout,
317
+ "stderr": e.stderr
318
+ }
319
+
320
+ if __name__ == "__main__":
321
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-statial/app/bioconductor-statial_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-statial/app/bioconductor-statial_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_statial'
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-statial/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-statial/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-statial:
5
+ build: .
6
+ image: mcp-bioconductor-statial:latest
7
+ container_name: mcp-bioconductor-statial
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-statial
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-statial/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-statial
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-statial/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-svp/app/bioconductor-svp_server.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Dict
5
+
6
+ # Note: The @mcp.tool decorator is expected to be provided by the MCP framework.
7
+ # This is a placeholder class to allow the code to be syntactically valid.
8
+ class mcp:
9
+ @staticmethod
10
+ def tool(func):
11
+ return func
12
+
13
+ @mcp.tool
14
+ def svp_train(
15
+ training_data: Path,
16
+ feature_data: Path,
17
+ output_model: Path,
18
+ method: str = "rf",
19
+ ) -> Dict:
20
+ """
21
+ Trains a Sequence-based Variant Prediction (SVP) model.
22
+
23
+ This tool wraps the `svp_train` function from the R Bioconductor package 'SVP'.
24
+ It takes training data and feature definitions to build a model, which is then
25
+ saved to a specified file. The R package 'SVP' and its dependencies must be
26
+ installed in the R environment.
27
+
28
+ Args:
29
+ training_data: Path to the training data file (tab-separated format).
30
+ This file should contain variant information and their known classifications.
31
+ feature_data: Path to the feature data file (tab-separated format).
32
+ This file defines the features to be used for model training.
33
+ output_model: Path where the trained model object (.rds file) will be saved.
34
+ method: The training method to be used by the underlying 'caret' package.
35
+ Defaults to 'rf' (Random Forest).
36
+
37
+ Returns:
38
+ A dictionary containing the command executed, stdout, stderr, and the path
39
+ to the output model file.
40
+ """
41
+ # 1. Input Validation
42
+ if not training_data.is_file():
43
+ raise FileNotFoundError(f"Training data file not found: {training_data}")
44
+ if not feature_data.is_file():
45
+ raise FileNotFoundError(f"Feature data file not found: {feature_data}")
46
+
47
+ output_model.parent.mkdir(parents=True, exist_ok=True)
48
+
49
+ # 2. R Script Generation
50
+ r_script_content = f"""
51
+ # Suppress startup messages for cleaner output
52
+ suppressPackageStartupMessages(library(SVP))
53
+
54
+ # Get arguments from command line
55
+ args <- commandArgs(trailingOnly=TRUE)
56
+ training_file <- args[1]
57
+ feature_file <- args[2]
58
+ output_model_file <- args[3]
59
+ train_method <- args[4]
60
+
61
+ # Read data - assuming tab-separated values with a header
62
+ tryCatch({{
63
+ training <- read.delim(training_file, header=TRUE, stringsAsFactors=FALSE)
64
+ feature <- read.delim(feature_file, header=TRUE, stringsAsFactors=FALSE)
65
+ }}, error = function(e) {{
66
+ stop(paste("Error reading input files:", e$message))
67
+ }})
68
+
69
+ # Train model
70
+ print("Starting model training...")
71
+ tryCatch({{
72
+ svp_model <- svp_train(training, feature, method=train_method)
73
+ }}, error = function(e) {{
74
+ stop(paste("Error during model training:", e$message))
75
+ }})
76
+
77
+ # Save model
78
+ tryCatch({{
79
+ saveRDS(svp_model, file=output_model_file)
80
+ }}, error = function(e) {{
81
+ stop(paste("Error saving the model file:", e$message))
82
+ }})
83
+
84
+ print(paste("Model training complete. Model saved to:", output_model_file))
85
+ """
86
+
87
+ # 3. Subprocess Execution
88
+ r_script_path = None
89
+ try:
90
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script:
91
+ tmp_r_script.write(r_script_content)
92
+ r_script_path = tmp_r_script.name
93
+
94
+ cmd = [
95
+ "Rscript",
96
+ r_script_path,
97
+ str(training_data.resolve()),
98
+ str(feature_data.resolve()),
99
+ str(output_model.resolve()),
100
+ method,
101
+ ]
102
+ command_executed = " ".join(cmd)
103
+
104
+ result = subprocess.run(
105
+ cmd,
106
+ check=True,
107
+ capture_output=True,
108
+ text=True,
109
+ )
110
+
111
+ # 4. Structured Result Return
112
+ return {
113
+ "command_executed": command_executed,
114
+ "stdout": result.stdout,
115
+ "stderr": result.stderr,
116
+ "output_files": {"model_file": str(output_model)},
117
+ }
118
+ except FileNotFoundError:
119
+ raise RuntimeError("Rscript command not found. Is R installed and in your system's PATH?")
120
+ except subprocess.CalledProcessError as e:
121
+ raise RuntimeError(
122
+ f"SVP training failed with exit code {e.returncode}.\n"
123
+ f"Command: {e.cmd}\n"
124
+ f"Stdout: {e.stdout}\n"
125
+ f"Stderr: {e.stderr}"
126
+ )
127
+ finally:
128
+ # Clean up the temporary R script
129
+ if r_script_path and Path(r_script_path).exists():
130
+ Path(r_script_path).unlink()
131
+
132
+
133
+ @mcp.tool
134
+ def svp_predict(
135
+ model_file: Path,
136
+ testing_data: Path,
137
+ feature_data: Path,
138
+ output_predictions: Path,
139
+ ) -> Dict:
140
+ """
141
+ Predicts variant effects using a pre-trained SVP model.
142
+
143
+ This tool wraps the `svp_predict` function from the R Bioconductor package 'SVP'.
144
+ It uses a trained model to predict the classification of new variants. The R package
145
+ 'SVP' and its dependencies must be installed in the R environment.
146
+
147
+ Args:
148
+ model_file: Path to the trained SVP model file (.rds format), generated by `svp_train`.
149
+ testing_data: Path to the data file containing new variants to be classified (tab-separated format).
150
+ feature_data: Path to the feature data file (tab-separated format). This must be the same
151
+ or have the same structure as the one used for training.
152
+ output_predictions: Path where the output predictions (tab-separated file) will be saved.
153
+
154
+ Returns:
155
+ A dictionary containing the command executed, stdout, stderr, and the path
156
+ to the output predictions file.
157
+ """
158
+ # 1. Input Validation
159
+ if not model_file.is_file():
160
+ raise FileNotFoundError(f"Model file not found: {model_file}")
161
+ if not testing_data.is_file():
162
+ raise FileNotFoundError(f"Testing data file not found: {testing_data}")
163
+ if not feature_data.is_file():
164
+ raise FileNotFoundError(f"Feature data file not found: {feature_data}")
165
+
166
+ output_predictions.parent.mkdir(parents=True, exist_ok=True)
167
+
168
+ # 2. R Script Generation
169
+ r_script_content = f"""
170
+ # Suppress startup messages for cleaner output
171
+ suppressPackageStartupMessages(library(SVP))
172
+
173
+ # Get arguments from command line
174
+ args <- commandArgs(trailingOnly=TRUE)
175
+ model_file <- args[1]
176
+ testing_file <- args[2]
177
+ feature_file <- args[3]
178
+ output_file <- args[4]
179
+
180
+ # Load model and data
181
+ tryCatch({{
182
+ svp_model <- readRDS(model_file)
183
+ testing <- read.delim(testing_file, header=TRUE, stringsAsFactors=FALSE)
184
+ feature <- read.delim(feature_file, header=TRUE, stringsAsFactors=FALSE)
185
+ }}, error = function(e) {{
186
+ stop(paste("Error reading input files:", e$message))
187
+ }})
188
+
189
+ # Make predictions
190
+ print("Starting prediction...")
191
+ tryCatch({{
192
+ svp_prediction <- svp_predict(svp_model, testing, feature)
193
+ }}, error = function(e) {{
194
+ stop(paste("Error during prediction:", e$message))
195
+ }})
196
+
197
+ # Write predictions to file
198
+ tryCatch({{
199
+ write.table(svp_prediction, file=output_file, sep="\\t", row.names=FALSE, quote=FALSE)
200
+ }}, error = function(e) {{
201
+ stop(paste("Error writing prediction output file:", e$message))
202
+ }})
203
+
204
+ print(paste("Prediction complete. Results saved to:", output_file))
205
+ """
206
+
207
+ # 3. Subprocess Execution
208
+ r_script_path = None
209
+ try:
210
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script:
211
+ tmp_r_script.write(r_script_content)
212
+ r_script_path = tmp_r_script.name
213
+
214
+ cmd = [
215
+ "Rscript",
216
+ r_script_path,
217
+ str(model_file.resolve()),
218
+ str(testing_data.resolve()),
219
+ str(feature_data.resolve()),
220
+ str(output_predictions.resolve()),
221
+ ]
222
+ command_executed = " ".join(cmd)
223
+
224
+ result = subprocess.run(
225
+ cmd,
226
+ check=True,
227
+ capture_output=True,
228
+ text=True,
229
+ )
230
+
231
+ # 4. Structured Result Return
232
+ return {
233
+ "command_executed": command_executed,
234
+ "stdout": result.stdout,
235
+ "stderr": result.stderr,
236
+ "output_files": {"predictions_file": str(output_predictions)},
237
+ }
238
+ except FileNotFoundError:
239
+ raise RuntimeError("Rscript command not found. Is R installed and in your system's PATH?")
240
+ except subprocess.CalledProcessError as e:
241
+ raise RuntimeError(
242
+ f"SVP prediction failed with exit code {e.returncode}.\n"
243
+ f"Command: {e.cmd}\n"
244
+ f"Stdout: {e.stdout}\n"
245
+ f"Stderr: {e.stderr}"
246
+ )
247
+ finally:
248
+ # Clean up the temporary R script
249
+ if r_script_path and Path(r_script_path).exists():
250
+ Path(r_script_path).unlink()