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

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_abundancebin/Dockerfile +40 -0
  2. Biomni/mcp_generated/mcp_abundancebin/app/abundancebin_server.py +136 -0
  3. Biomni/mcp_generated/mcp_abundancebin/app/abundancebin_shim_server.py +55 -0
  4. Biomni/mcp_generated/mcp_abundancebin/app/requirements.txt +1 -0
  5. Biomni/mcp_generated/mcp_abundancebin/docker-compose.yml +22 -0
  6. Biomni/mcp_generated/mcp_abundancebin/environment.yaml +10 -0
  7. Biomni/mcp_generated/mcp_abundancebin/requirements.txt +2 -0
  8. Biomni/mcp_generated/mcp_anarci/app/anarci_shim_server.py +55 -0
  9. Biomni/mcp_generated/mcp_anarci/app/requirements.txt +1 -0
  10. Biomni/mcp_generated/mcp_bcftools/app/__pycache__/bcftools_server.cpython-311.pyc +0 -0
  11. Biomni/mcp_generated/mcp_bioconductor-biostrings/Dockerfile +40 -0
  12. Biomni/mcp_generated/mcp_bioconductor-biostrings/app/bioconductor-biostrings_server.py +144 -0
  13. Biomni/mcp_generated/mcp_bioconductor-biostrings/app/bioconductor-biostrings_shim_server.py +55 -0
  14. Biomni/mcp_generated/mcp_bioconductor-biostrings/docker-compose.yml +22 -0
  15. Biomni/mcp_generated/mcp_bioconductor-biostrings/environment.yaml +10 -0
  16. Biomni/mcp_generated/mcp_bioconductor-biostrings/requirements.txt +2 -0
  17. Biomni/mcp_generated/mcp_bioconductor-despace/Dockerfile +40 -0
  18. Biomni/mcp_generated/mcp_bioconductor-despace/app/bioconductor-despace_server.py +303 -0
  19. Biomni/mcp_generated/mcp_bioconductor-despace/app/bioconductor-despace_shim_server.py +55 -0
  20. Biomni/mcp_generated/mcp_bioconductor-despace/app/requirements.txt +1 -0
  21. Biomni/mcp_generated/mcp_bioconductor-despace/docker-compose.yml +22 -0
  22. Biomni/mcp_generated/mcp_bioconductor-despace/environment.yaml +10 -0
  23. Biomni/mcp_generated/mcp_bioconductor-despace/requirements.txt +2 -0
  24. Biomni/mcp_generated/mcp_bioconductor-geomxtools/Dockerfile +40 -0
  25. Biomni/mcp_generated/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py +688 -0
  26. Biomni/mcp_generated/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_shim_server.py +55 -0
  27. Biomni/mcp_generated/mcp_bioconductor-geomxtools/docker-compose.yml +22 -0
  28. Biomni/mcp_generated/mcp_bioconductor-geomxtools/environment.yaml +10 -0
  29. Biomni/mcp_generated/mcp_bioconductor-geomxtools/requirements.txt +2 -0
  30. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/docker-compose.yml +22 -0
  31. Biomni/mcp_generated/mcp_bioconductor-glmgampoi/requirements.txt +2 -0
  32. Biomni/mcp_generated/mcp_bioconductor-infercnv/Dockerfile +40 -0
  33. Biomni/mcp_generated/mcp_bioconductor-infercnv/app/bioconductor-infercnv_server.py +298 -0
  34. Biomni/mcp_generated/mcp_bioconductor-infercnv/docker-compose.yml +22 -0
  35. Biomni/mcp_generated/mcp_bioconductor-infercnv/environment.yaml +10 -0
  36. Biomni/mcp_generated/mcp_bioconductor-infercnv/requirements.txt +2 -0
  37. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/Dockerfile +40 -0
  38. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py +195 -0
  39. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_shim_server.py +55 -0
  40. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/requirements.txt +1 -0
  41. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/docker-compose.yml +22 -0
  42. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/environment.yaml +10 -0
  43. Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/requirements.txt +2 -0
  44. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/Dockerfile +40 -0
  45. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/app/bioconductor-preprocesscore_server.py +340 -0
  46. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/app/bioconductor-preprocesscore_shim_server.py +55 -0
  47. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/app/requirements.txt +1 -0
  48. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/docker-compose.yml +22 -0
  49. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/environment.yaml +10 -0
  50. Biomni/mcp_generated/mcp_bioconductor-preprocesscore/requirements.txt +2 -0
Biomni/mcp_generated/mcp_abundancebin/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 abundancebin via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda abundancebin -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/abundancebin_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/abundancebin_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/abundancebin_server.py"]
40
+
Biomni/mcp_generated/mcp_abundancebin/app/abundancebin_server.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # Assume @mcp.tool() is defined in the execution environment.
7
+ # No import is needed for the final code.
8
+
9
+ # Set up logging
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
+ logger = logging.getLogger(__name__)
12
+
13
+ from mcp.server.fastmcp import FastMCP
14
+
15
+ SERVER_NAME = 'local_abundancebin'
16
+ mcp = FastMCP(SERVER_NAME)
17
+
18
+ @mcp.tool()
19
+ def abundancebin(
20
+ input_file: Path,
21
+ kmer_len: int = 20,
22
+ output: Optional[Path] = None,
23
+ exclude: Optional[int] = None,
24
+ exclude_max: Optional[int] = None,
25
+ output_fasta: bool = False,
26
+ bin_num: Optional[int] = None,
27
+ recursive_classification: bool = False,
28
+ ):
29
+ """
30
+ Performs abundance-based binning on a given input FASTA/FASTQ file.
31
+
32
+ This tool uses k-mer frequency to classify sequences into bins. It can either
33
+ classify into a specified number of bins or use a recursive classification approach.
34
+
35
+ Args:
36
+ input_file: Path to the input FASTA/FASTQ file.
37
+ kmer_len: The length of the k-mer to use for composition analysis (default: 20).
38
+ output: Path to the output log file. If not provided, defaults to '<input_file>.log'.
39
+ exclude: Exclude contigs with coverage lower than this count.
40
+ exclude_max: Exclude contigs with coverage higher than this count.
41
+ output_fasta: If True, output binned sequences into separate FASTA files.
42
+ bin_num: The specific number of bins to classify sequences into.
43
+ recursive_classification: If True, undergo recursive classification instead of specifying a bin number.
44
+ This is mutually exclusive with 'bin_num'.
45
+
46
+ Returns:
47
+ A dictionary containing the execution details and paths to output files.
48
+ """
49
+ # 1. Input Validation
50
+ if not input_file.is_file():
51
+ raise FileNotFoundError(f"Input file not found: {input_file}")
52
+
53
+ if bin_num is not None and recursive_classification:
54
+ raise ValueError("Parameters 'bin_num' and 'recursive_classification' are mutually exclusive. Please provide only one.")
55
+
56
+ if kmer_len <= 0:
57
+ raise ValueError("kmer_len must be a positive integer.")
58
+
59
+ # 2. Command Construction
60
+ # Determine the output file path based on the tool's default behavior
61
+ if output:
62
+ output_path = output
63
+ else:
64
+ output_path = input_file.with_suffix(".log")
65
+
66
+ # Ensure the output directory exists
67
+ output_path.parent.mkdir(parents=True, exist_ok=True)
68
+
69
+ cmd = [
70
+ "abundancebin",
71
+ "-input", str(input_file),
72
+ "-kmer_len", str(kmer_len),
73
+ "-output", str(output_path)
74
+ ]
75
+
76
+ if exclude is not None:
77
+ cmd.extend(["-exclude", str(exclude)])
78
+
79
+ if exclude_max is not None:
80
+ cmd.extend(["-exclude_max", str(exclude_max)])
81
+
82
+ if output_fasta:
83
+ cmd.append("-OUTPUT_FASTA")
84
+
85
+ if bin_num is not None:
86
+ cmd.extend(["-bin_num", str(bin_num)])
87
+
88
+ if recursive_classification:
89
+ cmd.append("-RECURSIVE_CLASSIFICATION")
90
+
91
+ command_executed = " ".join(cmd)
92
+ logger.info(f"Executing command: {command_executed}")
93
+
94
+ # 3. Subprocess Execution and Error Handling
95
+ try:
96
+ result = subprocess.run(
97
+ cmd,
98
+ capture_output=True,
99
+ text=True,
100
+ check=True
101
+ )
102
+
103
+ # The tool might generate more files if -OUTPUT_FASTA is used.
104
+ # For simplicity, we return the main log file. The user can infer others.
105
+ output_files = [str(output_path)] if output_path.exists() else []
106
+
107
+ # 4. Structured Result Return (Success)
108
+ return {
109
+ "command_executed": command_executed,
110
+ "stdout": result.stdout,
111
+ "stderr": result.stderr,
112
+ "output_files": output_files
113
+ }
114
+
115
+ except FileNotFoundError:
116
+ error_message = "abundancebin command not found. Please ensure the tool is installed and in your system's PATH."
117
+ logger.error(error_message)
118
+ # Re-raising is often better to signal a fatal environment error.
119
+ raise RuntimeError(error_message) from None
120
+
121
+ except subprocess.CalledProcessError as e:
122
+ logger.error(f"abundancebin failed with exit code {e.returncode}")
123
+ logger.error(f"Stderr: {e.stderr}")
124
+ logger.error(f"Stdout: {e.stdout}")
125
+
126
+ # 4. Structured Result Return (Failure)
127
+ return {
128
+ "command_executed": command_executed,
129
+ "stdout": e.stdout,
130
+ "stderr": e.stderr,
131
+ "return_code": e.returncode,
132
+ "output_files": []
133
+ }
134
+
135
+ if __name__ == "__main__":
136
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_abundancebin/app/abundancebin_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_abundancebin/app/abundancebin_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_abundancebin'
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_abundancebin/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_abundancebin/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-abundancebin:
5
+ build: .
6
+ image: mcp-abundancebin:latest
7
+ container_name: mcp-abundancebin
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=abundancebin
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_abundancebin/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - abundancebin
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_abundancebin/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_anarci/app/anarci_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_anarci/app/anarci_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_anarci'
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_anarci/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bcftools/app/__pycache__/bcftools_server.cpython-311.pyc ADDED
Binary file (39 kB). View file
 
Biomni/mcp_generated/mcp_bioconductor-biostrings/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-biostrings via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-biostrings -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-biostrings_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-biostrings_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-biostrings_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-biostrings/app/bioconductor-biostrings_server.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Optional, List, Dict, Any
5
+
6
+ # Mock the decorator for standalone execution
7
+ class mcp:
8
+ @staticmethod
9
+ def tool():
10
+ def decorator(func):
11
+ return func
12
+ return decorator
13
+
14
+ from mcp.server.fastmcp import FastMCP
15
+
16
+ SERVER_NAME = 'local_bioconductor_biostrings'
17
+ mcp = FastMCP(SERVER_NAME)
18
+
19
+ @mcp.tool()
20
+ def rscript(
21
+ script_file: Optional[Path] = None,
22
+ expressions: Optional[List[str]] = None,
23
+ script_args: Optional[List[str]] = None,
24
+ verbose: bool = False,
25
+ default_packages: Optional[str] = None,
26
+ vanilla: bool = False,
27
+ save: bool = False,
28
+ no_environ: bool = False,
29
+ no_site_file: bool = False,
30
+ no_init_file: bool = False,
31
+ restore: bool = False,
32
+ ) -> Dict[str, Any]:
33
+ """
34
+ Executes an R script or R expressions using the Rscript command-line tool.
35
+
36
+ This tool serves as a wrapper for Rscript, allowing for the execution of R code
37
+ from a file or directly from string expressions. It mirrors the functionality
38
+ provided by `Rscript --help`.
39
+
40
+ Args:
41
+ script_file: Path to the R script file to be executed. Mutually exclusive with 'expressions'.
42
+ expressions: A list of R expressions to be executed. Mutually exclusive with 'script_file'.
43
+ script_args: A list of arguments to be passed to the R script itself.
44
+ verbose: If True, enables verbose output, printing information on progress.
45
+ default_packages: A comma-separated string of package names to be loaded by default.
46
+ vanilla: If True, combines --no-save, --no-restore, --no-site-file, --no-init-file, and --no-environ.
47
+ If set, it overrides the individual flags (save, restore, etc.).
48
+ save: If True, the workspace is saved at the end of the session. Ignored if 'vanilla' is True.
49
+ no_environ: If True, site and user environment files are not read. Ignored if 'vanilla' is True.
50
+ no_site_file: If True, the site-wide Rprofile is not read. Ignored if 'vanilla' is True.
51
+ no_init_file: If True, the user's R profile is not read. Ignored if 'vanilla' is True.
52
+ restore: If True, previously saved objects are restored at startup. Ignored if 'vanilla' is True.
53
+
54
+ Returns:
55
+ A dictionary containing the executed command, stdout, stderr, and a list of output files (always empty).
56
+ """
57
+ # 1. Input Validation
58
+ if not script_file and not expressions:
59
+ raise ValueError("Either 'script_file' or 'expressions' must be provided.")
60
+ if script_file and expressions:
61
+ raise ValueError("'script_file' and 'expressions' are mutually exclusive and cannot be used together.")
62
+ if script_file and not script_file.is_file():
63
+ raise FileNotFoundError(f"The specified script file does not exist: {script_file}")
64
+ if expressions and not expressions:
65
+ raise ValueError("'expressions' list cannot be empty if provided.")
66
+
67
+ # 2. Command Construction
68
+ cmd = ["Rscript"]
69
+
70
+ if verbose:
71
+ cmd.append("--verbose")
72
+
73
+ if default_packages:
74
+ cmd.extend(["--default-packages", default_packages])
75
+
76
+ if vanilla:
77
+ cmd.append("--vanilla")
78
+ else:
79
+ # These options are combined and handled by the --vanilla flag in Rscript
80
+ if save:
81
+ cmd.append("--save")
82
+ if no_environ:
83
+ cmd.append("--no-environ")
84
+ if no_site_file:
85
+ cmd.append("--no-site-file")
86
+ if no_init_file:
87
+ cmd.append("--no-init-file")
88
+ if restore:
89
+ cmd.append("--restore")
90
+
91
+ # Add expressions or script file to the command
92
+ if expressions:
93
+ for expr in expressions:
94
+ cmd.extend(["-e", expr])
95
+ elif script_file:
96
+ cmd.append(str(script_file))
97
+
98
+ # Add arguments for the R script itself
99
+ if script_args:
100
+ cmd.extend(script_args)
101
+
102
+ command_str = " ".join(cmd)
103
+ logging.info(f"Executing command: {command_str}")
104
+
105
+ # 3. Subprocess Execution and Error Handling
106
+ try:
107
+ result = subprocess.run(
108
+ cmd,
109
+ capture_output=True,
110
+ text=True,
111
+ check=True,
112
+ )
113
+ stdout = result.stdout
114
+ stderr = result.stderr
115
+ except FileNotFoundError:
116
+ # This error occurs if 'Rscript' is not in the system's PATH
117
+ return {
118
+ "command_executed": command_str,
119
+ "stdout": "",
120
+ "stderr": "Error: 'Rscript' command not found. Please ensure R is installed and accessible in your system's PATH.",
121
+ "output_files": []
122
+ }
123
+ except subprocess.CalledProcessError as e:
124
+ # This error occurs if the R script itself fails (non-zero exit code)
125
+ logging.error(f"Rscript execution failed with return code {e.returncode}")
126
+ return {
127
+ "command_executed": command_str,
128
+ "stdout": e.stdout,
129
+ "stderr": e.stderr,
130
+ "output_files": []
131
+ }
132
+
133
+ # 4. Structured Result Return
134
+ # Rscript itself does not have a dedicated output file parameter. Any files
135
+ # created are determined by the R code within the script.
136
+ return {
137
+ "command_executed": command_str,
138
+ "stdout": stdout,
139
+ "stderr": stderr,
140
+ "output_files": []
141
+ }
142
+
143
+ if __name__ == "__main__":
144
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-biostrings/app/bioconductor-biostrings_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-biostrings/app/bioconductor-biostrings_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_biostrings'
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-biostrings/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-biostrings:
5
+ build: .
6
+ image: mcp-bioconductor-biostrings:latest
7
+ container_name: mcp-bioconductor-biostrings
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-biostrings
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-biostrings/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-biostrings
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-biostrings/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-despace/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-despace via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-despace -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-despace_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-despace_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-despace_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-despace/app/bioconductor-despace_server.py ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # MCP is a placeholder for the Model Context Protocol library.
7
+ # In a real environment, this would be: from mcp import tool
8
+ class mcp:
9
+ @staticmethod
10
+ def tool():
11
+ def decorator(func):
12
+ return func
13
+ return decorator
14
+
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
+
17
+ from mcp.server.fastmcp import FastMCP
18
+
19
+ SERVER_NAME = 'local_bioconductor_despace'
20
+ mcp = FastMCP(SERVER_NAME)
21
+
22
+ @mcp.tool()
23
+ def cluster_scrnaseq(
24
+ sc_object_path: Path,
25
+ output_rds_path: Path,
26
+ s_topics: int = 10,
27
+ n_top_genes: int = 2000,
28
+ ) -> dict:
29
+ """
30
+ Performs clustering on single-cell RNA-seq data as a preprocessing step for DeSpace.
31
+
32
+ This tool wraps the `cluster_scRNAseq` function from the DeSpace R package. It takes a
33
+ Seurat object, identifies highly variable genes, performs dimensionality reduction,
34
+ and identifies clusters based on topic modeling.
35
+
36
+ Args:
37
+ sc_object_path: Path to the input single-cell Seurat object (.rds file).
38
+ output_rds_path: Path to save the clustered single-cell Seurat object (.rds file).
39
+ s_topics: The number of topics (S) for topic modeling, representing putative cell types.
40
+ n_top_genes: Number of highly variable genes to use for clustering.
41
+
42
+ Returns:
43
+ A dictionary containing the executed command, stdout, stderr, and the path to the output file.
44
+ """
45
+ # --- Input Validation ---
46
+ if not sc_object_path.is_file():
47
+ raise FileNotFoundError(f"Input single-cell object not found: {sc_object_path}")
48
+ if not output_rds_path.parent.exists():
49
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
50
+ logging.info(f"Created output directory: {output_rds_path.parent}")
51
+
52
+ if s_topics <= 0:
53
+ raise ValueError("s_topics must be a positive integer.")
54
+ if n_top_genes <= 0:
55
+ raise ValueError("n_top_genes must be a positive integer.")
56
+
57
+ # --- Command Construction ---
58
+ # This assumes a wrapper R script 'cluster_scrnaseq.R' is in the system's PATH.
59
+ cmd = [
60
+ "Rscript", "cluster_scrnaseq.R",
61
+ "--sc_object_path", str(sc_object_path),
62
+ "--output_rds_path", str(output_rds_path),
63
+ "--s_topics", str(s_topics),
64
+ "--n_top_genes", str(n_top_genes),
65
+ ]
66
+ command_executed = " ".join(cmd)
67
+ logging.info(f"Executing command: {command_executed}")
68
+
69
+ # --- Subprocess Execution ---
70
+ try:
71
+ process = subprocess.run(
72
+ cmd,
73
+ check=True,
74
+ capture_output=True,
75
+ text=True,
76
+ )
77
+ stdout = process.stdout
78
+ stderr = process.stderr
79
+ logging.info("Single-cell clustering completed successfully.")
80
+ except FileNotFoundError:
81
+ err_msg = "Error: 'Rscript' command not found. Please ensure R and the required wrapper scripts are in the system's PATH."
82
+ logging.error(err_msg)
83
+ return {"command_executed": command_executed, "stdout": "", "stderr": err_msg, "output_files": []}
84
+ except subprocess.CalledProcessError as e:
85
+ logging.error(f"Single-cell clustering failed with exit code {e.returncode}.")
86
+ return {"command_executed": command_executed, "stdout": e.stdout, "stderr": e.stderr, "output_files": []}
87
+
88
+ # --- Structured Result Return ---
89
+ return {
90
+ "command_executed": command_executed,
91
+ "stdout": stdout,
92
+ "stderr": stderr,
93
+ "output_files": [str(output_rds_path)]
94
+ }
95
+
96
+ @mcp.tool()
97
+ def cluster_sp_data(
98
+ sp_object_path: Path,
99
+ output_rds_path: Path,
100
+ resolution: float = 0.8,
101
+ ) -> dict:
102
+ """
103
+ Performs clustering on spatial transcriptomics data as a preprocessing step for DeSpace.
104
+
105
+ This tool wraps the `cluster_sp_data` function from the DeSpace R package. It takes a
106
+ spatial Seurat object, normalizes the data, finds variable features, and performs
107
+ graph-based clustering.
108
+
109
+ Args:
110
+ sp_object_path: Path to the input spatial Seurat object (.rds file).
111
+ output_rds_path: Path to save the clustered spatial Seurat object (.rds file).
112
+ resolution: Clustering resolution for the Louvain algorithm.
113
+
114
+ Returns:
115
+ A dictionary containing the executed command, stdout, stderr, and the path to the output file.
116
+ """
117
+ # --- Input Validation ---
118
+ if not sp_object_path.is_file():
119
+ raise FileNotFoundError(f"Input spatial object not found: {sp_object_path}")
120
+ if not output_rds_path.parent.exists():
121
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
122
+ logging.info(f"Created output directory: {output_rds_path.parent}")
123
+
124
+ if resolution <= 0.0:
125
+ raise ValueError("resolution must be a positive float.")
126
+
127
+ # --- Command Construction ---
128
+ # This assumes a wrapper R script 'cluster_sp_data.R' is in the system's PATH.
129
+ cmd = [
130
+ "Rscript", "cluster_sp_data.R",
131
+ "--sp_object_path", str(sp_object_path),
132
+ "--output_rds_path", str(output_rds_path),
133
+ "--resolution", str(resolution),
134
+ ]
135
+ command_executed = " ".join(cmd)
136
+ logging.info(f"Executing command: {command_executed}")
137
+
138
+ # --- Subprocess Execution ---
139
+ try:
140
+ process = subprocess.run(
141
+ cmd,
142
+ check=True,
143
+ capture_output=True,
144
+ text=True,
145
+ )
146
+ stdout = process.stdout
147
+ stderr = process.stderr
148
+ logging.info("Spatial data clustering completed successfully.")
149
+ except FileNotFoundError:
150
+ err_msg = "Error: 'Rscript' command not found. Please ensure R and the required wrapper scripts are in the system's PATH."
151
+ logging.error(err_msg)
152
+ return {"command_executed": command_executed, "stdout": "", "stderr": err_msg, "output_files": []}
153
+ except subprocess.CalledProcessError as e:
154
+ logging.error(f"Spatial data clustering failed with exit code {e.returncode}.")
155
+ return {"command_executed": command_executed, "stdout": e.stdout, "stderr": e.stderr, "output_files": []}
156
+
157
+ # --- Structured Result Return ---
158
+ return {
159
+ "command_executed": command_executed,
160
+ "stdout": stdout,
161
+ "stderr": stderr,
162
+ "output_files": [str(output_rds_path)]
163
+ }
164
+
165
+ @mcp.tool()
166
+ def run_despace(
167
+ sc_object_path: Path,
168
+ sp_object_path: Path,
169
+ output_rds_path: Path,
170
+ sc_assay: str = "RNA",
171
+ sc_slot: str = "counts",
172
+ sp_assay: str = "Spatial",
173
+ sp_slot: str = "counts",
174
+ num_markers_sc: int = 10,
175
+ num_markers_sp: int = 10,
176
+ s_topics: int = 10,
177
+ n_top_genes: int = 2000,
178
+ resolution: float = 0.8,
179
+ sample_id: Optional[str] = None,
180
+ num_threads: int = 1,
181
+ save_model: bool = False,
182
+ model_path: Optional[Path] = None,
183
+ ) -> dict:
184
+ """
185
+ Runs the main DeSpace algorithm to integrate single-cell and spatial transcriptomics data.
186
+
187
+ This tool wraps the `DeSpace` R function. It can perform clustering internally if not
188
+ already present in the input objects, or it can use pre-computed clusters. The main
189
+ output is a Seurat object with cell-type deconvolution results.
190
+
191
+ Args:
192
+ sc_object_path: Path to the single-cell Seurat object (.rds file).
193
+ sp_object_path: Path to the spatial transcriptomics Seurat object (.rds file).
194
+ output_rds_path: Path to save the resulting Seurat object with DeSpace results.
195
+ sc_assay: Assay to use from the single-cell Seurat object.
196
+ sc_slot: Slot to use from the single-cell assay (e.g., 'counts', 'data').
197
+ sp_assay: Assay to use from the spatial Seurat object.
198
+ sp_slot: Slot to use from the spatial assay (e.g., 'counts', 'data').
199
+ num_markers_sc: Number of markers to use for each single-cell cluster.
200
+ num_markers_sp: Number of markers to use for each spatial cluster.
201
+ s_topics: The number of topics (S) for topic modeling, used if sc-data is not pre-clustered.
202
+ n_top_genes: Number of highly variable genes, used if sc-data is not pre-clustered.
203
+ resolution: Clustering resolution, used if sp-data is not pre-clustered.
204
+ sample_id: Optional identifier for the sample, used for saving the model.
205
+ num_threads: Number of parallel threads to use.
206
+ save_model: If True, save the trained DeSpace model.
207
+ model_path: Path to save the DeSpace model file. Required if save_model is True.
208
+
209
+ Returns:
210
+ A dictionary containing the executed command, stdout, stderr, and a list of output files.
211
+ """
212
+ # --- Input Validation ---
213
+ if not sc_object_path.is_file():
214
+ raise FileNotFoundError(f"Single-cell input file not found: {sc_object_path}")
215
+ if not sp_object_path.is_file():
216
+ raise FileNotFoundError(f"Spatial input file not found: {sp_object_path}")
217
+
218
+ if not output_rds_path.parent.exists():
219
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
220
+ logging.info(f"Created output directory: {output_rds_path.parent}")
221
+
222
+ if num_markers_sc <= 0:
223
+ raise ValueError("num_markers_sc must be a positive integer.")
224
+ if num_markers_sp <= 0:
225
+ raise ValueError("num_markers_sp must be a positive integer.")
226
+ if s_topics <= 0:
227
+ raise ValueError("s_topics must be a positive integer.")
228
+ if n_top_genes <= 0:
229
+ raise ValueError("n_top_genes must be a positive integer.")
230
+ if resolution <= 0.0:
231
+ raise ValueError("resolution must be a positive float.")
232
+ if num_threads <= 0:
233
+ raise ValueError("num_threads must be a positive integer.")
234
+
235
+ if save_model:
236
+ if model_path is None:
237
+ raise ValueError("model_path must be provided when save_model is True.")
238
+ if not model_path.parent.exists():
239
+ model_path.parent.mkdir(parents=True, exist_ok=True)
240
+ logging.info(f"Created model output directory: {model_path.parent}")
241
+
242
+ # --- Command Construction ---
243
+ # This assumes a wrapper R script 'run_despace.R' is in the system's PATH.
244
+ cmd = [
245
+ "Rscript", "run_despace.R",
246
+ "--sc_object_path", str(sc_object_path),
247
+ "--sp_object_path", str(sp_object_path),
248
+ "--output_rds_path", str(output_rds_path),
249
+ "--sc_assay", sc_assay,
250
+ "--sc_slot", sc_slot,
251
+ "--sp_assay", sp_assay,
252
+ "--sp_slot", sp_slot,
253
+ "--num_markers_sc", str(num_markers_sc),
254
+ "--num_markers_sp", str(num_markers_sp),
255
+ "--s_topics", str(s_topics),
256
+ "--n_top_genes", str(n_top_genes),
257
+ "--resolution", str(resolution),
258
+ "--num_threads", str(num_threads),
259
+ ]
260
+
261
+ if sample_id:
262
+ cmd.extend(["--sample_id", sample_id])
263
+
264
+ if save_model and model_path:
265
+ cmd.append("--save_model")
266
+ cmd.extend(["--model_path", str(model_path)])
267
+
268
+ command_executed = " ".join(cmd)
269
+ logging.info(f"Executing command: {command_executed}")
270
+
271
+ # --- Subprocess Execution ---
272
+ try:
273
+ process = subprocess.run(
274
+ cmd,
275
+ check=True,
276
+ capture_output=True,
277
+ text=True,
278
+ )
279
+ stdout = process.stdout
280
+ stderr = process.stderr
281
+ logging.info("DeSpace execution completed successfully.")
282
+ except FileNotFoundError:
283
+ err_msg = "Error: 'Rscript' command not found. Please ensure R and the required wrapper scripts are in the system's PATH."
284
+ logging.error(err_msg)
285
+ return {"command_executed": command_executed, "stdout": "", "stderr": err_msg, "output_files": []}
286
+ except subprocess.CalledProcessError as e:
287
+ logging.error(f"DeSpace execution failed with exit code {e.returncode}.")
288
+ return {"command_executed": command_executed, "stdout": e.stdout, "stderr": e.stderr, "output_files": []}
289
+
290
+ # --- Structured Result Return ---
291
+ output_files: List[str] = [str(output_rds_path)]
292
+ if save_model and model_path:
293
+ output_files.append(str(model_path))
294
+
295
+ return {
296
+ "command_executed": command_executed,
297
+ "stdout": stdout,
298
+ "stderr": stderr,
299
+ "output_files": output_files
300
+ }
301
+
302
+ if __name__ == "__main__":
303
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-despace/app/bioconductor-despace_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-despace/app/bioconductor-despace_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_despace'
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-despace/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-despace/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-despace:
5
+ build: .
6
+ image: mcp-bioconductor-despace:latest
7
+ container_name: mcp-bioconductor-despace
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-despace
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-despace/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-despace
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-despace/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-geomxtools/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-geomxtools via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-geomxtools -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-geomxtools_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-geomxtools_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-geomxtools_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py ADDED
@@ -0,0 +1,688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # In a real MCP environment, this would be imported.
7
+ # from mcp import tool as mcp_tool
8
+ # For this exercise, we assume the decorator @mcp.tool() is available.
9
+ class mcp:
10
+ @staticmethod
11
+ def tool():
12
+ def decorator(func):
13
+ return func
14
+ return decorator
15
+
16
+ from mcp.server.fastmcp import FastMCP
17
+
18
+ SERVER_NAME = 'local_bioconductor_geomxtools'
19
+ mcp = FastMCP(SERVER_NAME)
20
+
21
+ @mcp.tool()
22
+ def read_nanostring_geomx_set(
23
+ dcc_files_dir: Path,
24
+ pkc_files: List[Path],
25
+ pheno_data_file: Path,
26
+ output_rds_path: Path,
27
+ pheno_data_sheet: Optional[str] = None,
28
+ pheno_data_dcc_col_name: str = "Sample_ID",
29
+ protocol_data_col_names: Optional[List[str]] = None,
30
+ experiment_data_col_names: Optional[List[str]] = None,
31
+ ) -> dict:
32
+ """
33
+ Reads NanoString GeoMx files (DCC, PKC, annotation) and creates a NanoStringGeomxSet object.
34
+
35
+ This tool is a wrapper around the `readNanoStringGeoMxSet` function from the
36
+ R/Bioconductor package `GeomxTools`. It processes raw data into a structured
37
+ R object for downstream analysis.
38
+
39
+ Args:
40
+ dcc_files_dir: Path to the directory containing DCC files.
41
+ pkc_files: A list of paths to PKC files.
42
+ pheno_data_file: Path to the sample annotation file (e.g., an Excel file).
43
+ output_rds_path: Path for the output RDS file which will contain the NanoStringGeomxSet object.
44
+ pheno_data_sheet: Optional name of the worksheet in the Excel annotation file.
45
+ pheno_data_dcc_col_name: Column name in the annotation file that matches DCC file names.
46
+ protocol_data_col_names: Optional list of column names in annotation to be added to protocolData.
47
+ experiment_data_col_names: Optional list of column names in annotation to be added to experimentData.
48
+
49
+ Returns:
50
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
51
+ """
52
+ # --- Input Validation ---
53
+ if not dcc_files_dir.is_dir():
54
+ raise ValueError(f"DCC files directory not found: {dcc_files_dir}")
55
+ if not pkc_files:
56
+ raise ValueError("At least one PKC file must be provided.")
57
+ for pkc_file in pkc_files:
58
+ if not pkc_file.is_file():
59
+ raise ValueError(f"PKC file not found: {pkc_file}")
60
+ if not pheno_data_file.is_file():
61
+ raise ValueError(f"Phenotype data file not found: {pheno_data_file}")
62
+
63
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
64
+
65
+ # --- R Script Generation ---
66
+ # Safely create R vectors from Python lists
67
+ r_protocol_cols = f'c({", ".join(f"{col}" for col in protocol_data_col_names)})' if protocol_data_col_names else "NULL"
68
+ r_experiment_cols = f'c({", ".join(f"{col}" for col in experiment_data_col_names)})' if experiment_data_col_names else "NULL"
69
+ r_pheno_sheet = f'"{pheno_data_sheet}"' if pheno_data_sheet else "NULL"
70
+ pkc_files_r_vector = f'c({", ".join(f"{str(p)}" for p in pkc_files)})'
71
+
72
+ r_script_content = f"""
73
+ library(GeomxTools)
74
+
75
+ tryCatch({{
76
+ dcc_dir <- "{dcc_files_dir}"
77
+ pkc_files_vec <- {pkc_files_r_vector}
78
+ pheno_file <- "{pheno_data_file}"
79
+ output_path <- "{output_rds_path}"
80
+ pheno_dcc_col <- "{pheno_data_dcc_col_name}"
81
+
82
+ dcc_files <- list.files(dcc_dir, pattern = "\\\\.dcc$", full.names = TRUE, recursive = TRUE)
83
+ if (length(dcc_files) == 0) {{
84
+ stop("No .dcc files found in the specified directory.")
85
+ }}
86
+
87
+ geomx_data <- readNanoStringGeoMxSet(
88
+ dccFiles = dcc_files,
89
+ pkcFiles = pkc_files_vec,
90
+ phenoDataFile = pheno_file,
91
+ phenoDataSheet = {r_pheno_sheet},
92
+ phenoDataDccColName = pheno_dcc_col,
93
+ protocolDataColNames = {r_protocol_cols},
94
+ experimentDataColNames = {r_experiment_cols}
95
+ )
96
+
97
+ saveRDS(geomx_data, file = output_path)
98
+ cat("Successfully created NanoStringGeomxSet object and saved to", output_path, "\\n")
99
+ }}, error = function(e) {{
100
+ message("R script failed with error: ", e$message)
101
+ quit(status = 1)
102
+ }})
103
+ """
104
+
105
+ # --- Subprocess Execution ---
106
+ command_executed = ""
107
+ try:
108
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
109
+ r_script_file.write(r_script_content)
110
+ r_script_path = r_script_file.name
111
+
112
+ cmd = ["Rscript", r_script_path]
113
+ command_executed = " ".join(cmd)
114
+
115
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
116
+
117
+ return {
118
+ "command_executed": command_executed,
119
+ "stdout": process.stdout,
120
+ "stderr": process.stderr,
121
+ "output_files": [str(output_rds_path)]
122
+ }
123
+ except FileNotFoundError:
124
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
125
+ except subprocess.CalledProcessError as e:
126
+ return {
127
+ "command_executed": command_executed,
128
+ "stdout": e.stdout,
129
+ "stderr": e.stderr,
130
+ "error": "R script execution failed.",
131
+ "return_code": e.returncode,
132
+ "output_files": []
133
+ }
134
+ finally:
135
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
136
+ Path(r_script_path).unlink()
137
+
138
+ @mcp.tool()
139
+ def set_segment_qc_flags(
140
+ input_rds_path: Path,
141
+ output_rds_path: Path,
142
+ min_segment_reads: Optional[int] = 1000,
143
+ percent_aligned: Optional[float] = 80,
144
+ percent_saturation: Optional[float] = 50,
145
+ min_negative_count: Optional[int] = 10,
146
+ max_ntc_count: Optional[int] = 1000,
147
+ min_nuclei: Optional[int] = 200,
148
+ min_area: Optional[int] = 16000,
149
+ ) -> dict:
150
+ """
151
+ Sets segment QC flags in a NanoStringGeomxSet object based on specified cutoffs.
152
+
153
+ Args:
154
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
155
+ output_rds_path: Path for the output RDS file with QC flags applied.
156
+ min_segment_reads: Minimum number of reads in a segment.
157
+ percent_aligned: Minimum percentage of reads aligned.
158
+ percent_saturation: Minimum percentage of reads saturated.
159
+ min_negative_count: Minimum negative probe counts.
160
+ max_ntc_count: Maximum counts observed in NTC wells.
161
+ min_nuclei: Minimum number of nuclei in a segment.
162
+ min_area: Minimum area of a segment.
163
+
164
+ Returns:
165
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
166
+ """
167
+ # --- Input Validation ---
168
+ if not input_rds_path.is_file():
169
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
170
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
171
+
172
+ # --- R Script Generation ---
173
+ r_script_content = f"""
174
+ library(GeomxTools)
175
+
176
+ tryCatch({{
177
+ input_rds <- "{input_rds_path}"
178
+ output_rds <- "{output_rds_path}"
179
+
180
+ geomx_data <- readRDS(input_rds)
181
+
182
+ qc_cutoffs <- list()
183
+ if (!is.null({min_segment_reads or 'NULL'})) {{ qc_cutoffs$minSegmentReads <- {min_segment_reads} }}
184
+ if (!is.null({percent_aligned or 'NULL'})) {{ qc_cutoffs$percentAligned <- {percent_aligned} }}
185
+ if (!is.null({percent_saturation or 'NULL'})) {{ qc_cutoffs$percentSaturation <- {percent_saturation} }}
186
+ if (!is.null({min_negative_count or 'NULL'})) {{ qc_cutoffs$minNegativeCount <- {min_negative_count} }}
187
+ if (!is.null({max_ntc_count or 'NULL'})) {{ qc_cutoffs$maxNTCCount <- {max_ntc_count} }}
188
+ if (!is.null({min_nuclei or 'NULL'})) {{ qc_cutoffs$minNuclei <- {min_nuclei} }}
189
+ if (!is.null({min_area or 'NULL'})) {{ qc_cutoffs$minArea <- {min_area} }}
190
+
191
+ geomx_data_qc <- setSegmentQCFlags(geomx_data, qcCutoffs = qc_cutoffs)
192
+
193
+ saveRDS(geomx_data_qc, file = output_rds)
194
+ cat("Successfully applied segment QC flags and saved to", output_rds, "\\n")
195
+ }}, error = function(e) {{
196
+ message("R script failed with error: ", e$message)
197
+ quit(status = 1)
198
+ }})
199
+ """
200
+
201
+ # --- Subprocess Execution ---
202
+ command_executed = ""
203
+ try:
204
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
205
+ r_script_file.write(r_script_content)
206
+ r_script_path = r_script_file.name
207
+
208
+ cmd = ["Rscript", r_script_path]
209
+ command_executed = " ".join(cmd)
210
+
211
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
212
+
213
+ return {
214
+ "command_executed": command_executed,
215
+ "stdout": process.stdout,
216
+ "stderr": process.stderr,
217
+ "output_files": [str(output_rds_path)]
218
+ }
219
+ except FileNotFoundError:
220
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
221
+ except subprocess.CalledProcessError as e:
222
+ return {
223
+ "command_executed": command_executed,
224
+ "stdout": e.stdout,
225
+ "stderr": e.stderr,
226
+ "error": "R script execution failed.",
227
+ "return_code": e.returncode,
228
+ "output_files": []
229
+ }
230
+ finally:
231
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
232
+ Path(r_script_path).unlink()
233
+
234
+ @mcp.tool()
235
+ def set_bioprobe_qc_flags(
236
+ input_rds_path: Path,
237
+ output_rds_path: Path,
238
+ min_probe_ratio: float = 0.1,
239
+ percent_fail_grubbs: float = 20,
240
+ remove_local_outliers: bool = True,
241
+ ) -> dict:
242
+ """
243
+ Sets probe QC flags in a NanoStringGeomxSet object.
244
+
245
+ Args:
246
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
247
+ output_rds_path: Path for the output RDS file with QC flags applied.
248
+ min_probe_ratio: Minimum ratio of probes to the geometric mean of all probes.
249
+ percent_fail_grubbs: Percentage of segments that must fail Grubbs test for a probe to be flagged.
250
+ remove_local_outliers: If TRUE, local outliers will be removed.
251
+
252
+ Returns:
253
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
254
+ """
255
+ # --- Input Validation ---
256
+ if not input_rds_path.is_file():
257
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
258
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
259
+
260
+ # --- R Script Generation ---
261
+ r_script_content = f"""
262
+ library(GeomxTools)
263
+
264
+ tryCatch({{
265
+ input_rds <- "{input_rds_path}"
266
+ output_rds <- "{output_rds_path}"
267
+
268
+ geomx_data <- readRDS(input_rds)
269
+
270
+ qc_cutoffs <- list(
271
+ minProbeRatio = {min_probe_ratio},
272
+ percentFailGrubbs = {percent_fail_grubbs}
273
+ )
274
+
275
+ geomx_data_qc <- setBioProbeQCFlags(
276
+ geomx_data,
277
+ qcCutoffs = qc_cutoffs,
278
+ removeLocalOutliers = {str(remove_local_outliers).upper()}
279
+ )
280
+
281
+ saveRDS(geomx_data_qc, file = output_rds)
282
+ cat("Successfully applied bioprobe QC flags and saved to", output_rds, "\\n")
283
+ }}, error = function(e) {{
284
+ message("R script failed with error: ", e$message)
285
+ quit(status = 1)
286
+ }})
287
+ """
288
+
289
+ # --- Subprocess Execution ---
290
+ command_executed = ""
291
+ try:
292
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
293
+ r_script_file.write(r_script_content)
294
+ r_script_path = r_script_file.name
295
+
296
+ cmd = ["Rscript", r_script_path]
297
+ command_executed = " ".join(cmd)
298
+
299
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
300
+
301
+ return {
302
+ "command_executed": command_executed,
303
+ "stdout": process.stdout,
304
+ "stderr": process.stderr,
305
+ "output_files": [str(output_rds_path)]
306
+ }
307
+ except FileNotFoundError:
308
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
309
+ except subprocess.CalledProcessError as e:
310
+ return {
311
+ "command_executed": command_executed,
312
+ "stdout": e.stdout,
313
+ "stderr": e.stderr,
314
+ "error": "R script execution failed.",
315
+ "return_code": e.returncode,
316
+ "output_files": []
317
+ }
318
+ finally:
319
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
320
+ Path(r_script_path).unlink()
321
+
322
+ @mcp.tool()
323
+ def subset_geomx_set(
324
+ input_rds_path: Path,
325
+ output_rds_path: Path,
326
+ subset_by_pdata: bool = True,
327
+ subset_column: Optional[str] = None,
328
+ subset_values: Optional[List[str]] = None,
329
+ ) -> dict:
330
+ """
331
+ Subsets a NanoStringGeomxSet object based on phenotype (pData) or feature (fData) annotations.
332
+
333
+ Args:
334
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
335
+ output_rds_path: Path for the output subsetted RDS file.
336
+ subset_by_pdata: If True, subset by sample annotations (pData). If False, subset by feature annotations (fData).
337
+ subset_column: The column name in pData or fData to use for subsetting.
338
+ subset_values: A list of values to keep from the subset_column.
339
+
340
+ Returns:
341
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
342
+ """
343
+ # --- Input Validation ---
344
+ if not input_rds_path.is_file():
345
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
346
+ if not subset_column or not subset_values:
347
+ raise ValueError("subset_column and subset_values must be provided for subsetting.")
348
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
349
+
350
+ # --- R Script Generation ---
351
+ subset_values_r = f'c({", ".join(f"{v}" for v in subset_values)})'
352
+
353
+ r_script_content = f"""
354
+ library(GeomxTools)
355
+ library(Biobase)
356
+
357
+ tryCatch({{
358
+ object <- readRDS("{input_rds_path}")
359
+ subset_col <- "{subset_column}"
360
+ subset_vals <- {subset_values_r}
361
+
362
+ if ({str(subset_by_pdata).upper()}) {{
363
+ logic <- pData(object)[[subset_col]] %in% subset_vals
364
+ logic[is.na(logic)] <- FALSE
365
+ subset_object <- object[, logic]
366
+ }} else {{
367
+ logic <- fData(object)[[subset_col]] %in% subset_vals
368
+ logic[is.na(logic)] <- FALSE
369
+ subset_object <- object[logic, ]
370
+ }}
371
+
372
+ saveRDS(subset_object, file = "{output_rds_path}")
373
+ cat("Successfully subset object and saved to", "{output_rds_path}", "\\n")
374
+ }}, error = function(e) {{
375
+ message("R script failed with error: ", e$message)
376
+ quit(status = 1)
377
+ }})
378
+ """
379
+
380
+ # --- Subprocess Execution ---
381
+ command_executed = ""
382
+ try:
383
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
384
+ r_script_file.write(r_script_content)
385
+ r_script_path = r_script_file.name
386
+
387
+ cmd = ["Rscript", r_script_path]
388
+ command_executed = " ".join(cmd)
389
+
390
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
391
+
392
+ return {
393
+ "command_executed": command_executed,
394
+ "stdout": process.stdout,
395
+ "stderr": process.stderr,
396
+ "output_files": [str(output_rds_path)]
397
+ }
398
+ except FileNotFoundError:
399
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
400
+ except subprocess.CalledProcessError as e:
401
+ return {
402
+ "command_executed": command_executed,
403
+ "stdout": e.stdout,
404
+ "stderr": e.stderr,
405
+ "error": "R script execution failed.",
406
+ "return_code": e.returncode,
407
+ "output_files": []
408
+ }
409
+ finally:
410
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
411
+ Path(r_script_path).unlink()
412
+
413
+ @mcp.tool()
414
+ def aggregate_counts(
415
+ input_rds_path: Path,
416
+ output_rds_path: Path,
417
+ elt: str = "exprs",
418
+ ) -> dict:
419
+ """
420
+ Aggregates probe-level counts to the target level in a NanoStringGeomxSet object.
421
+
422
+ Args:
423
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
424
+ output_rds_path: Path for the output RDS file with aggregated counts.
425
+ elt: The name of the assay data element to aggregate.
426
+
427
+ Returns:
428
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
429
+ """
430
+ # --- Input Validation ---
431
+ if not input_rds_path.is_file():
432
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
433
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
434
+
435
+ # --- R Script Generation ---
436
+ r_script_content = f"""
437
+ library(GeomxTools)
438
+
439
+ tryCatch({{
440
+ geomx_data <- readRDS("{input_rds_path}")
441
+ aggregated_data <- aggregateCounts(geomx_data, elt = "{elt}")
442
+
443
+ saveRDS(aggregated_data, file = "{output_rds_path}")
444
+ cat("Successfully aggregated counts and saved to", "{output_rds_path}", "\\n")
445
+ }}, error = function(e) {{
446
+ message("R script failed with error: ", e$message)
447
+ quit(status = 1)
448
+ }})
449
+ """
450
+
451
+ # --- Subprocess Execution ---
452
+ command_executed = ""
453
+ try:
454
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
455
+ r_script_file.write(r_script_content)
456
+ r_script_path = r_script_file.name
457
+
458
+ cmd = ["Rscript", r_script_path]
459
+ command_executed = " ".join(cmd)
460
+
461
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
462
+
463
+ return {
464
+ "command_executed": command_executed,
465
+ "stdout": process.stdout,
466
+ "stderr": process.stderr,
467
+ "output_files": [str(output_rds_path)]
468
+ }
469
+ except FileNotFoundError:
470
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
471
+ except subprocess.CalledProcessError as e:
472
+ return {
473
+ "command_executed": command_executed,
474
+ "stdout": e.stdout,
475
+ "stderr": e.stderr,
476
+ "error": "R script execution failed.",
477
+ "return_code": e.returncode,
478
+ "output_files": []
479
+ }
480
+ finally:
481
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
482
+ Path(r_script_path).unlink()
483
+
484
+ @mcp.tool()
485
+ def normalize_geomx(
486
+ input_rds_path: Path,
487
+ output_rds_path: Path,
488
+ norm_method: str,
489
+ from_elt: str = "exprs",
490
+ to_elt: str = "exprs_norm",
491
+ housekeepers: Optional[List[str]] = None,
492
+ ) -> dict:
493
+ """
494
+ Normalizes the count data in a NanoStringGeomxSet object.
495
+
496
+ Args:
497
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
498
+ output_rds_path: Path for the output RDS file with normalized data.
499
+ norm_method: Normalization method. Must be one of 'quant', 'neg', 'hk'.
500
+ from_elt: The assay data element to use for normalization.
501
+ to_elt: The name of the new assay data element to store normalized values.
502
+ housekeepers: A list of housekeeper gene names, required if norm_method is 'hk'.
503
+
504
+ Returns:
505
+ A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file.
506
+ """
507
+ # --- Input Validation ---
508
+ if not input_rds_path.is_file():
509
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
510
+
511
+ valid_methods = ["quant", "neg", "hk"]
512
+ if norm_method not in valid_methods:
513
+ raise ValueError(f"Invalid norm_method '{norm_method}'. Must be one of {valid_methods}.")
514
+
515
+ if norm_method == "hk" and not housekeepers:
516
+ raise ValueError("Housekeeper genes must be provided for 'hk' normalization.")
517
+
518
+ output_rds_path.parent.mkdir(parents=True, exist_ok=True)
519
+
520
+ housekeepers_r = f'c({", ".join(f"{hk}" for hk in housekeepers)})' if housekeepers else "NULL"
521
+
522
+ # --- R Script Generation ---
523
+ r_script_content = f"""
524
+ library(GeomxTools)
525
+
526
+ tryCatch({{
527
+ geomx_data <- readRDS("{input_rds_path}")
528
+
529
+ normalized_data <- normalize(
530
+ geomx_data,
531
+ norm.method = "{norm_method}",
532
+ fromElt = "{from_elt}",
533
+ toElt = "{to_elt}",
534
+ housekeepers = {housekeepers_r}
535
+ )
536
+
537
+ saveRDS(normalized_data, file = "{output_rds_path}")
538
+ cat("Successfully normalized data and saved to", "{output_rds_path}", "\\n")
539
+ }}, error = function(e) {{
540
+ message("R script failed with error: ", e$message)
541
+ quit(status = 1)
542
+ }})
543
+ """
544
+
545
+ # --- Subprocess Execution ---
546
+ command_executed = ""
547
+ try:
548
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
549
+ r_script_file.write(r_script_content)
550
+ r_script_path = r_script_file.name
551
+
552
+ cmd = ["Rscript", r_script_path]
553
+ command_executed = " ".join(cmd)
554
+
555
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
556
+
557
+ return {
558
+ "command_executed": command_executed,
559
+ "stdout": process.stdout,
560
+ "stderr": process.stderr,
561
+ "output_files": [str(output_rds_path)]
562
+ }
563
+ except FileNotFoundError:
564
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
565
+ except subprocess.CalledProcessError as e:
566
+ return {
567
+ "command_executed": command_executed,
568
+ "stdout": e.stdout,
569
+ "stderr": e.stderr,
570
+ "error": "R script execution failed.",
571
+ "return_code": e.returncode,
572
+ "output_files": []
573
+ }
574
+ finally:
575
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
576
+ Path(r_script_path).unlink()
577
+
578
+ @mcp.tool()
579
+ def mixed_model_de(
580
+ input_rds_path: Path,
581
+ output_csv_path: Path,
582
+ elt: str,
583
+ model_formula_fixed: str,
584
+ group_var: str,
585
+ model_formula_random: Optional[str] = None,
586
+ contrasts: Optional[List[str]] = None,
587
+ n_cores: int = 1,
588
+ ) -> dict:
589
+ """
590
+ Performs differential expression analysis using a linear mixed model.
591
+
592
+ Args:
593
+ input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object.
594
+ output_csv_path: Path for the output CSV file with DE results.
595
+ elt: The assay data element to use for the analysis (e.g., 'exprs_norm').
596
+ model_formula_fixed: The fixed effects part of the model formula (e.g., 'region + diseaseStatus').
597
+ group_var: The main variable of interest for testing (e.g., 'diseaseStatus').
598
+ model_formula_random: The random effects part of the model formula (e.g., '(1|slideName)').
599
+ contrasts: A list of contrasts to test (e.g., ['diseaseA - diseaseB', 'diseaseC - diseaseB']).
600
+ n_cores: Number of cores to use for parallel processing.
601
+
602
+ Returns:
603
+ A dictionary containing the execution command, stdout, stderr, and the path to the output CSV file.
604
+ """
605
+ # --- Input Validation ---
606
+ if not input_rds_path.is_file():
607
+ raise ValueError(f"Input RDS file not found: {input_rds_path}")
608
+ if n_cores < 1:
609
+ raise ValueError("n_cores must be at least 1.")
610
+ output_csv_path.parent.mkdir(parents=True, exist_ok=True)
611
+
612
+ # --- R Script Generation ---
613
+ formula_str = f"~ {model_formula_fixed}"
614
+ if model_formula_random:
615
+ formula_str += f" + {model_formula_random}"
616
+
617
+ contrasts_r = "NULL"
618
+ if contrasts:
619
+ contrasts_r = f'c({", ".join(f"{c}" for c in contrasts)})'
620
+
621
+ r_script_content = f"""
622
+ library(GeomxTools)
623
+ library(limma)
624
+ library(Biobase)
625
+
626
+ tryCatch({{
627
+ object <- readRDS("{input_rds_path}")
628
+
629
+ model_formula <- as.formula("{formula_str}")
630
+
631
+ contrast_matrix <- NULL
632
+ if (!is.null({contrasts_r})) {{
633
+ contrast_matrix <- makeContrasts(contrasts = {contrasts_r}, levels = unique(pData(object)[["{group_var}"]]))
634
+ }}
635
+
636
+ results <- mixedModelDE(
637
+ object,
638
+ elt = "{elt}",
639
+ modelFormula = model_formula,
640
+ groupVar = "{group_var}",
641
+ nCores = {n_cores},
642
+ multiCore = {str(n_cores > 1).upper()},
643
+ contrasts = contrast_matrix
644
+ )
645
+
646
+ write.csv(results, file = "{output_csv_path}", row.names = FALSE)
647
+ cat("Successfully performed DE analysis and saved results to", "{output_csv_path}", "\\n")
648
+ }}, error = function(e) {{
649
+ message("R script failed with error: ", e$message)
650
+ quit(status = 1)
651
+ }})
652
+ """
653
+
654
+ # --- Subprocess Execution ---
655
+ command_executed = ""
656
+ try:
657
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file:
658
+ r_script_file.write(r_script_content)
659
+ r_script_path = r_script_file.name
660
+
661
+ cmd = ["Rscript", r_script_path]
662
+ command_executed = " ".join(cmd)
663
+
664
+ process = subprocess.run(cmd, capture_output=True, text=True, check=True)
665
+
666
+ return {
667
+ "command_executed": command_executed,
668
+ "stdout": process.stdout,
669
+ "stderr": process.stderr,
670
+ "output_files": [str(output_csv_path)]
671
+ }
672
+ except FileNotFoundError:
673
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
674
+ except subprocess.CalledProcessError as e:
675
+ return {
676
+ "command_executed": command_executed,
677
+ "stdout": e.stdout,
678
+ "stderr": e.stderr,
679
+ "error": "R script execution failed.",
680
+ "return_code": e.returncode,
681
+ "output_files": []
682
+ }
683
+ finally:
684
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
685
+ Path(r_script_path).unlink()
686
+
687
+ if __name__ == "__main__":
688
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_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-geomxtools/app/bioconductor-geomxtools_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_geomxtools'
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-geomxtools/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-geomxtools:
5
+ build: .
6
+ image: mcp-bioconductor-geomxtools:latest
7
+ container_name: mcp-bioconductor-geomxtools
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-geomxtools
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-geomxtools/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-geomxtools
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-geomxtools/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-glmgampoi/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-glmgampoi:
5
+ build: .
6
+ image: mcp-bioconductor-glmgampoi:latest
7
+ container_name: mcp-bioconductor-glmgampoi
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-glmgampoi
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-glmgampoi/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-infercnv/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-infercnv via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-infercnv -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-infercnv_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-infercnv_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-infercnv_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-infercnv/app/bioconductor-infercnv_server.py ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+ import tempfile
6
+ import shlex
7
+
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ SERVER_NAME = 'local_bioconductor_infercnv'
11
+ mcp = FastMCP(SERVER_NAME)
12
+
13
+ @mcp.tool()
14
+ def infercnv_run(
15
+ raw_counts_matrix: str,
16
+ annotations_file: str,
17
+ gene_order_file: str,
18
+ out_dir: str,
19
+ ref_group_names: Optional[List[str]] = None,
20
+ cutoff: float = 1.0,
21
+ min_cells_per_gene: int = 3,
22
+ cluster_by_groups: bool = True,
23
+ denoise: bool = False,
24
+ hmm: bool = False,
25
+ hmm_type: str = "i6",
26
+ analysis_mode: str = "samples",
27
+ num_threads: int = 1,
28
+ plot_steps: bool = False,
29
+ no_plot: bool = False,
30
+ window_length: int = 101,
31
+ max_centered_threshold: float = 3.0,
32
+ leiden_resolution: float = 0.05,
33
+ ):
34
+ """
35
+ Run the full InferCNV pipeline to identify somatic copy number alterations in single-cell RNA-seq data.
36
+
37
+ Args:
38
+ raw_counts_matrix: Path to the matrix of gene expression counts (genes as rows, cells as columns).
39
+ annotations_file: Path to the cell annotations file (cell name and group).
40
+ gene_order_file: Path to the gene positions file (gene, chromosome, start, stop).
41
+ out_dir: Directory to save the output files.
42
+ ref_group_names: List of group names to use as reference (normal) cells. If None, all cells are used.
43
+ cutoff: Threshold for gene expression. Use 1.0 for Smart-seq2 and 0.1 for 10x Genomics.
44
+ min_cells_per_gene: Minimum number of cells a gene must be expressed in to be kept.
45
+ cluster_by_groups: Whether to cluster cells by their annotation groups.
46
+ denoise: Whether to apply denoising filters.
47
+ hmm: Whether to run the Hidden Markov Model (HMM) to predict CNV states.
48
+ hmm_type: Type of HMM to use ('i6' for 6-state model, 'i3' for 3-state model).
49
+ analysis_mode: Analysis mode ('samples' or 'subclusters').
50
+ num_threads: Number of CPU threads to use for parallel processing.
51
+ plot_steps: Whether to generate plots for every intermediate step.
52
+ no_plot: If True, skips the final heatmap generation.
53
+ window_length: Length of the moving average window for smoothing.
54
+ max_centered_threshold: Maximum value for centering the expression data.
55
+ leiden_resolution: Resolution for Leiden clustering if analysis_mode is 'subclusters'.
56
+ """
57
+ # Input validation
58
+ raw_path = Path(raw_counts_matrix)
59
+ ann_path = Path(annotations_file)
60
+ gene_path = Path(gene_order_file)
61
+ out_path = Path(out_dir)
62
+
63
+ if not raw_path.exists():
64
+ return {"error": f"Raw counts matrix not found: {raw_counts_matrix}"}
65
+ if not ann_path.exists():
66
+ return {"error": f"Annotations file not found: {annotations_file}"}
67
+ if not gene_path.exists():
68
+ return {"error": f"Gene order file not found: {gene_order_file}"}
69
+
70
+ if hmm_type not in ["i6", "i3"]:
71
+ return {"error": "hmm_type must be either 'i6' or 'i3'"}
72
+
73
+ if analysis_mode not in ["samples", "subclusters"]:
74
+ return {"error": "analysis_mode must be either 'samples' or 'subclusters'"}
75
+
76
+ os.makedirs(out_path, exist_ok=True)
77
+
78
+ # Prepare R vector for reference groups
79
+ if ref_group_names:
80
+ ref_groups_r = "c(" + ", ".join([f"'{g}'" for g in ref_group_names]) + ")"
81
+ else:
82
+ ref_groups_r = "NULL"
83
+
84
+ # Construct R script
85
+ r_script_content = f"""
86
+ library(infercnv)
87
+
88
+ # Create InferCNV Object
89
+ infercnv_obj = CreateInfercnvObject(
90
+ raw_counts_matrix = "{raw_path.absolute()}",
91
+ gene_order_file = "{gene_path.absolute()}",
92
+ annotations_file = "{ann_path.absolute()}",
93
+ ref_group_names = {ref_groups_r}
94
+ )
95
+
96
+ # Run InferCNV Pipeline
97
+ infercnv_obj = infercnv::run(
98
+ infercnv_obj,
99
+ cutoff = {cutoff},
100
+ min_cells_per_gene = {min_cells_per_gene},
101
+ out_dir = "{out_path.absolute()}",
102
+ cluster_by_groups = {str(cluster_by_groups).upper()},
103
+ denoise = {str(denoise).upper()},
104
+ HMM = {str(hmm).upper()},
105
+ HMM_type = "{hmm_type}",
106
+ analysis_mode = "{analysis_mode}",
107
+ num_threads = {num_threads},
108
+ plot_steps = {str(plot_steps).upper()},
109
+ no_plot = {str(no_plot).upper()},
110
+ window_length = {window_length},
111
+ max_centered_threshold = {max_centered_threshold},
112
+ leiden_resolution = {leiden_resolution}
113
+ )
114
+ """
115
+
116
+ try:
117
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
118
+ tmp.write(r_script_content)
119
+ tmp_path = tmp.name
120
+
121
+ cmd = ["Rscript", tmp_path]
122
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
123
+
124
+ # Cleanup temp file
125
+ os.unlink(tmp_path)
126
+
127
+ # Identify output files
128
+ output_files = [str(f) for f in out_path.glob("*") if f.is_file()]
129
+
130
+ return {
131
+ "command_executed": " ".join(cmd),
132
+ "stdout": result.stdout,
133
+ "stderr": result.stderr,
134
+ "output_files": output_files,
135
+ "status": "success"
136
+ }
137
+
138
+ except subprocess.CalledProcessError as e:
139
+ if os.path.exists(tmp_path):
140
+ os.unlink(tmp_path)
141
+ return {
142
+ "command_executed": " ".join(e.cmd),
143
+ "stdout": e.stdout,
144
+ "stderr": e.stderr,
145
+ "error": "InferCNV execution failed."
146
+ }
147
+ except Exception as e:
148
+ return {"error": str(e)}
149
+
150
+ @mcp.tool()
151
+ def infercnv_plot(
152
+ infercnv_obj_path: str,
153
+ out_dir: str,
154
+ output_filename: str = "infercnv_plot",
155
+ color_safe_pal: bool = False,
156
+ title: str = "InferCNV Heatmap",
157
+ cluster_by_groups: bool = True,
158
+ x_center: float = 1.0,
159
+ x_range: Optional[float] = None,
160
+ custom_color_pal: Optional[List[str]] = None,
161
+ ):
162
+ """
163
+ Generate or regenerate plots from a saved InferCNV object.
164
+
165
+ Args:
166
+ infercnv_obj_path: Path to the saved .rds or .obj InferCNV object.
167
+ out_dir: Directory to save the plot.
168
+ output_filename: Name of the output plot file (without extension).
169
+ color_safe_pal: Use a color-blind safe palette.
170
+ title: Title of the plot.
171
+ cluster_by_groups: Whether to cluster by groups in the plot.
172
+ x_center: Value to center the color scale on (usually 1.0).
173
+ x_range: Range of values to display (e.g., 0.1 means 0.9 to 1.1).
174
+ custom_color_pal: Optional list of colors for the heatmap palette.
175
+ """
176
+ obj_path = Path(infercnv_obj_path)
177
+ out_path = Path(out_dir)
178
+
179
+ if not obj_path.exists():
180
+ return {"error": f"InferCNV object not found: {infercnv_obj_path}"}
181
+
182
+ os.makedirs(out_path, exist_ok=True)
183
+
184
+ x_range_r = f"{x_range}" if x_range is not None else "NULL"
185
+ color_pal_r = "NULL"
186
+ if custom_color_pal:
187
+ color_pal_r = "c(" + ", ".join([f"'{c}'" for c in custom_color_pal]) + ")"
188
+
189
+ r_script_content = f"""
190
+ library(infercnv)
191
+ infercnv_obj = readRDS("{obj_path.absolute()}")
192
+
193
+ plot_cnv(
194
+ infercnv_obj,
195
+ out_dir = "{out_path.absolute()}",
196
+ output_filename = "{output_filename}",
197
+ color_safe_pal = {str(color_safe_pal).upper()},
198
+ title = "{title}",
199
+ cluster_by_groups = {str(cluster_by_groups).upper()},
200
+ x_center = {x_center},
201
+ x_range = {x_range_r},
202
+ custom_color_pal = {color_pal_r}
203
+ )
204
+ """
205
+
206
+ try:
207
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
208
+ tmp.write(r_script_content)
209
+ tmp_path = tmp.name
210
+
211
+ cmd = ["Rscript", tmp_path]
212
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
213
+ os.unlink(tmp_path)
214
+
215
+ return {
216
+ "command_executed": " ".join(cmd),
217
+ "stdout": result.stdout,
218
+ "stderr": result.stderr,
219
+ "output_files": [str(f) for f in out_path.glob(f"{output_filename}*")],
220
+ "status": "success"
221
+ }
222
+
223
+ except subprocess.CalledProcessError as e:
224
+ if os.path.exists(tmp_path):
225
+ os.unlink(tmp_path)
226
+ return {
227
+ "command_executed": " ".join(e.cmd),
228
+ "stdout": e.stdout,
229
+ "stderr": e.stderr,
230
+ "error": "InferCNV plotting failed."
231
+ }
232
+ except Exception as e:
233
+ return {"error": str(e)}
234
+
235
+ @mcp.tool()
236
+ def infercnv_filter_genes(
237
+ raw_counts_matrix: str,
238
+ gene_order_file: str,
239
+ output_matrix_path: str,
240
+ min_cells_per_gene: int = 3,
241
+ ):
242
+ """
243
+ Pre-filter a counts matrix to remove genes expressed in fewer than a threshold number of cells.
244
+
245
+ Args:
246
+ raw_counts_matrix: Path to the input counts matrix.
247
+ gene_order_file: Path to the gene order file.
248
+ output_matrix_path: Path to save the filtered matrix.
249
+ min_cells_per_gene: Minimum number of cells a gene must be expressed in.
250
+ """
251
+ raw_path = Path(raw_counts_matrix)
252
+ gene_path = Path(gene_order_file)
253
+ out_path = Path(output_matrix_path)
254
+
255
+ if not raw_path.exists():
256
+ return {"error": f"Input matrix not found: {raw_counts_matrix}"}
257
+
258
+ r_script_content = f"""
259
+ library(infercnv)
260
+ # Load data
261
+ counts = read.table("{raw_path.absolute()}", header=TRUE, row.names=1, check.names=FALSE)
262
+ # Filter
263
+ gene_counts = rowSums(counts > 0)
264
+ filtered_counts = counts[gene_counts >= {min_cells_per_gene}, ]
265
+ # Save
266
+ write.table(filtered_counts, file="{out_path.absolute()}", quote=FALSE, sep='\\t')
267
+ """
268
+
269
+ try:
270
+ with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp:
271
+ tmp.write(r_script_content)
272
+ tmp_path = tmp.name
273
+
274
+ cmd = ["Rscript", tmp_path]
275
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
276
+ os.unlink(tmp_path)
277
+
278
+ return {
279
+ "command_executed": " ".join(cmd),
280
+ "stdout": result.stdout,
281
+ "stderr": result.stderr,
282
+ "output_files": [str(out_path)],
283
+ "status": "success"
284
+ }
285
+ except subprocess.CalledProcessError as e:
286
+ if os.path.exists(tmp_path):
287
+ os.unlink(tmp_path)
288
+ return {
289
+ "command_executed": " ".join(e.cmd),
290
+ "stdout": e.stdout,
291
+ "stderr": e.stderr,
292
+ "error": "Filtering failed."
293
+ }
294
+ except Exception as e:
295
+ return {"error": str(e)}
296
+
297
+ if __name__ == "__main__":
298
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-infercnv/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-infercnv:
5
+ build: .
6
+ image: mcp-bioconductor-infercnv:latest
7
+ container_name: mcp-bioconductor-infercnv
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-infercnv
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-infercnv/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-infercnv
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-infercnv/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Install Miniconda
8
+ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && bash /tmp/miniconda.sh -b -p /opt/conda && rm /tmp/miniconda.sh
9
+
10
+ # Add conda to PATH
11
+ ENV PATH="/opt/conda/bin:$PATH"
12
+
13
+ # Install bioconductor-org.hs.eg.db via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-org.hs.eg.db -y && conda clean -a
15
+
16
+ # Install Python dependencies
17
+ RUN pip install uv
18
+ RUN uv pip install --system fastmcp
19
+
20
+ # Create app directory
21
+ WORKDIR /app
22
+
23
+ # Copy your MCP server
24
+ COPY app/bioconductor-org.hs.eg.db_server.py /app/
25
+
26
+ # Create workspace and output directories
27
+ RUN mkdir -p /app/workspace /app/output
28
+
29
+ # Make sure the server script is executable
30
+ RUN chmod +x /app/bioconductor-org.hs.eg.db_server.py
31
+
32
+ # Expose port for MCP over HTTP (optional)
33
+ EXPOSE 8000
34
+
35
+ # Health check
36
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)"
37
+
38
+ # Default command runs the MCP server via stdio
39
+ CMD ["python", "/app/bioconductor-org.hs.eg.db_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import json
3
+ import tempfile
4
+ from pathlib import Path
5
+ from typing import List, Optional, Dict, Any
6
+
7
+ def run_r_command(script: str) -> Dict[str, Any]:
8
+ """
9
+ Helper function to execute R code and capture output.
10
+ Uses jsonlite in R to return structured data.
11
+ """
12
+ # Wrap the script to load the library and output JSON
13
+ full_script = f"""
14
+ suppressPackageStartupMessages(library(org.hs.eg.db))
15
+ suppressPackageStartupMessages(library(jsonlite))
16
+
17
+ tryCatch({{
18
+ {script}
19
+ }}, error = function(e) {{
20
+ write(paste("ERROR:", e$message), stderr())
21
+ q(status = 1)
22
+ }})
23
+ """
24
+
25
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
26
+ tmp.write(full_script)
27
+ tmp_path = tmp.name
28
+
29
+ try:
30
+ process = subprocess.run(
31
+ ["Rscript", tmp_path],
32
+ capture_output=True,
33
+ text=True,
34
+ check=True
35
+ )
36
+ return {
37
+ "stdout": process.stdout,
38
+ "stderr": process.stderr,
39
+ "command_executed": f"Rscript {tmp_path}"
40
+ }
41
+ except subprocess.CalledProcessError as e:
42
+ return {
43
+ "error": e.stderr or e.stdout,
44
+ "command_executed": f"Rscript {tmp_path}",
45
+ "stdout": e.stdout,
46
+ "stderr": e.stderr
47
+ }
48
+ finally:
49
+ if Path(tmp_path).exists():
50
+ Path(tmp_path).unlink()
51
+
52
+ from mcp.server.fastmcp import FastMCP
53
+
54
+ SERVER_NAME = 'local_bioconductor_org_hs_eg_db'
55
+ mcp = FastMCP(SERVER_NAME)
56
+
57
+ @mcp.tool()
58
+ def org_hs_eg_db_select(
59
+ keys: List[str],
60
+ columns: List[str],
61
+ keytype: str = "ENTREZID",
62
+ ) -> Dict[str, Any]:
63
+ """
64
+ Retrieve annotations for the specified keys from the Human (org.hs.eg.db) database.
65
+
66
+ Args:
67
+ keys: A list of identifiers to look up (e.g., ["7157", "4312"] or ["TP53", "BRCA1"]).
68
+ columns: The types of data to retrieve (e.g., ["SYMBOL", "GENENAME", "ENSEMBL"]).
69
+ keytype: The type of the input keys (e.g., "ENTREZID", "SYMBOL", "ENSEMBL").
70
+ """
71
+ # Validation
72
+ if not keys:
73
+ return {"error": "At least one key must be provided."}
74
+ if not columns:
75
+ return {"error": "At least one column must be provided."}
76
+
77
+ # Format R vectors
78
+ r_keys = 'c("' + '","'.join(keys) + '")'
79
+ r_cols = 'c("' + '","'.join(columns) + '")'
80
+
81
+ script = f"""
82
+ res <- select(org.hs.eg.db, keys = {r_keys}, columns = {r_cols}, keytype = "{keytype}")
83
+ cat(toJSON(res, pretty = TRUE))
84
+ """
85
+
86
+ result = run_r_command(script)
87
+ return result
88
+
89
+ @mcp.tool()
90
+ def org_hs_eg_db_keytypes() -> Dict[str, Any]:
91
+ """
92
+ List all available types of identifiers (keytypes) that can be used as input for queries.
93
+ Common types include ENTREZID, SYMBOL, ENSEMBL, and UNIPROT.
94
+ """
95
+ script = """
96
+ res <- keytypes(org.hs.eg.db)
97
+ cat(toJSON(res))
98
+ """
99
+ return run_r_command(script)
100
+
101
+ @mcp.tool()
102
+ def org_hs_eg_db_columns() -> Dict[str, Any]:
103
+ """
104
+ List all available annotation columns that can be retrieved from the database.
105
+ """
106
+ script = """
107
+ res <- columns(org.hs.eg.db)
108
+ cat(toJSON(res))
109
+ """
110
+ return run_r_command(script)
111
+
112
+ @mcp.tool()
113
+ def org_hs_eg_db_map_symbol_to_entrez(
114
+ symbols: List[str]
115
+ ) -> Dict[str, Any]:
116
+ """
117
+ A convenience tool to quickly map Human Gene Symbols to Entrez IDs.
118
+
119
+ Args:
120
+ symbols: List of gene symbols (e.g., ["TP53", "APOE"]).
121
+ """
122
+ if not symbols:
123
+ return {"error": "No symbols provided."}
124
+
125
+ r_keys = 'c("' + '","'.join(symbols) + '")'
126
+ script = f"""
127
+ res <- select(org.hs.eg.db, keys = {r_keys}, columns = c("ENTREZID"), keytype = "SYMBOL")
128
+ cat(toJSON(res, pretty = TRUE))
129
+ """
130
+ return run_r_command(script)
131
+
132
+ @mcp.tool()
133
+ def org_hs_eg_db_get_keys(
134
+ keytype: str = "SYMBOL",
135
+ pattern: str = "",
136
+ limit: int = 100
137
+ ) -> Dict[str, Any]:
138
+ """
139
+ Retrieve a list of all valid keys of a specific type, optionally filtered by a pattern.
140
+
141
+ Args:
142
+ keytype: The type of keys to retrieve (e.g., "SYMBOL", "ENSEMBL").
143
+ pattern: A string pattern to filter keys (uses grep-style matching).
144
+ limit: Maximum number of keys to return to prevent overwhelming output.
145
+ """
146
+ if limit <= 0:
147
+ limit = 100
148
+
149
+ script = f"""
150
+ all_keys <- keys(org.hs.eg.db, keytype = "{keytype}")
151
+ if ("{pattern}" != "") {{
152
+ all_keys <- all_keys[grep("{pattern}", all_keys)]
153
+ }}
154
+ res <- head(all_keys, {limit})
155
+ cat(toJSON(res))
156
+ """
157
+ return run_r_command(script)
158
+
159
+ @mcp.tool()
160
+ def org_hs_eg_db_metadata() -> Dict[str, Any]:
161
+ """
162
+ Get metadata about the org.hs.eg.db package, including version, organism, and data sources.
163
+ """
164
+ script = """
165
+ res <- metadata(org.hs.eg.db)
166
+ cat(toJSON(res, pretty = TRUE))
167
+ """
168
+ return run_r_command(script)
169
+
170
+ @mcp.tool()
171
+ def org_hs_eg_db_map_ids(
172
+ ids: List[str],
173
+ from_type: str,
174
+ to_type: str
175
+ ) -> Dict[str, Any]:
176
+ """
177
+ Generic tool to map identifiers from one type to another.
178
+
179
+ Args:
180
+ ids: List of input identifiers.
181
+ from_type: The source identifier type (e.g., "ENSEMBL").
182
+ to_type: The target identifier type (e.g., "SYMBOL").
183
+ """
184
+ if not ids:
185
+ return {"error": "No IDs provided."}
186
+
187
+ r_keys = 'c("' + '","'.join(ids) + '")'
188
+ script = f"""
189
+ res <- select(org.hs.eg.db, keys = {r_keys}, columns = c("{to_type}"), keytype = "{from_type}")
190
+ cat(toJSON(res, pretty = TRUE))
191
+ """
192
+ return run_r_command(script)
193
+
194
+ if __name__ == "__main__":
195
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_shim_server.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_help_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_org_hs_eg_db'
13
+
14
+
15
+ class _ShimMCP:
16
+ @staticmethod
17
+ def tool(*args, **kwargs):
18
+ if args and callable(args[0]) and len(args) == 1 and not kwargs:
19
+ return args[0]
20
+ def _decorator(fn):
21
+ return fn
22
+ return _decorator
23
+
24
+
25
+ def _resolve_source_server():
26
+ if LOCAL_SERVER.exists() and LOCAL_SERVER.name != Path(__file__).name:
27
+ return LOCAL_SERVER
28
+ return SOURCE_SERVER
29
+
30
+
31
+ def _load_functions():
32
+ source_server = _resolve_source_server()
33
+ code = source_server.read_text(encoding="utf-8")
34
+ tree = ast.parse(code, filename=str(source_server))
35
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
36
+ namespace = {
37
+ "__name__": "__mcp_source__",
38
+ "mcp": _ShimMCP(),
39
+ }
40
+ exec(compile(code, str(source_server), "exec"), namespace, namespace)
41
+ loaded = []
42
+ for name in function_names:
43
+ fn = namespace.get(name)
44
+ if callable(fn):
45
+ loaded.append(fn)
46
+ return loaded
47
+
48
+
49
+ mcp = FastMCP(SERVER_NAME)
50
+ for _fn in _load_functions():
51
+ mcp.tool()(_fn)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-org.hs.eg.db:
5
+ build: .
6
+ image: mcp-bioconductor-org.hs.eg.db:latest
7
+ container_name: mcp-bioconductor-org.hs.eg.db
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-org.hs.eg.db
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-org.hs.eg.db/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-org.hs.eg.db
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-org.hs.eg.db/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-preprocesscore/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-preprocesscore via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-preprocesscore -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-preprocesscore_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-preprocesscore_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-preprocesscore_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-preprocesscore/app/bioconductor-preprocesscore_server.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+ import tempfile
6
+
7
+ def run_r_command(script_content: str):
8
+ """Helper to execute R code and handle errors."""
9
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp:
10
+ tmp.write("library(preprocessCore)\n")
11
+ tmp.write(script_content)
12
+ tmp_path = tmp.name
13
+
14
+ try:
15
+ result = subprocess.run(
16
+ ["Rscript", tmp_path],
17
+ capture_output=True,
18
+ text=True,
19
+ check=True
20
+ )
21
+ return result.stdout, result.stderr
22
+ except subprocess.CalledProcessError as e:
23
+ raise RuntimeError(f"R execution failed: {e.stderr}\nStdout: {e.stdout}")
24
+ finally:
25
+ if os.path.exists(tmp_path):
26
+ os.remove(tmp_path)
27
+
28
+ from mcp.server.fastmcp import FastMCP
29
+
30
+ SERVER_NAME = 'local_bioconductor_preprocesscore'
31
+ mcp = FastMCP(SERVER_NAME)
32
+
33
+ @mcp.tool()
34
+ def preprocesscore_normalize_quantiles(
35
+ input_file: str,
36
+ output_file: str,
37
+ keep_names: bool = True,
38
+ sep: str = ",",
39
+ header: bool = True
40
+ ):
41
+ """
42
+ Perform Quantile Normalization on a numeric matrix.
43
+
44
+ Args:
45
+ input_file: Path to the input CSV/TSV file containing the matrix.
46
+ output_file: Path where the normalized matrix will be saved.
47
+ keep_names: Whether to preserve row and column names in the output.
48
+ sep: Delimiter used in the input file (e.g., ',' or '\t').
49
+ header: Whether the input file has a header row.
50
+ """
51
+ input_path = Path(input_file)
52
+ output_path = Path(output_file)
53
+
54
+ if not input_path.exists():
55
+ return {"error": f"Input file {input_file} not found."}
56
+
57
+ r_script = f"""
58
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
59
+ normalized_data <- normalize.quantiles(data, keep.names={str(keep_names).upper()})
60
+ write.table(normalized_data, "{output_path}", sep="{sep}", col.names={str(header).upper()}, row.names=TRUE, quote=FALSE)
61
+ """
62
+
63
+ try:
64
+ stdout, stderr = run_r_command(r_script)
65
+ return {
66
+ "command_executed": "normalize.quantiles",
67
+ "stdout": stdout,
68
+ "stderr": stderr,
69
+ "output_files": [str(output_path)]
70
+ }
71
+ except Exception as e:
72
+ return {"error": str(e)}
73
+
74
+ @mcp.tool()
75
+ def preprocesscore_normalize_quantiles_robust(
76
+ input_file: str,
77
+ output_file: str,
78
+ remove_extreme: str = "both",
79
+ n_remove: int = 1,
80
+ use_log2: bool = False,
81
+ sep: str = ",",
82
+ header: bool = True
83
+ ):
84
+ """
85
+ Perform Robust Quantile Normalization.
86
+
87
+ Args:
88
+ input_file: Path to the input CSV/TSV file.
89
+ output_file: Path for the output file.
90
+ remove_extreme: How to remove outliers: 'none', 'left', 'right', or 'both'.
91
+ n_remove: Number of extreme values to remove.
92
+ use_log2: Whether to apply log2 transformation before normalization.
93
+ sep: Delimiter used in the input file.
94
+ header: Whether the input file has a header row.
95
+ """
96
+ input_path = Path(input_file)
97
+ output_path = Path(output_file)
98
+
99
+ if not input_path.exists():
100
+ return {"error": f"Input file {input_file} not found."}
101
+
102
+ if remove_extreme not in ["none", "left", "right", "both"]:
103
+ return {"error": "remove_extreme must be one of: none, left, right, both"}
104
+
105
+ r_script = f"""
106
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
107
+ normalized_data <- normalize.quantiles.robust(
108
+ data,
109
+ remove.extreme="{remove_extreme}",
110
+ n.remove={n_remove},
111
+ use.log2={str(use_log2).upper()}
112
+ )
113
+ write.table(normalized_data, "{output_path}", sep="{sep}", col.names={str(header).upper()}, row.names=TRUE, quote=FALSE)
114
+ """
115
+
116
+ try:
117
+ stdout, stderr = run_r_command(r_script)
118
+ return {
119
+ "command_executed": "normalize.quantiles.robust",
120
+ "stdout": stdout,
121
+ "stderr": stderr,
122
+ "output_files": [str(output_path)]
123
+ }
124
+ except Exception as e:
125
+ return {"error": str(e)}
126
+
127
+ @mcp.tool()
128
+ def preprocesscore_background_correct(
129
+ input_file: str,
130
+ output_file: str,
131
+ method: str = "rma",
132
+ sep: str = ",",
133
+ header: bool = True
134
+ ):
135
+ """
136
+ Perform background correction on a matrix of intensities.
137
+
138
+ Args:
139
+ input_file: Path to the input CSV/TSV file.
140
+ output_file: Path for the output file.
141
+ method: Correction method (typically 'rma').
142
+ sep: Delimiter used in the input file.
143
+ header: Whether the input file has a header row.
144
+ """
145
+ input_path = Path(input_file)
146
+ output_path = Path(output_file)
147
+
148
+ if not input_path.exists():
149
+ return {"error": f"Input file {input_file} not found."}
150
+
151
+ r_script = f"""
152
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
153
+ corrected_data <- background.correct(data, method="{method}")
154
+ write.table(corrected_data, "{output_path}", sep="{sep}", col.names={str(header).upper()}, row.names=TRUE, quote=FALSE)
155
+ """
156
+
157
+ try:
158
+ stdout, stderr = run_r_command(r_script)
159
+ return {
160
+ "command_executed": f"background.correct(method='{method}')",
161
+ "stdout": stdout,
162
+ "stderr": stderr,
163
+ "output_files": [str(output_path)]
164
+ }
165
+ except Exception as e:
166
+ return {"error": str(e)}
167
+
168
+ @mcp.tool()
169
+ def preprocesscore_sub_col_summarize_median_polish(
170
+ input_file: str,
171
+ output_file: str,
172
+ group_labels: List[int],
173
+ sep: str = ",",
174
+ header: bool = True
175
+ ):
176
+ """
177
+ Summarize columns of a matrix using Median Polish based on group labels.
178
+
179
+ Args:
180
+ input_file: Path to the input CSV/TSV file.
181
+ output_file: Path for the output file.
182
+ group_labels: A list of integers representing the group for each column (e.g., [1, 1, 2, 2]).
183
+ sep: Delimiter used in the input file.
184
+ header: Whether the input file has a header row.
185
+ """
186
+ input_path = Path(input_file)
187
+ output_path = Path(output_file)
188
+
189
+ if not input_path.exists():
190
+ return {"error": f"Input file {input_file} not found."}
191
+
192
+ # Convert Python list to R vector string
193
+ r_groups = f"c({','.join(map(str, group_labels))})"
194
+
195
+ r_script = f"""
196
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
197
+ groups <- as.integer({r_groups})
198
+ summarized <- subColSummarizeMedianPolish(data, groups)
199
+ write.table(summarized, "{output_path}", sep="{sep}", col.names=NA, quote=FALSE)
200
+ """
201
+
202
+ try:
203
+ stdout, stderr = run_r_command(r_script)
204
+ return {
205
+ "command_executed": "subColSummarizeMedianPolish",
206
+ "stdout": stdout,
207
+ "stderr": stderr,
208
+ "output_files": [str(output_path)]
209
+ }
210
+ except Exception as e:
211
+ return {"error": str(e)}
212
+
213
+ @mcp.tool()
214
+ def preprocesscore_sub_col_summarize_log_avg(
215
+ input_file: str,
216
+ output_file: str,
217
+ group_labels: List[int],
218
+ sep: str = ",",
219
+ header: bool = True
220
+ ):
221
+ """
222
+ Summarize columns of a matrix using Log-Average based on group labels.
223
+
224
+ Args:
225
+ input_file: Path to the input CSV/TSV file.
226
+ output_file: Path for the output file.
227
+ group_labels: A list of integers representing the group for each column.
228
+ """
229
+ input_path = Path(input_file)
230
+ output_path = Path(output_file)
231
+
232
+ if not input_path.exists():
233
+ return {"error": f"Input file {input_file} not found."}
234
+
235
+ r_groups = f"c({','.join(map(str, group_labels))})"
236
+
237
+ r_script = f"""
238
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
239
+ groups <- as.integer({r_groups})
240
+ summarized <- subColSummarizeLogAvg(data, groups)
241
+ write.table(summarized, "{output_path}", sep="{sep}", col.names=NA, quote=FALSE)
242
+ """
243
+
244
+ try:
245
+ stdout, stderr = run_r_command(r_script)
246
+ return {
247
+ "command_executed": "subColSummarizeLogAvg",
248
+ "stdout": stdout,
249
+ "stderr": stderr,
250
+ "output_files": [str(output_path)]
251
+ }
252
+ except Exception as e:
253
+ return {"error": str(e)}
254
+
255
+ @mcp.tool()
256
+ def preprocesscore_sub_col_summarize_log_median(
257
+ input_file: str,
258
+ output_file: str,
259
+ group_labels: List[int],
260
+ sep: str = ",",
261
+ header: bool = True
262
+ ):
263
+ """
264
+ Summarize columns of a matrix using Log-Median based on group labels.
265
+
266
+ Args:
267
+ input_file: Path to the input CSV/TSV file.
268
+ output_file: Path for the output file.
269
+ group_labels: A list of integers representing the group for each column.
270
+ """
271
+ input_path = Path(input_file)
272
+ output_path = Path(output_file)
273
+
274
+ if not input_path.exists():
275
+ return {"error": f"Input file {input_file} not found."}
276
+
277
+ r_groups = f"c({','.join(map(str, group_labels))})"
278
+
279
+ r_script = f"""
280
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
281
+ groups <- as.integer({r_groups})
282
+ summarized <- subColSummarizeLogMedian(data, groups)
283
+ write.table(summarized, "{output_path}", sep="{sep}", col.names=NA, quote=FALSE)
284
+ """
285
+
286
+ try:
287
+ stdout, stderr = run_r_command(r_script)
288
+ return {
289
+ "command_executed": "subColSummarizeLogMedian",
290
+ "stdout": stdout,
291
+ "stderr": stderr,
292
+ "output_files": [str(output_path)]
293
+ }
294
+ except Exception as e:
295
+ return {"error": str(e)}
296
+
297
+ @mcp.tool()
298
+ def preprocesscore_sub_col_summarize_biweight_midavg(
299
+ input_file: str,
300
+ output_file: str,
301
+ group_labels: List[int],
302
+ sep: str = ",",
303
+ header: bool = True
304
+ ):
305
+ """
306
+ Summarize columns of a matrix using Biweight Mid-average based on group labels.
307
+
308
+ Args:
309
+ input_file: Path to the input CSV/TSV file.
310
+ output_file: Path for the output file.
311
+ group_labels: A list of integers representing the group for each column.
312
+ """
313
+ input_path = Path(input_file)
314
+ output_path = Path(output_file)
315
+
316
+ if not input_path.exists():
317
+ return {"error": f"Input file {input_file} not found."}
318
+
319
+ r_groups = f"c({','.join(map(str, group_labels))})"
320
+
321
+ r_script = f"""
322
+ data <- as.matrix(read.table("{input_path}", header={str(header).upper()}, sep="{sep}"))
323
+ groups <- as.integer({r_groups})
324
+ summarized <- subColSummarizeBiweightMidavg(data, groups)
325
+ write.table(summarized, "{output_path}", sep="{sep}", col.names=NA, quote=FALSE)
326
+ """
327
+
328
+ try:
329
+ stdout, stderr = run_r_command(r_script)
330
+ return {
331
+ "command_executed": "subColSummarizeBiweightMidavg",
332
+ "stdout": stdout,
333
+ "stderr": stderr,
334
+ "output_files": [str(output_path)]
335
+ }
336
+ except Exception as e:
337
+ return {"error": str(e)}
338
+
339
+ if __name__ == "__main__":
340
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-preprocesscore/app/bioconductor-preprocesscore_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-preprocesscore/app/bioconductor-preprocesscore_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_preprocesscore'
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-preprocesscore/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-preprocesscore/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-preprocesscore:
5
+ build: .
6
+ image: mcp-bioconductor-preprocesscore:latest
7
+ container_name: mcp-bioconductor-preprocesscore
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-preprocesscore
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-preprocesscore/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-preprocesscore
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-preprocesscore/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp