czty commited on
Commit
b7da0d4
·
verified ·
1 Parent(s): a28ebe7

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. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/Dockerfile +40 -0
  2. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/bioawk_server.py +120 -0
  3. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/bioawk_shim_server.py +45 -0
  4. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/requirements.txt +1 -0
  5. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/docker-compose.yml +22 -0
  6. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/environment.yaml +10 -0
  7. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/requirements.txt +2 -0
  8. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/Dockerfile +40 -0
  9. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/bioconductor-ancombc_server.py +251 -0
  10. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/bioconductor-ancombc_shim_server.py +45 -0
  11. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/requirements.txt +1 -0
  12. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/docker-compose.yml +22 -0
  13. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/environment.yaml +10 -0
  14. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/requirements.txt +2 -0
  15. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/Dockerfile +40 -0
  16. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/requirements.txt +1 -0
  17. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/docker-compose.yml +22 -0
  18. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/environment.yaml +10 -0
  19. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/requirements.txt +2 -0
  20. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/Dockerfile +40 -0
  21. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_server.py +221 -0
  22. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_shim_server.py +45 -0
  23. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/requirements.txt +1 -0
  24. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/docker-compose.yml +22 -0
  25. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/environment.yaml +10 -0
  26. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/requirements.txt +2 -0
  27. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/Dockerfile +40 -0
  28. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_shim_server.py +45 -0
  29. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/docker-compose.yml +22 -0
  30. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/environment.yaml +10 -0
  31. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/requirements.txt +2 -0
  32. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/Dockerfile +40 -0
  33. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/bioconductor-ensembldb_server.py +433 -0
  34. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/bioconductor-ensembldb_shim_server.py +45 -0
  35. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/requirements.txt +1 -0
  36. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/docker-compose.yml +22 -0
  37. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/environment.yaml +10 -0
  38. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/requirements.txt +2 -0
  39. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/Dockerfile +40 -0
  40. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_shim_server.py +45 -0
  41. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/environment.yaml +10 -0
  42. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/requirements.txt +2 -0
  43. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/Dockerfile +40 -0
  44. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/bioconductor-genomeinfodbdata_server.py +130 -0
  45. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/bioconductor-genomeinfodbdata_shim_server.py +45 -0
  46. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/requirements.txt +1 -0
  47. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/docker-compose.yml +22 -0
  48. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/environment.yaml +10 -0
  49. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/requirements.txt +2 -0
  50. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.db/Dockerfile +40 -0
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/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 bioawk via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioawk -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/bioawk_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/bioawk_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/bioawk_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/bioawk_server.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Dict, Any
4
+
5
+ # @mcp.tool() is a placeholder for the actual decorator.
6
+ # The user prompt says "NO NEED to import mcp".
7
+ def tool(*args, **kwargs):
8
+ def decorator(f):
9
+ return f
10
+ return decorator
11
+
12
+ mcp = type("mcp", (), {"tool": tool})
13
+
14
+
15
+ @mcp.tool()
16
+ def bioawk(
17
+ program: Optional[str] = None,
18
+ program_file: Optional[Path] = None,
19
+ input_files: Optional[List[Path]] = None,
20
+ format: Optional[str] = None,
21
+ tab_separator: bool = False,
22
+ retain_header: bool = False,
23
+ variables: Optional[List[str]] = None,
24
+ field_separator: Optional[str] = None,
25
+ ) -> Dict[str, Any]:
26
+ """
27
+ Executes the bioawk tool, a variant of awk for processing biological data formats.
28
+
29
+ bioawk extends awk by understanding common bioinformatics formats, making it easy
30
+ to parse and manipulate files like FASTA, FASTQ, SAM, VCF, etc.
31
+
32
+ Args:
33
+ program: The awk program script to execute, provided as a string.
34
+ program_file: A file containing the awk program script.
35
+ Exactly one of 'program' or 'program_file' must be provided.
36
+ input_files: A list of input files to process. If not provided,
37
+ bioawk will read from standard input.
38
+ format: The format of the input data. Supported formats include:
39
+ fasta, fastq, sam, vcf, bed, gff, gtf, psl, blast, hmmer, cigar.
40
+ If not specified, bioawk defaults to 'fasta'.
41
+ tab_separator: Use tab as the input and output field separator (-t).
42
+ retain_header: Retain the header in the output (-H).
43
+ variables: A list of variable assignments in 'var=value' format (-v).
44
+ field_separator: The input field separator, equivalent to awk's -F option.
45
+
46
+ Returns:
47
+ A dictionary containing the executed command, stdout, and stderr.
48
+ """
49
+ # 1. Input Validation
50
+ if not (program or program_file) or (program and program_file):
51
+ raise ValueError("Exactly one of 'program' or 'program_file' must be provided.")
52
+
53
+ if program_file and not program_file.is_file():
54
+ raise FileNotFoundError(f"Program file not found: {program_file}")
55
+
56
+ if input_files:
57
+ for file_path in input_files:
58
+ if not file_path.is_file():
59
+ raise FileNotFoundError(f"Input file not found: {file_path}")
60
+
61
+ VALID_FORMATS = {
62
+ "fasta", "fastq", "sam", "vcf", "bed", "gff",
63
+ "gtf", "psl", "blast", "hmmer", "cigar"
64
+ }
65
+ if format and format.lower() not in VALID_FORMATS:
66
+ raise ValueError(f"Invalid format '{format}'. Must be one of {VALID_FORMATS}")
67
+
68
+ if variables:
69
+ for var in variables:
70
+ if "=" not in var:
71
+ raise ValueError(f"Invalid variable assignment '{var}'. Must be in 'var=value' format.")
72
+
73
+ # 2. Command Construction
74
+ cmd = ["bioawk"]
75
+
76
+ if format:
77
+ cmd.extend(["-c", format])
78
+ if tab_separator:
79
+ cmd.append("-t")
80
+ if retain_header:
81
+ cmd.append("-H")
82
+ if field_separator:
83
+ cmd.extend(["-F", field_separator])
84
+ if variables:
85
+ for var in variables:
86
+ cmd.extend(["-v", var])
87
+
88
+ if program_file:
89
+ cmd.extend(["-f", str(program_file)])
90
+ elif program:
91
+ cmd.append(program)
92
+
93
+ if input_files:
94
+ cmd.extend([str(p) for p in input_files])
95
+
96
+ # 3. Subprocess Execution
97
+ command_executed = " ".join(cmd)
98
+ try:
99
+ result = subprocess.run(
100
+ cmd,
101
+ capture_output=True,
102
+ text=True,
103
+ check=True,
104
+ )
105
+ return {
106
+ "command_executed": command_executed,
107
+ "stdout": result.stdout,
108
+ "stderr": result.stderr,
109
+ }
110
+ except FileNotFoundError:
111
+ # This error is raised if the 'bioawk' command itself is not found.
112
+ raise RuntimeError("bioawk command not found. Please ensure it is installed and in your PATH.")
113
+ except subprocess.CalledProcessError as e:
114
+ # This error is raised if bioawk returns a non-zero exit code.
115
+ return {
116
+ "command_executed": command_executed,
117
+ "stdout": e.stdout,
118
+ "stderr": e.stderr,
119
+ "error": f"bioawk failed with exit code {e.returncode}"
120
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/bioawk_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_bioawk/app/bioawk_server.py')
11
+ SERVER_NAME = 'biosci_bioawk'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioawk:
5
+ build: .
6
+ image: mcp-bioawk:latest
7
+ container_name: mcp-bioawk
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioawk
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioawk
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/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-ancombc via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-ancombc -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-ancombc_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-ancombc_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-ancombc_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/bioconductor-ancombc_server.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import textwrap
4
+ from pathlib import Path
5
+ from typing import Dict, List, Optional
6
+
7
+ # @mcp.tool() is a placeholder for the actual decorator.
8
+ # The function is written to be compatible with the MCP server environment.
9
+ def mcp_tool_placeholder(*args, **kwargs):
10
+ def decorator(func):
11
+ return func
12
+ return decorator
13
+
14
+ @mcp_tool_placeholder()
15
+ def ancombc2(
16
+ otu_table: Path,
17
+ sample_metadata: Path,
18
+ formula: str,
19
+ output_dir: Path,
20
+ taxonomy_table: Optional[Path] = None,
21
+ p_adj_method: str = "holm",
22
+ zero_cut: float = 0.9,
23
+ lib_cut: int = 1000,
24
+ struc_zero: bool = False,
25
+ neg_lb: bool = False,
26
+ tol: float = 1e-05,
27
+ max_iter: int = 100,
28
+ conserve: bool = False,
29
+ alpha: float = 0.05,
30
+ global_test: bool = False,
31
+ group: Optional[str] = None,
32
+ mdfdr_dist: str = "normal",
33
+ n_cl: int = 1,
34
+ verbose: bool = False,
35
+ output_prefix: str = "ancombc2_results",
36
+ ) -> Dict:
37
+ """
38
+ Performs differential abundance analysis on microbiome data using ANCOM-BC2.
39
+
40
+ ANCOM-BC2 (Analysis of Compositions of Microbiomes with Bias Correction 2) is a
41
+ method for identifying differentially abundant taxa between groups while correcting
42
+ for sample-specific and taxon-specific biases.
43
+
44
+ Args:
45
+ otu_table: Path to the feature/OTU count table (CSV format). Rows should be taxa
46
+ and columns should be samples. The first column should be taxon IDs.
47
+ sample_metadata: Path to the sample metadata file (CSV format). Rows should be
48
+ samples and columns should be metadata variables. The first
49
+ column should be sample IDs.
50
+ formula: An R-style formula string specifying the model, e.g., "age + sex + diagnosis".
51
+ Variables must correspond to columns in the sample_metadata file.
52
+ output_dir: Path to the directory where output files will be saved.
53
+ taxonomy_table: Optional path to the taxonomy table (CSV format). Rows should be
54
+ taxa, columns should be taxonomic ranks. The first column must
55
+ be taxon IDs matching the OTU table.
56
+ p_adj_method: Method for p-value adjustment.
57
+ Options: "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none".
58
+ zero_cut: A numerical value between 0 and 1. Taxa with a proportion of zeros greater
59
+ than this value will be excluded from the analysis.
60
+ lib_cut: A numerical value. Samples with library sizes less than this value will be
61
+ excluded from the analysis.
62
+ struc_zero: Whether to detect structural zeros.
63
+ neg_lb: Whether to use the negative binomial distribution for modeling sampling fractions.
64
+ If FALSE, a log-linear model is used.
65
+ tol: Convergence tolerance for the optimization algorithm.
66
+ max_iter: Maximum number of iterations for the optimization algorithm.
67
+ conserve: Whether to use a conservative variance estimate for the test statistic.
68
+ alpha: Significance level for identifying differentially abundant taxa.
69
+ global_test: Whether to perform a global test for the specified group variable.
70
+ group: The name of the group variable for the global test. Required if `global_test` is True.
71
+ Must be a column name in the sample_metadata file.
72
+ mdfdr_dist: The assumed distribution of the E-values in m-DFDR.
73
+ Options: "normal", "t".
74
+ n_cl: Number of CPU cores to use for parallel computation.
75
+ verbose: Whether to display progress messages during execution.
76
+ output_prefix: Prefix for the output result files.
77
+
78
+ Returns:
79
+ A dictionary containing the executed command, stdout, stderr, and a list of
80
+ paths to the generated output files.
81
+ """
82
+ # --- 1. Input Validation ---
83
+ if not otu_table.is_file():
84
+ raise FileNotFoundError(f"OTU table not found at: {otu_table}")
85
+ if not sample_metadata.is_file():
86
+ raise FileNotFoundError(f"Sample metadata not found at: {sample_metadata}")
87
+ if taxonomy_table and not taxonomy_table.is_file():
88
+ raise FileNotFoundError(f"Taxonomy table not found at: {taxonomy_table}")
89
+
90
+ p_adj_methods = ["holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none"]
91
+ if p_adj_method not in p_adj_methods:
92
+ raise ValueError(f"Invalid p_adj_method '{p_adj_method}'. Must be one of {p_adj_methods}")
93
+
94
+ mdfdr_dists = ["normal", "t"]
95
+ if mdfdr_dist not in mdfdr_dists:
96
+ raise ValueError(f"Invalid mdfdr_dist '{mdfdr_dist}'. Must be one of {mdfdr_dists}")
97
+
98
+ if not 0 <= zero_cut <= 1:
99
+ raise ValueError("zero_cut must be between 0 and 1.")
100
+ if not 0 < alpha < 1:
101
+ raise ValueError("alpha must be between 0 and 1.")
102
+ if lib_cut < 0:
103
+ raise ValueError("lib_cut must be a non-negative integer.")
104
+ if max_iter <= 0:
105
+ raise ValueError("max_iter must be a positive integer.")
106
+ if n_cl <= 0:
107
+ raise ValueError("n_cl must be a positive integer.")
108
+ if tol <= 0:
109
+ raise ValueError("tol must be a positive float.")
110
+
111
+ if global_test and not group:
112
+ raise ValueError("The 'group' parameter is required when 'global_test' is True.")
113
+
114
+ # --- 2. Prepare for Execution ---
115
+ output_dir.mkdir(parents=True, exist_ok=True)
116
+
117
+ output_res_path = output_dir / f"{output_prefix}_DA.csv"
118
+ output_files = [str(output_res_path)]
119
+
120
+ output_global_res_path = output_dir / f"{output_prefix}_global.csv"
121
+ if global_test:
122
+ output_files.append(str(output_global_res_path))
123
+
124
+ # Convert Python types to R-compatible strings
125
+ r_tax_file = f"'{taxonomy_table.resolve()}'" if taxonomy_table else "NULL"
126
+ r_group = f"'{group}'" if group else "NULL"
127
+ r_struc_zero = "TRUE" if struc_zero else "FALSE"
128
+ r_neg_lb = "TRUE" if neg_lb else "FALSE"
129
+ r_conserve = "TRUE" if conserve else "FALSE"
130
+ r_global_test = "TRUE" if global_test else "FALSE"
131
+ r_verbose = "TRUE" if verbose else "FALSE"
132
+
133
+ # --- 3. Generate R Script ---
134
+ r_script_content = textwrap.dedent(f"""
135
+ # Load required libraries
136
+ library(ANCOMBC)
137
+ library(phyloseq)
138
+
139
+ # --- Parameters ---
140
+ otu_file <- '{otu_table.resolve()}'
141
+ meta_file <- '{sample_metadata.resolve()}'
142
+ tax_file <- {r_tax_file}
143
+ output_res_path <- '{output_res_path.resolve()}'
144
+ output_global_res_path <- '{output_global_res_path.resolve()}'
145
+
146
+ # --- Load and prepare data ---
147
+ # read.csv with row.names = 1 assumes first column is the index
148
+ otu_mat <- as.matrix(read.csv(otu_file, row.names = 1, check.names = FALSE))
149
+ meta_data <- read.csv(meta_file, row.names = 1, check.names = FALSE)
150
+
151
+ # Ensure sample names match and are in the same order
152
+ samples_in_common <- intersect(rownames(meta_data), colnames(otu_mat))
153
+ if (length(samples_in_common) == 0) {{
154
+ stop("No common sample IDs found between OTU table and metadata.")
155
+ }}
156
+ otu_mat <- otu_mat[, samples_in_common, drop = FALSE]
157
+ meta_data <- meta_data[samples_in_common, , drop = FALSE]
158
+
159
+ OTU <- otu_table(otu_mat, taxa_are_rows = TRUE)
160
+ META <- sample_data(meta_data)
161
+
162
+ # Load taxonomy if provided
163
+ if (!is.null(tax_file)) {{
164
+ tax_mat <- as.matrix(read.csv(tax_file, row.names = 1, check.names = FALSE))
165
+ # Ensure taxon names match
166
+ taxa_in_common <- intersect(rownames(tax_mat), rownames(otu_mat))
167
+ if (length(taxa_in_common) == 0) {{
168
+ warning("No common taxon IDs found between OTU table and taxonomy table. Proceeding without taxonomy.")
169
+ pseq <- phyloseq(OTU, META)
170
+ }} else {{
171
+ tax_mat_filtered <- tax_mat[taxa_in_common, , drop = FALSE]
172
+ TAX <- tax_table(tax_mat_filtered)
173
+ pseq <- phyloseq(OTU, META, TAX)
174
+ }}
175
+ }} else {{
176
+ pseq <- phyloseq(OTU, META)
177
+ }}
178
+
179
+ # --- Run ANCOM-BC2 ---
180
+ output <- ancombc2(
181
+ data = pseq,
182
+ formula = "{formula}",
183
+ p_adj_method = "{p_adj_method}",
184
+ zero_cut = {zero_cut},
185
+ lib_cut = {lib_cut},
186
+ struc_zero = {r_struc_zero},
187
+ neg_lb = {r_neg_lb},
188
+ tol = {tol},
189
+ max_iter = {max_iter},
190
+ conserve = {r_conserve},
191
+ alpha = {alpha},
192
+ global = {r_global_test},
193
+ group = {r_group},
194
+ mdfdr_dist = "{mdfdr_dist}",
195
+ n_cl = {n_cl},
196
+ verbose = {r_verbose}
197
+ )
198
+
199
+ # --- Save results ---
200
+ # Save differential abundance results
201
+ res_df <- data.frame(output$res)
202
+ write.csv(res_df, file = output_res_path, row.names = TRUE)
203
+
204
+ # Save global test results if applicable
205
+ if ({r_global_test} && !is.null(output$res_global)) {{
206
+ res_global_df <- data.frame(output$res_global)
207
+ write.csv(res_global_df, file = output_global_res_path, row.names = TRUE)
208
+ }}
209
+
210
+ print("ANCOM-BC2 analysis completed successfully.")
211
+ """)
212
+
213
+ # --- 4. Execute ---
214
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
215
+ r_script_file.write(r_script_content)
216
+ r_script_path = r_script_file.name
217
+
218
+ command = ["Rscript", r_script_path]
219
+ command_executed = " ".join(command)
220
+
221
+ try:
222
+ result = subprocess.run(
223
+ command,
224
+ capture_output=True,
225
+ text=True,
226
+ check=True,
227
+ )
228
+ stdout = result.stdout
229
+ stderr = result.stderr
230
+ except subprocess.CalledProcessError as e:
231
+ # Clean up the temporary script file on error
232
+ Path(r_script_path).unlink()
233
+ return {
234
+ "command_executed": command_executed,
235
+ "stdout": e.stdout,
236
+ "stderr": e.stderr,
237
+ "return_code": e.returncode,
238
+ "output_files": []
239
+ }
240
+ finally:
241
+ # Ensure temporary script is always cleaned up
242
+ if Path(r_script_path).exists():
243
+ Path(r_script_path).unlink()
244
+
245
+ # --- 5. Return results ---
246
+ return {
247
+ "command_executed": command_executed,
248
+ "stdout": stdout,
249
+ "stderr": stderr,
250
+ "output_files": output_files
251
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/bioconductor-ancombc_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-ancombc/app/bioconductor-ancombc_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_ancombc'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-ancombc:
5
+ build: .
6
+ image: mcp-bioconductor-ancombc:latest
7
+ container_name: mcp-bioconductor-ancombc
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-ancombc
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/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-ancombc
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/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-bsgenome via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-bsgenome -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-bsgenome_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-bsgenome_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-bsgenome_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-bsgenome:
5
+ build: .
6
+ image: mcp-bioconductor-bsgenome:latest
7
+ container_name: mcp-bioconductor-bsgenome
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-bsgenome
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/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-bsgenome
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/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-concordexr via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-concordexr -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-concordexr_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-concordexr_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-concordexr_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_server.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # Mock the decorator for standalone execution
7
+ class mcp:
8
+ @staticmethod
9
+ def tool(func):
10
+ return func
11
+
12
+ @mcp.tool
13
+ def compute_concordex(
14
+ data_file: Path,
15
+ ranks_file: Path,
16
+ indices_file: Path,
17
+ output_dir: Path,
18
+ data_type: str = "counts",
19
+ abundance_min: float = 1.0,
20
+ abundance_max: Optional[float] = None,
21
+ sample_min: float = 0.0,
22
+ sample_max: Optional[float] = None,
23
+ filter_perc: float = 0.0,
24
+ filter_count: int = 0,
25
+ plot: bool = True,
26
+ plot_format: str = "png",
27
+ plot_width: int = 10,
28
+ plot_height: int = 10,
29
+ export: bool = True,
30
+ export_rds: bool = True,
31
+ ):
32
+ """
33
+ Computes the Concordance Index (Concordex) using the concordexR package.
34
+
35
+ This tool wraps the `computeConcordex` function. It takes expression/count data,
36
+ pre-computed ranks, and sample indices to calculate concordex scores. It can
37
+ filter data, generate plots, and export results to files.
38
+
39
+ Args:
40
+ data_file: Path to the input data file (e.g., counts matrix). Assumed to be a tab-separated file with a header and row names in the first column.
41
+ ranks_file: Path to the ranks file. Assumed to be a tab-separated file with a header and row names in the first column.
42
+ indices_file: Path to the indices file. Assumed to be a single-column, tab-separated file without a header.
43
+ output_dir: Path to the directory where output files will be saved.
44
+ data_type: The type of data in `data_file`. Must be either 'counts' or 'ranks'.
45
+ abundance_min: The minimum abundance of a feature to be included in the analysis.
46
+ abundance_max: The maximum abundance of a feature. If None, no upper limit is applied.
47
+ sample_min: The minimum value of a sample to be included.
48
+ sample_max: The maximum value of a sample. If None, no upper limit is applied.
49
+ filter_perc: The percentage of samples a feature must be present in to be retained. Value must be between 0.0 and 1.0.
50
+ filter_count: The minimum number of counts a feature must have across all samples to be retained.
51
+ plot: If True, generate concordex plots.
52
+ plot_format: The format for the output plots (e.g., 'png', 'pdf', 'svg').
53
+ plot_width: The width of the output plots in inches.
54
+ plot_height: The height of the output plots in inches.
55
+ export: If True, export the concordex results to text files.
56
+ export_rds: If True, save the final concordex R object to an .rds file for later use (e.g., with plot_concordex).
57
+ """
58
+ # Input validation
59
+ if not data_file.is_file():
60
+ raise FileNotFoundError(f"Input data file not found: {data_file}")
61
+ if not ranks_file.is_file():
62
+ raise FileNotFoundError(f"Ranks file not found: {ranks_file}")
63
+ if not indices_file.is_file():
64
+ raise FileNotFoundError(f"Indices file not found: {indices_file}")
65
+
66
+ if data_type not in ["counts", "ranks"]:
67
+ raise ValueError("data_type must be either 'counts' or 'ranks'.")
68
+ if not (0.0 <= filter_perc <= 1.0):
69
+ raise ValueError("filter_perc must be between 0.0 and 1.0.")
70
+ if filter_count < 0:
71
+ raise ValueError("filter_count must be a non-negative integer.")
72
+ if plot_format not in ["png", "pdf", "svg", "jpeg", "tiff"]:
73
+ raise ValueError(f"Unsupported plot format: {plot_format}")
74
+
75
+ output_dir.mkdir(parents=True, exist_ok=True)
76
+
77
+ # Build the R script
78
+ r_script_lines = [
79
+ "library(concordexR)",
80
+ f'data <- read.table("{data_file.resolve()}", header=TRUE, sep="\\t", row.names=1, check.names=FALSE)',
81
+ f'ranks <- read.table("{ranks_file.resolve()}", header=TRUE, sep="\\t", row.names=1, check.names=FALSE)',
82
+ f'indices_df <- read.table("{indices_file.resolve()}", header=FALSE, sep="\\t")',
83
+ "indices <- as.list(indices_df$V1)",
84
+ "concordex_result <- computeConcordex(",
85
+ " data = data,",
86
+ " ranks = ranks,",
87
+ " indices = indices,",
88
+ f' type = "{data_type}",',
89
+ f" abundance.min = {abundance_min},",
90
+ f" abundance.max = {abundance_max if abundance_max is not None else 'Inf'},",
91
+ f" sample.min = {sample_min},",
92
+ f" sample.max = {sample_max if sample_max is not None else 'Inf'},",
93
+ f" filter.perc = {filter_perc},",
94
+ f" filter.count = {filter_count},",
95
+ f' output.dir = "{output_dir.resolve()}",',
96
+ f" plot = {'TRUE' if plot else 'FALSE'},",
97
+ f' plot.format = "{plot_format}",',
98
+ f" plot.width = {plot_width},",
99
+ f" plot.height = {plot_height},",
100
+ f" export = {'TRUE' if export else 'FALSE'}",
101
+ ")",
102
+ ]
103
+
104
+ if export_rds:
105
+ rds_path = output_dir.resolve() / "concordex_object.rds"
106
+ r_script_lines.append(f'saveRDS(concordex_result, file = "{rds_path}")')
107
+
108
+ r_script = "\n".join(r_script_lines)
109
+
110
+ # Use a temporary file for the R script
111
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as f:
112
+ f.write(r_script)
113
+ r_script_path = Path(f.name)
114
+
115
+ cmd = ["Rscript", str(r_script_path)]
116
+ command_executed = " ".join(cmd)
117
+
118
+ try:
119
+ result = subprocess.run(
120
+ cmd,
121
+ capture_output=True,
122
+ text=True,
123
+ check=True,
124
+ )
125
+
126
+ output_files = [str(p) for p in output_dir.glob("**/*") if p.is_file()]
127
+
128
+ return {
129
+ "command_executed": command_executed,
130
+ "stdout": result.stdout,
131
+ "stderr": result.stderr,
132
+ "output_files": output_files,
133
+ }
134
+ except subprocess.CalledProcessError as e:
135
+ raise RuntimeError(
136
+ f"R script execution failed with return code {e.returncode}.\n"
137
+ f"Command: {command_executed}\n"
138
+ f"Stdout: {e.stdout}\n"
139
+ f"Stderr: {e.stderr}"
140
+ ) from e
141
+ finally:
142
+ r_script_path.unlink()
143
+
144
+ @mcp.tool
145
+ def plot_concordex(
146
+ concordex_object_file: Path,
147
+ output_dir: Path,
148
+ plot_format: str = "png",
149
+ plot_width: int = 10,
150
+ plot_height: int = 10,
151
+ ):
152
+ """
153
+ Generates plots from a pre-computed concordex object.
154
+
155
+ This tool wraps the `plotConcordex` function from the concordexR package.
156
+ It takes an .rds file containing a concordex object (generated by the
157
+ compute_concordex tool) and creates visualization plots.
158
+
159
+ Args:
160
+ concordex_object_file: Path to the .rds file containing the concordex object.
161
+ output_dir: Path to the directory where output plots will be saved.
162
+ plot_format: The format for the output plots (e.g., 'png', 'pdf', 'svg').
163
+ plot_width: The width of the output plots in inches.
164
+ plot_height: The height of the output plots in inches.
165
+ """
166
+ # Input validation
167
+ if not concordex_object_file.is_file():
168
+ raise FileNotFoundError(f"Concordex object file not found: {concordex_object_file}")
169
+ if concordex_object_file.suffix != ".rds":
170
+ raise ValueError("concordex_object_file must be an .rds file.")
171
+ if plot_format not in ["png", "pdf", "svg", "jpeg", "tiff"]:
172
+ raise ValueError(f"Unsupported plot format: {plot_format}")
173
+
174
+ output_dir.mkdir(parents=True, exist_ok=True)
175
+
176
+ # Build the R script
177
+ r_script_lines = [
178
+ "library(concordexR)",
179
+ f'concordex_obj <- readRDS("{concordex_object_file.resolve()}")',
180
+ "plotConcordex(",
181
+ " concordex.object = concordex_obj,",
182
+ f' output.dir = "{output_dir.resolve()}",',
183
+ f' plot.format = "{plot_format}",',
184
+ f" plot.width = {plot_width},",
185
+ f" plot.height = {plot_height}",
186
+ ")",
187
+ ]
188
+ r_script = "\n".join(r_script_lines)
189
+
190
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as f:
191
+ f.write(r_script)
192
+ r_script_path = Path(f.name)
193
+
194
+ cmd = ["Rscript", str(r_script_path)]
195
+ command_executed = " ".join(cmd)
196
+
197
+ try:
198
+ result = subprocess.run(
199
+ cmd,
200
+ capture_output=True,
201
+ text=True,
202
+ check=True,
203
+ )
204
+
205
+ output_files = [str(p) for p in output_dir.glob("**/*") if p.is_file()]
206
+
207
+ return {
208
+ "command_executed": command_executed,
209
+ "stdout": result.stdout,
210
+ "stderr": result.stderr,
211
+ "output_files": output_files,
212
+ }
213
+ except subprocess.CalledProcessError as e:
214
+ raise RuntimeError(
215
+ f"R script execution failed with return code {e.returncode}.\n"
216
+ f"Command: {command_executed}\n"
217
+ f"Stdout: {e.stdout}\n"
218
+ f"Stderr: {e.stderr}"
219
+ ) from e
220
+ finally:
221
+ r_script_path.unlink()
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-concordexr/app/bioconductor-concordexr_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_concordexr'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-concordexr:
5
+ build: .
6
+ image: mcp-bioconductor-concordexr:latest
7
+ container_name: mcp-bioconductor-concordexr
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-concordexr
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/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-concordexr
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/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-decipher via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-decipher -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-decipher_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-decipher_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-decipher_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-decipher/app/bioconductor-decipher_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_decipher'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-decipher:
5
+ build: .
6
+ image: mcp-bioconductor-decipher:latest
7
+ container_name: mcp-bioconductor-decipher
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-decipher
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/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-decipher
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/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-ensembldb via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-ensembldb -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-ensembldb_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-ensembldb_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-ensembldb_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/bioconductor-ensembldb_server.py ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import textwrap
4
+ from pathlib import Path
5
+ from typing import Optional, List, Dict, Any
6
+
7
+ # Assume mcp.tool is imported from a framework library
8
+ class mcp:
9
+ def tool(func):
10
+ return func
11
+
12
+ def _run_r_script(r_script_content: str, args: List[str]) -> Dict[str, Any]:
13
+ """
14
+ A helper function to execute an R script using Rscript, handling errors.
15
+
16
+ This function requires that R and the required Bioconductor packages
17
+ (e.g., ensembldb, AnnotationFilter) are installed in the execution environment.
18
+
19
+ Args:
20
+ r_script_content: A string containing the R code to execute.
21
+ args: A list of command-line arguments for the R script.
22
+
23
+ Returns:
24
+ A dictionary containing the executed command, stdout, and stderr.
25
+
26
+ Raises:
27
+ RuntimeError: If Rscript is not found in the system's PATH.
28
+ subprocess.CalledProcessError: If the R script execution fails.
29
+ """
30
+ try:
31
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as tmp_script:
32
+ script_path = Path(tmp_script.name)
33
+ tmp_script.write(r_script_content)
34
+
35
+ command = ["Rscript", str(script_path)] + args
36
+
37
+ process = subprocess.run(
38
+ command,
39
+ check=True,
40
+ capture_output=True,
41
+ text=True,
42
+ encoding='utf-8'
43
+ )
44
+ return {
45
+ "command_executed": " ".join(command),
46
+ "stdout": process.stdout,
47
+ "stderr": process.stderr
48
+ }
49
+ except FileNotFoundError:
50
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
51
+ except subprocess.CalledProcessError as e:
52
+ error_message = (
53
+ f"R script execution failed with exit code {e.returncode}.\n"
54
+ f"Command: {' '.join(e.cmd)}\n"
55
+ f"Stdout: {e.stdout}\n"
56
+ f"Stderr: {e.stderr}"
57
+ )
58
+ # Re-raise with a more informative stderr
59
+ raise subprocess.CalledProcessError(e.returncode, e.cmd, output=e.stdout, stderr=error_message)
60
+ finally:
61
+ if 'script_path' in locals() and script_path.exists():
62
+ script_path.unlink()
63
+
64
+ @mcp.tool
65
+ def create_ensdb_from_gtf(
66
+ gtf_file: Path,
67
+ output_db: Path,
68
+ organism: str,
69
+ genome_version: str,
70
+ ensembl_version: str,
71
+ ) -> Dict[str, Any]:
72
+ """
73
+ Creates an EnsemblDB SQLite database from a GTF file.
74
+
75
+ This tool wraps the `ensDbFromGtf` function from the R/Bioconductor
76
+ package 'ensembldb'. It requires R and the 'ensembldb' package to be
77
+ installed in the environment.
78
+
79
+ Args:
80
+ gtf_file: Path to the input GTF file.
81
+ output_db: Path for the output SQLite database file.
82
+ organism: Name of the organism (e.g., 'Homo_sapiens').
83
+ genome_version: Genome build version (e.g., 'GRCh38').
84
+ ensembl_version: Ensembl release version (e.g., '104').
85
+
86
+ Returns:
87
+ A dictionary containing the command executed, stdout, stderr,
88
+ and a list of output files.
89
+ """
90
+ # Input validation
91
+ if not gtf_file.is_file():
92
+ raise FileNotFoundError(f"Input GTF file not found: {gtf_file}")
93
+ if not output_db.parent.is_dir():
94
+ raise NotADirectoryError(f"Output directory does not exist: {output_db.parent}")
95
+ if not all([organism, genome_version, ensembl_version]):
96
+ raise ValueError("organism, genome_version, and ensembl_version must be non-empty strings.")
97
+
98
+ r_script = textwrap.dedent("""
99
+ library("ensembldb")
100
+ args <- commandArgs(trailingOnly = TRUE)
101
+ ensDbFromGtf(
102
+ gtf = args[1],
103
+ outfile = args[2],
104
+ organism = args[3],
105
+ genomeVersion = args[4],
106
+ version = args[5]
107
+ )
108
+ """)
109
+
110
+ args = [
111
+ str(gtf_file),
112
+ str(output_db),
113
+ organism,
114
+ genome_version,
115
+ ensembl_version
116
+ ]
117
+
118
+ result = _run_r_script(r_script, args)
119
+ result["output_files"] = [str(output_db)]
120
+ return result
121
+
122
+ @mcp.tool
123
+ def create_ensdb_from_gff(
124
+ gff_file: Path,
125
+ output_db: Path,
126
+ organism: str,
127
+ genome_version: str,
128
+ ensembl_version: str,
129
+ ) -> Dict[str, Any]:
130
+ """
131
+ Creates an EnsemblDB SQLite database from a GFF3 file.
132
+
133
+ This tool wraps the `ensDbFromGff` function from the R/Bioconductor
134
+ package 'ensembldb'. It requires R and the 'ensembldb' package to be
135
+ installed in the environment.
136
+
137
+ Args:
138
+ gff_file: Path to the input GFF3 file.
139
+ output_db: Path for the output SQLite database file.
140
+ organism: Name of the organism (e.g., 'Homo_sapiens').
141
+ genome_version: Genome build version (e.g., 'GRCh38').
142
+ ensembl_version: Ensembl release version (e.g., '104').
143
+
144
+ Returns:
145
+ A dictionary containing the command executed, stdout, stderr,
146
+ and a list of output files.
147
+ """
148
+ # Input validation
149
+ if not gff_file.is_file():
150
+ raise FileNotFoundError(f"Input GFF file not found: {gff_file}")
151
+ if not output_db.parent.is_dir():
152
+ raise NotADirectoryError(f"Output directory does not exist: {output_db.parent}")
153
+ if not all([organism, genome_version, ensembl_version]):
154
+ raise ValueError("organism, genome_version, and ensembl_version must be non-empty strings.")
155
+
156
+ r_script = textwrap.dedent("""
157
+ library("ensembldb")
158
+ args <- commandArgs(trailingOnly = TRUE)
159
+ ensDbFromGff(
160
+ gff = args[1],
161
+ outfile = args[2],
162
+ organism = args[3],
163
+ genomeVersion = args[4],
164
+ version = args[5]
165
+ )
166
+ """)
167
+
168
+ args = [
169
+ str(gff_file),
170
+ str(output_db),
171
+ organism,
172
+ genome_version,
173
+ ensembl_version
174
+ ]
175
+
176
+ result = _run_r_script(r_script, args)
177
+ result["output_files"] = [str(output_db)]
178
+ return result
179
+
180
+ def _build_query_script(query_function: str, extra_r_code: str = "") -> str:
181
+ """Helper to generate the R script for various query types."""
182
+ return textwrap.dedent(f"""
183
+ library("ensembldb")
184
+ library("AnnotationFilter")
185
+
186
+ args <- commandArgs(trailingOnly = TRUE)
187
+
188
+ parse_args <- function(args) {{
189
+ params <- list()
190
+ for (arg in args) {{
191
+ if (startsWith(arg, "--")) {{
192
+ parts <- strsplit(substring(arg, 3), "=", fixed = TRUE)[[1]]
193
+ key <- parts[1]
194
+ value <- if (length(parts) > 1) parts[2] else TRUE
195
+ params[[key]] <- value
196
+ }}
197
+ }}
198
+ return(params)
199
+ }}
200
+
201
+ params <- parse_args(args)
202
+
203
+ if (is.null(params$db) || is.null(params$outfile)) {{
204
+ stop("Both --db and --outfile arguments are required.")
205
+ }}
206
+
207
+ edb <- EnsDb(params$db)
208
+
209
+ filter_list <- list()
210
+ if (!is.null(params$filter_gene_id)) {{
211
+ filter_list <- c(filter_list, GeneIdFilter(params$filter_gene_id))
212
+ }}
213
+ if (!is.null(params$filter_gene_biotype)) {{
214
+ filter_list <- c(filter_list, GeneBiotypeFilter(params$filter_gene_biotype))
215
+ }}
216
+ if (!is.null(params$filter_seq_name)) {{
217
+ filter_list <- c(filter_list, SeqNameFilter(params$filter_seq_name))
218
+ }}
219
+ if (!is.null(params$filter_tx_id)) {{
220
+ filter_list <- c(filter_list, TxIdFilter(params$filter_tx_id))
221
+ }}
222
+ if (!is.null(params$filter_tx_biotype)) {{
223
+ filter_list <- c(filter_list, TxBiotypeFilter(params$filter_tx_biotype))
224
+ }}
225
+ if (!is.null(params$filter_exon_id)) {{
226
+ filter_list <- c(filter_list, ExonIdFilter(params$filter_exon_id))
227
+ }}
228
+
229
+ final_filter <- NULL
230
+ if (length(filter_list) > 0) {{
231
+ final_filter <- AnnotationFilterList(filter_list, logicOp = "&")
232
+ }}
233
+
234
+ {extra_r_code}
235
+
236
+ results <- {query_function}(edb, filter = final_filter)
237
+
238
+ write.table(as.data.frame(results), file = params$outfile, sep = "\t", row.names = FALSE, quote = FALSE)
239
+
240
+ cat("Successfully wrote query results to:", params$outfile, "\n")
241
+ """)
242
+
243
+ @mcp.tool
244
+ def query_genes(
245
+ db_file: Path,
246
+ output_tsv: Path,
247
+ filter_gene_id: Optional[str] = None,
248
+ filter_gene_biotype: Optional[str] = None,
249
+ filter_seq_name: Optional[str] = None,
250
+ ) -> Dict[str, Any]:
251
+ """
252
+ Queries gene information from an EnsemblDB SQLite database.
253
+
254
+ This tool wraps the `genes` function from 'ensembldb'. It requires R,
255
+ 'ensembldb', and 'AnnotationFilter' packages.
256
+
257
+ Args:
258
+ db_file: Path to the input EnsemblDB SQLite file.
259
+ output_tsv: Path for the output TSV file.
260
+ filter_gene_id: Optional filter by Ensembl gene ID.
261
+ filter_gene_biotype: Optional filter by gene biotype (e.g., 'protein_coding').
262
+ filter_seq_name: Optional filter by sequence/chromosome name (e.g., 'X').
263
+
264
+ Returns:
265
+ A dictionary containing the command executed, stdout, stderr,
266
+ and a list of output files.
267
+ """
268
+ if not db_file.is_file():
269
+ raise FileNotFoundError(f"Input database file not found: {db_file}")
270
+ if not output_tsv.parent.is_dir():
271
+ raise NotADirectoryError(f"Output directory does not exist: {output_tsv.parent}")
272
+
273
+ r_script = _build_query_script("genes")
274
+
275
+ args = [f"--db={db_file}", f"--outfile={output_tsv}"]
276
+ if filter_gene_id:
277
+ args.append(f"--filter_gene_id={filter_gene_id}")
278
+ if filter_gene_biotype:
279
+ args.append(f"--filter_gene_biotype={filter_gene_biotype}")
280
+ if filter_seq_name:
281
+ args.append(f"--filter_seq_name={filter_seq_name}")
282
+
283
+ result = _run_r_script(r_script, args)
284
+ result["output_files"] = [str(output_tsv)]
285
+ return result
286
+
287
+ @mcp.tool
288
+ def query_transcripts(
289
+ db_file: Path,
290
+ output_tsv: Path,
291
+ filter_gene_id: Optional[str] = None,
292
+ filter_tx_id: Optional[str] = None,
293
+ filter_tx_biotype: Optional[str] = None,
294
+ ) -> Dict[str, Any]:
295
+ """
296
+ Queries transcript information from an EnsemblDB SQLite database.
297
+
298
+ This tool wraps the `transcripts` function from 'ensembldb'. It requires R,
299
+ 'ensembldb', and 'AnnotationFilter' packages.
300
+
301
+ Args:
302
+ db_file: Path to the input EnsemblDB SQLite file.
303
+ output_tsv: Path for the output TSV file.
304
+ filter_gene_id: Optional filter by Ensembl gene ID.
305
+ filter_tx_id: Optional filter by Ensembl transcript ID.
306
+ filter_tx_biotype: Optional filter by transcript biotype (e.g., 'protein_coding').
307
+
308
+ Returns:
309
+ A dictionary containing the command executed, stdout, stderr,
310
+ and a list of output files.
311
+ """
312
+ if not db_file.is_file():
313
+ raise FileNotFoundError(f"Input database file not found: {db_file}")
314
+ if not output_tsv.parent.is_dir():
315
+ raise NotADirectoryError(f"Output directory does not exist: {output_tsv.parent}")
316
+
317
+ r_script = _build_query_script("transcripts")
318
+
319
+ args = [f"--db={db_file}", f"--outfile={output_tsv}"]
320
+ if filter_gene_id:
321
+ args.append(f"--filter_gene_id={filter_gene_id}")
322
+ if filter_tx_id:
323
+ args.append(f"--filter_tx_id={filter_tx_id}")
324
+ if filter_tx_biotype:
325
+ args.append(f"--filter_tx_biotype={filter_tx_biotype}")
326
+
327
+ result = _run_r_script(r_script, args)
328
+ result["output_files"] = [str(output_tsv)]
329
+ return result
330
+
331
+ @mcp.tool
332
+ def query_exons(
333
+ db_file: Path,
334
+ output_tsv: Path,
335
+ filter_gene_id: Optional[str] = None,
336
+ filter_tx_id: Optional[str] = None,
337
+ filter_exon_id: Optional[str] = None,
338
+ ) -> Dict[str, Any]:
339
+ """
340
+ Queries exon information from an EnsemblDB SQLite database.
341
+
342
+ This tool wraps the `exons` function from 'ensembldb'. It requires R,
343
+ 'ensembldb', and 'AnnotationFilter' packages.
344
+
345
+ Args:
346
+ db_file: Path to the input EnsemblDB SQLite file.
347
+ output_tsv: Path for the output TSV file.
348
+ filter_gene_id: Optional filter by Ensembl gene ID.
349
+ filter_tx_id: Optional filter by Ensembl transcript ID.
350
+ filter_exon_id: Optional filter by Ensembl exon ID.
351
+
352
+ Returns:
353
+ A dictionary containing the command executed, stdout, stderr,
354
+ and a list of output files.
355
+ """
356
+ if not db_file.is_file():
357
+ raise FileNotFoundError(f"Input database file not found: {db_file}")
358
+ if not output_tsv.parent.is_dir():
359
+ raise NotADirectoryError(f"Output directory does not exist: {output_tsv.parent}")
360
+
361
+ r_script = _build_query_script("exons")
362
+
363
+ args = [f"--db={db_file}", f"--outfile={output_tsv}"]
364
+ if filter_gene_id:
365
+ args.append(f"--filter_gene_id={filter_gene_id}")
366
+ if filter_tx_id:
367
+ args.append(f"--filter_tx_id={filter_tx_id}")
368
+ if filter_exon_id:
369
+ args.append(f"--filter_exon_id={filter_exon_id}")
370
+
371
+ result = _run_r_script(r_script, args)
372
+ result["output_files"] = [str(output_tsv)]
373
+ return result
374
+
375
+ @mcp.tool
376
+ def query_promoters(
377
+ db_file: Path,
378
+ output_tsv: Path,
379
+ upstream: int = 2000,
380
+ downstream: int = 200,
381
+ filter_gene_id: Optional[str] = None,
382
+ filter_tx_id: Optional[str] = None,
383
+ ) -> Dict[str, Any]:
384
+ """
385
+ Extracts promoter regions for transcripts from an EnsemblDB.
386
+
387
+ This tool wraps the `promoters` function from 'ensembldb'. It requires R,
388
+ 'ensembldb', and 'AnnotationFilter' packages.
389
+
390
+ Args:
391
+ db_file: Path to the input EnsemblDB SQLite file.
392
+ output_tsv: Path for the output TSV file.
393
+ upstream: The number of bases upstream of the TSS to include.
394
+ downstream: The number of bases downstream of the TSS to include.
395
+ filter_gene_id: Optional filter by Ensembl gene ID.
396
+ filter_tx_id: Optional filter by Ensembl transcript ID.
397
+
398
+ Returns:
399
+ A dictionary containing the command executed, stdout, stderr,
400
+ and a list of output files.
401
+ """
402
+ if not db_file.is_file():
403
+ raise FileNotFoundError(f"Input database file not found: {db_file}")
404
+ if not output_tsv.parent.is_dir():
405
+ raise NotADirectoryError(f"Output directory does not exist: {output_tsv.parent}")
406
+ if upstream < 0 or downstream < 0:
407
+ raise ValueError("upstream and downstream values must be non-negative.")
408
+
409
+ # The 'promoters' function takes upstream/downstream as direct arguments,
410
+ # so we modify the R script generation slightly.
411
+ extra_r_code = textwrap.dedent(f"""
412
+ up <- as.numeric(params$upstream)
413
+ down <- as.numeric(params$downstream)
414
+ results <- promoters(edb, upstream = up, downstream = down, filter = final_filter)
415
+ """)
416
+
417
+ # We remove the final `results <- ...` line from the base template
418
+ base_script = _build_query_script("", extra_r_code)
419
+
420
+ args = [
421
+ f"--db={db_file}",
422
+ f"--outfile={output_tsv}",
423
+ f"--upstream={upstream}",
424
+ f"--downstream={downstream}"
425
+ ]
426
+ if filter_gene_id:
427
+ args.append(f"--filter_gene_id={filter_gene_id}")
428
+ if filter_tx_id:
429
+ args.append(f"--filter_tx_id={filter_tx_id}")
430
+
431
+ result = _run_r_script(base_script, args)
432
+ result["output_files"] = [str(output_tsv)]
433
+ return result
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/bioconductor-ensembldb_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-ensembldb/app/bioconductor-ensembldb_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_ensembldb'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-ensembldb:
5
+ build: .
6
+ image: mcp-bioconductor-ensembldb:latest
7
+ container_name: mcp-bioconductor-ensembldb
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-ensembldb
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/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-ensembldb
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/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-escher via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-escher -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-escher_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-escher_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-escher_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-escher/app/bioconductor-escher_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_escher'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/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-escher
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/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-genomeinfodbdata via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-genomeinfodbdata -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-genomeinfodbdata_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-genomeinfodbdata_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-genomeinfodbdata_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/bioconductor-genomeinfodbdata_server.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import List, Optional, Dict, Any
4
+
5
+ # MCP decorator placeholder
6
+ def tool(*args, **kwargs):
7
+ def decorator(f):
8
+ return f
9
+ return decorator
10
+
11
+ # In a real MCP environment, the 'mcp' object would be imported.
12
+ # For this standalone script, we define a placeholder.
13
+ class mcp:
14
+ tool = tool
15
+
16
+ @mcp.tool()
17
+ def run_rscript(
18
+ script_file: Optional[Path] = None,
19
+ expressions: Optional[List[str]] = None,
20
+ args: Optional[List[str]] = None,
21
+ verbose: bool = False,
22
+ default_packages: Optional[str] = None,
23
+ save: bool = False,
24
+ no_environ: bool = False,
25
+ no_site_file: bool = False,
26
+ no_init_file: bool = False,
27
+ restore: bool = False,
28
+ vanilla: bool = False,
29
+ ) -> Dict[str, Any]:
30
+ """
31
+ Executes an R script using the Rscript interpreter.
32
+
33
+ This tool is a general-purpose wrapper for the Rscript command-line utility.
34
+ The 'bioconductor-genomeinfodbdata' package is a data-only package and does not
35
+ have its own executable. It is intended to be used within R scripts, which can
36
+ be run with this tool.
37
+
38
+ Args:
39
+ script_file: Path to the R script file to be executed.
40
+ expressions: A list of R expressions to be executed. Use this or script_file, not both.
41
+ args: A list of arguments to be passed to the R script.
42
+ verbose: Print information on progress (--verbose).
43
+ default_packages: A comma-separated list of package names to be loaded by default (--default-packages).
44
+ save: Save the workspace at the end of the session (--save).
45
+ no_environ: Don't read the site and user environment files (--no-environ).
46
+ no_site_file: Don't read the site-wide Rprofile (--no-site-file).
47
+ no_init_file: Don't read the user R profile (--no-init-file).
48
+ restore: Restore previously saved objects at startup (--restore).
49
+ vanilla: Combine --no-save, --no-restore, --no-site-file, --no-init-file, and --no-environ (--vanilla).
50
+
51
+ Returns:
52
+ A dictionary containing the command executed, stdout, stderr, and a list of output files.
53
+ """
54
+ # Input validation
55
+ if script_file and expressions:
56
+ raise ValueError("Provide either 'script_file' or 'expressions', not both.")
57
+ if not script_file and not expressions:
58
+ raise ValueError("Either 'script_file' or 'expressions' must be provided.")
59
+
60
+ if script_file:
61
+ if not script_file.is_file():
62
+ raise FileNotFoundError(f"The script file does not exist: {script_file}")
63
+
64
+ cmd = ["Rscript"]
65
+
66
+ # Add R-specific options
67
+ if vanilla:
68
+ cmd.append("--vanilla")
69
+ else:
70
+ if save:
71
+ cmd.append("--save")
72
+ if no_environ:
73
+ cmd.append("--no-environ")
74
+ if no_site_file:
75
+ cmd.append("--no-site-file")
76
+ if no_init_file:
77
+ cmd.append("--no-init-file")
78
+ if restore:
79
+ cmd.append("--restore")
80
+
81
+ if verbose:
82
+ cmd.append("--verbose")
83
+ if default_packages:
84
+ cmd.extend(["--default-packages", default_packages])
85
+
86
+ # Add script file or expressions
87
+ if script_file:
88
+ cmd.append(str(script_file))
89
+ elif expressions:
90
+ for expr in expressions:
91
+ cmd.extend(["-e", expr])
92
+
93
+ # Add script arguments
94
+ if args:
95
+ cmd.extend(args)
96
+
97
+ command_executed = " ".join(cmd)
98
+
99
+ try:
100
+ result = subprocess.run(
101
+ cmd,
102
+ capture_output=True,
103
+ text=True,
104
+ check=True,
105
+ )
106
+ stdout = result.stdout
107
+ stderr = result.stderr
108
+ except FileNotFoundError:
109
+ return {
110
+ "command_executed": command_executed,
111
+ "stdout": "",
112
+ "stderr": "Error: Rscript not found. Is R installed and in your PATH?",
113
+ "output_files": []
114
+ }
115
+ except subprocess.CalledProcessError as e:
116
+ return {
117
+ "command_executed": command_executed,
118
+ "stdout": e.stdout,
119
+ "stderr": e.stderr,
120
+ "output_files": []
121
+ }
122
+
123
+ # Since the script's output files are unknown, we return an empty list.
124
+ # The user's R script is responsible for managing its own outputs.
125
+ return {
126
+ "command_executed": command_executed,
127
+ "stdout": stdout,
128
+ "stderr": stderr,
129
+ "output_files": [],
130
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/bioconductor-genomeinfodbdata_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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-genomeinfodbdata/app/bioconductor-genomeinfodbdata_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_genomeinfodbdata'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-genomeinfodbdata:
5
+ build: .
6
+ image: mcp-bioconductor-genomeinfodbdata:latest
7
+ container_name: mcp-bioconductor-genomeinfodbdata
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-genomeinfodbdata
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
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/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-genomeinfodbdata
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.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-go.db via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-go.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-go.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-go.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-go.db_server.py"]
40
+