czty commited on
Commit
29ae43d
·
verified ·
1 Parent(s): 7107b01

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/biomni/tool/__pycache__/cancer_biology.cpython-311.pyc +0 -0
  2. Biomni/biomni/tool/tool_description/__pycache__/biochemistry.cpython-311.pyc +0 -0
  3. Biomni/biomni/tool/tool_description/lab_automation.py +52 -0
  4. Biomni/biomni/tool/tool_description/support_tools.py +74 -0
  5. Biomni/mcp_generated/mcp_bioconductor-bsgenome/Dockerfile +40 -0
  6. Biomni/mcp_generated/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_shim_server.py +55 -0
  7. Biomni/mcp_generated/mcp_bioconductor-bsgenome/environment.yaml +10 -0
  8. Biomni/mcp_generated/mcp_bioconductor-bsgenome/requirements.txt +2 -0
  9. Biomni/mcp_generated/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py +208 -0
  10. Biomni/mcp_generated/mcp_bioconductor-cotan/docker-compose.yml +22 -0
  11. Biomni/mcp_generated/mcp_bioconductor-cotan/environment.yaml +10 -0
  12. Biomni/mcp_generated/mcp_bioconductor-cotan/requirements.txt +2 -0
  13. Biomni/mcp_generated/mcp_bioconductor-dnacopy/Dockerfile +40 -0
  14. Biomni/mcp_generated/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py +208 -0
  15. Biomni/mcp_generated/mcp_bioconductor-dnacopy/app/requirements.txt +1 -0
  16. Biomni/mcp_generated/mcp_bioconductor-dnacopy/docker-compose.yml +22 -0
  17. Biomni/mcp_generated/mcp_bioconductor-gsva/Dockerfile +40 -0
  18. Biomni/mcp_generated/mcp_bioconductor-gsva/app/bioconductor-gsva_server.py +215 -0
  19. Biomni/mcp_generated/mcp_bioconductor-gsva/app/bioconductor-gsva_shim_server.py +55 -0
  20. Biomni/mcp_generated/mcp_bioconductor-gsva/environment.yaml +10 -0
  21. Biomni/mcp_generated/mcp_bioconductor-gsva/requirements.txt +2 -0
  22. Biomni/mcp_generated/mcp_bioconductor-poem/Dockerfile +40 -0
  23. Biomni/mcp_generated/mcp_bioconductor-poem/app/bioconductor-poem_server.py +237 -0
  24. Biomni/mcp_generated/mcp_bioconductor-poem/app/requirements.txt +1 -0
  25. Biomni/mcp_generated/mcp_bioconductor-poem/docker-compose.yml +22 -0
  26. Biomni/mcp_generated/mcp_bioconductor-poem/environment.yaml +10 -0
  27. Biomni/mcp_generated/mcp_bioconductor-poem/requirements.txt +2 -0
  28. Biomni/mcp_generated/mcp_bioconductor-sctreeviz/Dockerfile +40 -0
  29. Biomni/mcp_generated/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py +230 -0
  30. Biomni/mcp_generated/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_shim_server.py +55 -0
  31. Biomni/mcp_generated/mcp_bioconductor-sctreeviz/docker-compose.yml +22 -0
  32. Biomni/mcp_generated/mcp_bioconductor-sctreeviz/requirements.txt +2 -0
  33. Biomni/mcp_generated/mcp_echidna/Dockerfile +40 -0
  34. Biomni/mcp_generated/mcp_echidna/environment.yaml +10 -0
  35. Biomni/mcp_generated/mcp_epic/app/epic_shim_server.py +55 -0
  36. Biomni/mcp_generated/mcp_epic/docker-compose.yml +22 -0
  37. Biomni/mcp_generated/mcp_gmap/Dockerfile +40 -0
  38. Biomni/mcp_generated/mcp_gmap/docker-compose.yml +22 -0
  39. Biomni/mcp_generated/mcp_mummer/app/mummer_server.py +145 -0
  40. Biomni/mcp_generated/mcp_perl-pod-escapes/environment.yaml +10 -0
  41. Biomni/mcp_generated/mcp_perl-xml-regexp/Dockerfile +40 -0
  42. Biomni/mcp_generated/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py +170 -0
  43. Biomni/mcp_generated/mcp_perl-xml-regexp/app/perl-xml-regexp_shim_server.py +55 -0
  44. Biomni/mcp_generated/mcp_perl-xml-regexp/app/requirements.txt +1 -0
  45. Biomni/mcp_generated/mcp_perl-xml-regexp/docker-compose.yml +22 -0
  46. Biomni/mcp_generated/mcp_searchgui/app/searchgui_shim_server.py +55 -0
  47. Biomni/mcp_generated/mcp_searchgui/environment.yaml +10 -0
  48. Biomni/mcp_generated/mcp_sortmerna/app/requirements.txt +1 -0
  49. Biomni/mcp_generated/mcp_sortmerna/app/sortmerna_server.py +216 -0
  50. Biomni/mcp_generated/mcp_sortmerna/requirements.txt +2 -0
Biomni/biomni/tool/__pycache__/cancer_biology.cpython-311.pyc ADDED
Binary file (64.8 kB). View file
 
Biomni/biomni/tool/tool_description/__pycache__/biochemistry.cpython-311.pyc ADDED
Binary file (4.76 kB). View file
 
Biomni/biomni/tool/tool_description/lab_automation.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description = [
2
+ {
3
+ "description": "Test a PyLabRobot script based on the script content.",
4
+ "name": "test_pylabrobot_script",
5
+ "optional_parameters": [
6
+ {
7
+ "default": False,
8
+ "description": "If True, enable tracking of the script execution",
9
+ "name": "enable_tracking",
10
+ "type": "bool",
11
+ },
12
+ {
13
+ "default": 60,
14
+ "description": "Timeout in seconds for the script execution",
15
+ "name": "timeout_seconds",
16
+ "type": "int",
17
+ },
18
+ {
19
+ "default": False,
20
+ "description": "If True, save the test results as a .json file",
21
+ "name": "save_test_report",
22
+ "type": "bool",
23
+ },
24
+ {
25
+ "default": None,
26
+ "description": "Directory to save the test results. If provided, the test results will be saved as a .json file in this directory",
27
+ "name": "test_report_dir",
28
+ "type": "str",
29
+ },
30
+ ],
31
+ "required_parameters": [
32
+ {
33
+ "default": None,
34
+ "description": "Script content to test",
35
+ "name": "script_input",
36
+ "type": "str",
37
+ }
38
+ ],
39
+ },
40
+ {
41
+ "description": "Get the documentation for the liquid handling section of the PyLabRobot tutorial.",
42
+ "name": "get_pylabrobot_documentation_liquid",
43
+ "optional_parameters": [],
44
+ "required_parameters": [],
45
+ },
46
+ {
47
+ "description": "Get the documentation for the material handling section of the PyLabRobot tutorial.",
48
+ "name": "get_pylabrobot_documentation_material",
49
+ "optional_parameters": [],
50
+ "required_parameters": [],
51
+ },
52
+ ]
Biomni/biomni/tool/tool_description/support_tools.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description = [
2
+ {
3
+ "description": "Executes the provided Python command in the notebook environment and returns the output.",
4
+ "name": "run_python_repl",
5
+ "optional_parameters": [],
6
+ "required_parameters": [
7
+ {
8
+ "default": None,
9
+ "description": "Python command to execute in the notebook environment",
10
+ "name": "command",
11
+ "type": "str",
12
+ }
13
+ ],
14
+ },
15
+ {
16
+ "description": "Read the source code of a function from any module path.",
17
+ "name": "read_function_source_code",
18
+ "optional_parameters": [],
19
+ "required_parameters": [
20
+ {
21
+ "default": None,
22
+ "description": "Fully qualified function name "
23
+ "(e.g., "
24
+ "'bioagentos.tool.support_tools.write_python_code')",
25
+ "name": "function_name",
26
+ "type": "str",
27
+ }
28
+ ],
29
+ },
30
+ {
31
+ "description": "Download data from Synapse using entity IDs. Requires SYNAPSE_AUTH_TOKEN environment variable for authentication. CRITICAL: Always specify entity_type parameter based on what you're downloading (file, dataset, folder, project). Check user hints like 'files' or search results to determine correct type. Multiple IDs only work with entity_type='file'. Recursive only works with entity_type='folder'.",
32
+ "name": "download_synapse_data",
33
+ "optional_parameters": [
34
+ {
35
+ "name": "download_location",
36
+ "type": "str",
37
+ "description": "Directory where files will be downloaded",
38
+ "default": ".",
39
+ },
40
+ {
41
+ "name": "follow_link",
42
+ "type": "bool",
43
+ "description": "Whether to follow links to download the linked entity",
44
+ "default": False,
45
+ },
46
+ {
47
+ "name": "recursive",
48
+ "type": "bool",
49
+ "description": "Whether to recursively download folders and their contents. ONLY valid with entity_type='folder'",
50
+ "default": False,
51
+ },
52
+ {
53
+ "name": "timeout",
54
+ "type": "int",
55
+ "description": "Timeout in seconds for each download operation",
56
+ "default": 300,
57
+ },
58
+ {
59
+ "name": "entity_type",
60
+ "type": "str",
61
+ "description": "Type of Synapse entity: 'file', 'dataset', 'folder', or 'project'. MUST match actual entity type! Check user hints (e.g., 'files' means entity_type='file') or search results ('node_type' field). Default 'dataset' should only be used for actual datasets.",
62
+ "default": "dataset",
63
+ },
64
+ ],
65
+ "required_parameters": [
66
+ {
67
+ "name": "entity_ids",
68
+ "type": "str|list[str]",
69
+ "description": "Synapse entity ID(s) to download. For files: single ID or list of IDs. For datasets/folders/projects: single ID only",
70
+ "default": None,
71
+ }
72
+ ],
73
+ },
74
+ ]
Biomni/mcp_generated/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
+
Biomni/mcp_generated/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_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-bsgenome/app/bioconductor-bsgenome_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_bsgenome'
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-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
+
Biomni/mcp_generated/mcp_bioconductor-bsgenome/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import textwrap
4
+ from pathlib import Path
5
+ from typing import List, Optional, Dict, Any
6
+
7
+ # This is a placeholder for the MCP decorator.
8
+ # In a real MCP environment, this would be provided by the MCP framework.
9
+ class mcp:
10
+ @staticmethod
11
+ def tool(func):
12
+ def wrapper(*args, **kwargs):
13
+ return func(*args, **kwargs)
14
+ return wrapper
15
+
16
+ @mcp.tool
17
+ def run_cotan_analysis(
18
+ raw_count_matrix: Path,
19
+ output_dir: Path,
20
+ min_reads_per_cell: int = 1000,
21
+ min_cells_per_gene: int = 3,
22
+ num_cores: int = 1,
23
+ run_gdi: bool = True,
24
+ run_clustering: bool = True,
25
+ num_clusters: int = 10,
26
+ save_cotan_object: bool = True,
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Performs a complete COTAN analysis for single-cell gene co-expression.
30
+
31
+ This tool takes a raw UMI count matrix and runs the standard COTAN workflow,
32
+ which includes data cleaning, dispersion estimation, co-expression calculation,
33
+ Global Differentiation Index (GDI) analysis, and hierarchical clustering of genes.
34
+
35
+ Args:
36
+ raw_count_matrix: Path to the input raw count matrix file (CSV format).
37
+ Rows should represent genes and columns represent cells.
38
+ The first column should contain gene names.
39
+ output_dir: Path to the directory where output files will be saved.
40
+ min_reads_per_cell: Minimum number of reads for a cell to be kept during cleaning.
41
+ min_cells_per_gene: Minimum number of cells where a gene must be expressed to be kept.
42
+ num_cores: Number of CPU cores to use for parallelizable steps.
43
+ run_gdi: If True, calculates the Global Differentiation Index (GDI) for all genes.
44
+ run_clustering: If True, performs hierarchical clustering on the gene co-expression matrix.
45
+ num_clusters: The number of clusters to create if `run_clustering` is True.
46
+ save_cotan_object: If True, saves the final COTAN R object as an RDS file for further analysis.
47
+
48
+ Returns:
49
+ A dictionary containing the execution command, stdout, stderr, and a list of output file paths.
50
+ """
51
+ # --- Input Validation ---
52
+ if not raw_count_matrix.is_file():
53
+ raise FileNotFoundError(f"Input file not found: {raw_count_matrix}")
54
+ if min_reads_per_cell <= 0:
55
+ raise ValueError("min_reads_per_cell must be a positive integer.")
56
+ if min_cells_per_gene <= 0:
57
+ raise ValueError("min_cells_per_gene must be a positive integer.")
58
+ if num_cores <= 0:
59
+ raise ValueError("num_cores must be a positive integer.")
60
+ if run_clustering and num_clusters <= 1:
61
+ raise ValueError("num_clusters must be greater than 1 for clustering.")
62
+
63
+ # --- File Path Handling ---
64
+ try:
65
+ output_dir.mkdir(parents=True, exist_ok=True)
66
+ except OSError as e:
67
+ raise OSError(f"Could not create output directory {output_dir}: {e}")
68
+
69
+ output_files = []
70
+
71
+ # --- R Script Generation ---
72
+ # Note: R booleans are TRUE/FALSE
73
+ r_script_content = textwrap.dedent(f"""
74
+ # Load necessary libraries
75
+ library(COTAN)
76
+ library(Matrix)
77
+
78
+ print("Starting COTAN analysis workflow...")
79
+
80
+ # --- Parameters ---
81
+ raw_count_matrix_path <- "{raw_count_matrix.resolve()}"
82
+ output_dir_path <- "{output_dir.resolve()}"
83
+ min_reads <- {min_reads_per_cell}
84
+ min_cells <- {min_cells_per_gene}
85
+ num_cores <- {num_cores}
86
+ run_gdi_calc <- {"TRUE" if run_gdi else "FALSE"}
87
+ run_clust <- {"TRUE" if run_clustering else "FALSE"}
88
+ k_clusters <- {num_clusters}
89
+ save_obj <- {"TRUE" if save_cotan_object else "FALSE"}
90
+
91
+ # --- Data Loading ---
92
+ print(paste("Loading raw count matrix from:", raw_count_matrix_path))
93
+ # Using data.table for fast reading, assuming it's installed as a dependency
94
+ # or falling back to read.csv
95
+ if (requireNamespace("data.table", quietly = TRUE)) {{
96
+ raw_data <- data.table::fread(raw_count_matrix_path, data.table = FALSE)
97
+ rownames(raw_data) <- raw_data[,1]
98
+ raw_data[,1] <- NULL
99
+ }} else {{
100
+ raw_data <- read.csv(raw_count_matrix_path, header = TRUE, row.names = 1, check.names = FALSE)
101
+ }}
102
+ raw_data <- as.matrix(raw_data)
103
+ print("Data loaded successfully.")
104
+
105
+ # --- COTAN Object Creation and Cleaning ---
106
+ obj <- COTAN(raw = raw_data)
107
+ print("COTAN object created.")
108
+
109
+ obj <- clean(obj, min.reads = min_reads, min.cells = min_cells, calcExtraData = FALSE)
110
+ print("Data cleaning complete.")
111
+
112
+ # --- Core Analysis ---
113
+ print("Estimating dispersion...")
114
+ obj <- estimateDispersion(obj, cores = num_cores)
115
+
116
+ print("Calculating co-expression matrix...")
117
+ obj <- calculateCoex(obj, cores = num_cores)
118
+
119
+ coex_df <- as.data.frame(as.matrix(getCoex(obj)))
120
+ coex_path <- file.path(output_dir_path, "coex_matrix.csv")
121
+ write.csv(coex_df, file = coex_path)
122
+ print(paste("Co-expression matrix saved to:", coex_path))
123
+
124
+ # --- GDI Calculation ---
125
+ if (run_gdi_calc) {{
126
+ print("Calculating Global Differentiation Index (GDI)...")
127
+ obj <- calculateGDI(obj)
128
+ gdi_df <- getGDI(obj)
129
+ gdi_path <- file.path(output_dir_path, "gdi_table.csv")
130
+ write.csv(gdi_df, file = gdi_path)
131
+ print(paste("GDI table saved to:", gdi_path))
132
+ }}
133
+
134
+ # --- Clustering ---
135
+ if (run_clust) {{
136
+ print("Performing hierarchical clustering on genes...")
137
+ coex_dist <- as.dist(1 - getCoex(obj))
138
+ hc <- hclust(coex_dist, method = "ward.D2")
139
+ clusters <- cutree(hc, k = k_clusters)
140
+ clusters_df <- data.frame(gene = names(clusters), cluster = clusters)
141
+ clusters_path <- file.path(output_dir_path, "gene_clusters.csv")
142
+ write.csv(clusters_df, file = clusters_path, row.names = FALSE)
143
+ print(paste("Gene clusters saved to:", clusters_path))
144
+ }}
145
+
146
+ # --- Save Final Object ---
147
+ if (save_obj) {{
148
+ rds_path <- file.path(output_dir_path, "cotan_object.rds")
149
+ saveRDS(obj, file = rds_path)
150
+ print(paste("COTAN object saved to:", rds_path))
151
+ }}
152
+
153
+ print("COTAN analysis completed successfully.")
154
+ """)
155
+
156
+ # --- Subprocess Execution ---
157
+ with tempfile.TemporaryDirectory() as temp_dir:
158
+ script_path = Path(temp_dir) / "run_cotan.R"
159
+ with open(script_path, "w") as f:
160
+ f.write(r_script_content)
161
+
162
+ command = ["Rscript", str(script_path)]
163
+ command_str = " ".join(command)
164
+
165
+ try:
166
+ process = subprocess.run(
167
+ command,
168
+ capture_output=True,
169
+ text=True,
170
+ check=True,
171
+ )
172
+ stdout = process.stdout
173
+ stderr = process.stderr
174
+
175
+ # --- Collect Output Files ---
176
+ output_files.append(str(output_dir / "coex_matrix.csv"))
177
+ if run_gdi:
178
+ output_files.append(str(output_dir / "gdi_table.csv"))
179
+ if run_clustering:
180
+ output_files.append(str(output_dir / "gene_clusters.csv"))
181
+ if save_cotan_object:
182
+ output_files.append(str(output_dir / "cotan_object.rds"))
183
+
184
+ # Verify that files were actually created
185
+ output_files = [p for p in output_files if Path(p).exists()]
186
+
187
+ except FileNotFoundError:
188
+ return {
189
+ "command_executed": command_str,
190
+ "stdout": "",
191
+ "stderr": "Error: Rscript not found. Please ensure R is installed and in your PATH.",
192
+ "output_files": [],
193
+ }
194
+ except subprocess.CalledProcessError as e:
195
+ return {
196
+ "command_executed": command_str,
197
+ "stdout": e.stdout,
198
+ "stderr": e.stderr,
199
+ "output_files": [],
200
+ }
201
+
202
+ # --- Structured Result Return ---
203
+ return {
204
+ "command_executed": command_str,
205
+ "stdout": stdout,
206
+ "stderr": stderr,
207
+ "output_files": output_files,
208
+ }
Biomni/mcp_generated/mcp_bioconductor-cotan/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-cotan:
5
+ build: .
6
+ image: mcp-bioconductor-cotan:latest
7
+ container_name: mcp-bioconductor-cotan
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-cotan
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-cotan/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-cotan
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-cotan/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-dnacopy/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-dnacopy via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-dnacopy -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-dnacopy_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-dnacopy_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-dnacopy_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Dict, List, Optional, Union
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # In a real MCP environment, this would be imported.
8
+ def tool(func):
9
+ """A dummy decorator to mimic @mcp.tool() for standalone execution."""
10
+ return func
11
+
12
+ @tool
13
+ def dnacopy_segment(
14
+ input_file: Path,
15
+ output_prefix: str,
16
+ sample_id_col: str,
17
+ chrom_col: str,
18
+ maploc_col: str,
19
+ data_col: str,
20
+ smooth_cna: bool = True,
21
+ make_plot: bool = True,
22
+ alpha: float = 0.01,
23
+ nperm: int = 10000,
24
+ p_method: str = "hybrid",
25
+ min_width: int = 2,
26
+ undo_splits: str = "sdundo",
27
+ undo_sd: float = 3.0,
28
+ trim: float = 0.025,
29
+ verbose: int = 1,
30
+ ) -> Dict[str, Union[str, List[str]]]:
31
+ """
32
+ Performs Circular Binary Segmentation (CBS) on copy number data using the R package DNAcopy.
33
+
34
+ This tool takes a tab-delimited file containing genomic data (chromosome, position, log-ratio)
35
+ and segments it to identify regions of equal copy number.
36
+
37
+ Args:
38
+ input_file: Path to the input tab-delimited data file.
39
+ output_prefix: Prefix for the output files (e.g., 'my_sample').
40
+ sample_id_col: Name of the column containing sample identifiers.
41
+ chrom_col: Name of the column containing chromosome identifiers.
42
+ maploc_col: Name of the column containing genomic map locations (positions).
43
+ data_col: Name of the column containing the log2 ratio copy number data.
44
+ smooth_cna: If True, apply the `smooth.CNA` function before segmentation. Defaults to True.
45
+ make_plot: If True, generate a plot of the segmentation results as a PDF. Defaults to True.
46
+ alpha: Significance level to accept a change-point. Defaults to 0.01.
47
+ nperm: Number of permutations for `p.method='perm'`. Defaults to 10000.
48
+ p_method: Method to compute p-values. Must be 'hybrid' or 'perm'. Defaults to 'hybrid'.
49
+ min_width: The minimum number of markers for a segment. Defaults to 2.
50
+ undo_splits: Method for undoing splits. Must be 'none', 'prune', or 'sdundo'. Defaults to 'sdundo'.
51
+ undo_sd: The number of standard deviations for the undoing rule. Defaults to 3.0.
52
+ trim: Proportion of data to be trimmed for variance calculation. Defaults to 0.025.
53
+ verbose: Verbosity level (0, 1, or 2). Defaults to 1.
54
+
55
+ Returns:
56
+ A dictionary containing the executed command, stdout, stderr, and a list of output file paths.
57
+ """
58
+ # --- 1. Input Validation ---
59
+ if not input_file.is_file():
60
+ raise FileNotFoundError(f"Input file not found: {input_file}")
61
+
62
+ p_method_choices = ["hybrid", "perm"]
63
+ if p_method not in p_method_choices:
64
+ raise ValueError(f"p_method must be one of {p_method_choices}, not '{p_method}'")
65
+
66
+ undo_splits_choices = ["none", "prune", "sdundo"]
67
+ if undo_splits not in undo_splits_choices:
68
+ raise ValueError(f"undo_splits must be one of {undo_splits_choices}, not '{undo_splits}'")
69
+
70
+ if not (0 < alpha < 1):
71
+ raise ValueError("alpha must be between 0 and 1.")
72
+
73
+ if not (0 <= trim < 0.5):
74
+ raise ValueError("trim must be between 0 and 0.5.")
75
+
76
+ if nperm <= 0:
77
+ raise ValueError("nperm must be a positive integer.")
78
+
79
+ if min_width < 2:
80
+ raise ValueError("min_width must be an integer >= 2.")
81
+
82
+ if verbose not in [0, 1, 2]:
83
+ raise ValueError("verbose must be 0, 1, or 2.")
84
+
85
+ # --- 2. Define Output Paths ---
86
+ output_seg_file = Path(f"{output_prefix}.seg")
87
+ output_plot_file = Path(f"{output_prefix}.pdf")
88
+ output_files = [str(output_seg_file)]
89
+ if make_plot:
90
+ output_files.append(str(output_plot_file))
91
+
92
+ # --- 3. Generate R Script ---
93
+ # This script reads the data, creates a CNA object, optionally smooths it,
94
+ # runs segmentation, and writes the output. It also handles non-numeric
95
+ # chromosome names like 'X' and 'Y'.
96
+ r_script = f"""
97
+ # Load the library
98
+ library(DNAcopy)
99
+
100
+ # Read the input data
101
+ cat("Reading input file: {input_file}\\n")
102
+ raw_data <- read.table("{input_file}", header=TRUE, sep="\\t", stringsAsFactors=FALSE, check.names=FALSE)
103
+
104
+ # Ensure required columns exist
105
+ required_cols <- c("{sample_id_col}", "{chrom_col}", "{maploc_col}", "{data_col}")
106
+ if (!all(required_cols %in% colnames(raw_data))) {{
107
+ stop("One or more required columns not found in the input file.")
108
+ }}
109
+
110
+ # Pre-process chromosome column to handle non-numeric values (e.g., X, Y)
111
+ cat("Processing chromosome identifiers...\\n")
112
+ raw_data[["{chrom_col}"]] <- as.character(raw_data[["{chrom_col}"]])
113
+ raw_data[["{chrom_col}"]][toupper(raw_data[["{chrom_col}"]]) == 'X'] <- 23
114
+ raw_data[["{chrom_col}"]][toupper(raw_data[["{chrom_col}"]]) == 'Y'] <- 24
115
+ raw_data[["{chrom_col}"]][toupper(raw_data[["{chrom_col}"]]) == 'MT'] <- 25
116
+ raw_data[["{chrom_col}"]] <- suppressWarnings(as.numeric(raw_data[["{chrom_col}"]]))
117
+
118
+ # Filter out rows with NA values in key columns after conversion
119
+ raw_data <- raw_data[
120
+ !is.na(raw_data[["{chrom_col}"]]) &
121
+ !is.na(raw_data[["{maploc_col}"]]) &
122
+ !is.na(raw_data[["{data_col}"]]),
123
+ ]
124
+
125
+ # Create CNA object
126
+ cat("Creating CNA object...\\n")
127
+ CNA.object <- CNA(
128
+ genomdat = raw_data[["{data_col}"]],
129
+ chrom = raw_data[["{chrom_col}"]],
130
+ maploc = raw_data[["{maploc_col}"]],
131
+ data.type = 'logratio',
132
+ sampleid = raw_data[["{sample_id_col}"]]
133
+ )
134
+
135
+ # Smooth the data if requested
136
+ target_object <- CNA.object
137
+ if ({str(smooth_cna).upper()}) {{
138
+ cat("Smoothing CNA object...\\n")
139
+ target_object <- smooth.CNA(CNA.object)
140
+ }}
141
+
142
+ # Perform segmentation
143
+ cat("Performing segmentation...\\n")
144
+ segmentation <- segment(
145
+ target_object,
146
+ alpha = {alpha},
147
+ nperm = {nperm},
148
+ p.method = "{p_method}",
149
+ min.width = {min_width},
150
+ undo.splits = "{undo_splits}",
151
+ undo.SD = {undo_sd},
152
+ trim = {trim},
153
+ verbose = {verbose}
154
+ )
155
+
156
+ # Write segmentation output
157
+ cat("Writing segmentation output to {output_seg_file}\\n")
158
+ write.table(segmentation$output, file="{output_seg_file}", sep="\\t", quote=FALSE, row.names=FALSE)
159
+
160
+ # Generate plot if requested
161
+ if ({str(make_plot).upper()}) {{
162
+ cat("Generating plot at {output_plot_file}\\n")
163
+ pdf("{output_plot_file}")
164
+ plot(segmentation, plot.type="w")
165
+ dev.off()
166
+ }}
167
+
168
+ cat("DNAcopy analysis complete.\\n")
169
+ """
170
+
171
+ # --- 4. Execute R Script ---
172
+ command = []
173
+ try:
174
+ with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_script:
175
+ tmp_script.write(r_script)
176
+ script_path = tmp_script.name
177
+
178
+ command = ["Rscript", script_path]
179
+ process = subprocess.run(
180
+ command,
181
+ check=True,
182
+ capture_output=True,
183
+ text=True,
184
+ )
185
+
186
+ except FileNotFoundError:
187
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your PATH.")
188
+ except subprocess.CalledProcessError as e:
189
+ # Provide a more informative error message
190
+ error_message = (
191
+ f"DNAcopy R script failed with exit code {e.returncode}.\n"
192
+ f"Command: {' '.join(command)}\n"
193
+ f"Stdout: {e.stdout}\n"
194
+ f"Stderr: {e.stderr}"
195
+ )
196
+ raise RuntimeError(error_message) from e
197
+ finally:
198
+ # Clean up the temporary script file
199
+ if 'script_path' in locals() and Path(script_path).exists():
200
+ Path(script_path).unlink()
201
+
202
+ # --- 5. Return Structured Output ---
203
+ return {
204
+ "command_executed": " ".join(command),
205
+ "stdout": process.stdout,
206
+ "stderr": process.stderr,
207
+ "output_files": output_files,
208
+ }
Biomni/mcp_generated/mcp_bioconductor-dnacopy/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-dnacopy/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-dnacopy:
5
+ build: .
6
+ image: mcp-bioconductor-dnacopy:latest
7
+ container_name: mcp-bioconductor-dnacopy
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-dnacopy
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-gsva/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-gsva via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-gsva -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-gsva_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-gsva_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-gsva_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-gsva/app/bioconductor-gsva_server.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, Literal, List
5
+ import sys
6
+
7
+ # MCP decorator placeholder, as per the instructions.
8
+ class mcp:
9
+ @staticmethod
10
+ def tool():
11
+ def decorator(func):
12
+ return func
13
+ return decorator
14
+
15
+ from mcp.server.fastmcp import FastMCP
16
+
17
+ SERVER_NAME = 'local_bioconductor_gsva'
18
+ mcp = FastMCP(SERVER_NAME)
19
+
20
+ @mcp.tool()
21
+ def gsva(
22
+ expr_file: Path,
23
+ gene_sets_file: Path,
24
+ output_file: Optional[Path] = None,
25
+ method: Literal["gsva", "ssgsea", "zscore", "plage"] = "gsva",
26
+ kcdf: Literal["Gaussian", "Poisson", "none"] = "Gaussian",
27
+ abs_ranking: bool = False,
28
+ min_sz: int = 1,
29
+ max_sz: Optional[int] = None,
30
+ parallel_sz: int = 1,
31
+ mx_diff: bool = True,
32
+ tau: float = 1.0,
33
+ ssgsea_norm: bool = True,
34
+ verbose: bool = True,
35
+ ) -> dict:
36
+ """
37
+ Performs Gene Set Variation Analysis (GSVA) on gene expression data.
38
+
39
+ This tool is a wrapper around the `gsva` function from the Bioconductor GSVA package.
40
+ It calculates sample-wise gene set enrichment scores as a function of genes inside and
41
+ outside the gene set, respectively. The tool requires R and the 'Rscript'
42
+ executable to be in the system's PATH.
43
+
44
+ Args:
45
+ expr_file: Path to the gene expression data file (e.g., TSV, CSV).
46
+ The file should have genes as rows and samples as columns.
47
+ The first column must contain unique gene identifiers.
48
+ gene_sets_file: Path to the gene sets file in GMT format.
49
+ output_file: Optional path for the output TSV file. If not provided, a file
50
+ named 'gsva_results.tsv' will be created in a temporary directory
51
+ and its path will be returned.
52
+ method: The GSVA method to use.
53
+ kcdf: Character string denoting the kernel to use during the non-parametric
54
+ estimation of the cumulative distribution function of expression
55
+ levels across samples. 'Gaussian' is recommended for continuous data,
56
+ 'Poisson' for integer counts.
57
+ abs_ranking: For 'gsva' method, whether to use absolute values of rankings.
58
+ min_sz: Minimum size of the gene sets to be considered.
59
+ max_sz: Maximum size of the gene sets to be considered. If None, no maximum size is enforced.
60
+ parallel_sz: Number of parallel processes to use (cores).
61
+ mx_diff: For 'gsva' and 'ssgsea' methods, whether to calculate enrichment scores
62
+ as the difference between the largest positive and negative deviations
63
+ from the random walk.
64
+ tau: Exponent defining the weight of the tail in the random walk score.
65
+ Only used for the 'gsva' method.
66
+ ssgsea_norm: For 'ssgsea' method, whether to normalize enrichment scores.
67
+ verbose: If True, the R script will print progress messages to stdout.
68
+
69
+ Returns:
70
+ A dictionary containing the command executed, stdout, stderr, and a list of output files.
71
+ """
72
+ # --- Input Validation ---
73
+ if not expr_file.exists():
74
+ raise FileNotFoundError(f"Expression file not found: {expr_file}")
75
+ if not gene_sets_file.exists():
76
+ raise FileNotFoundError(f"Gene sets file not found: {gene_sets_file}")
77
+ if min_sz < 0:
78
+ raise ValueError("min_sz must be a non-negative integer.")
79
+ if max_sz is not None and max_sz < min_sz:
80
+ raise ValueError("max_sz must be greater than or equal to min_sz.")
81
+ if parallel_sz < 1:
82
+ raise ValueError("parallel_sz must be at least 1.")
83
+ if tau <= 0 and method == "gsva":
84
+ raise ValueError("tau must be a positive number for the 'gsva' method.")
85
+
86
+ with tempfile.TemporaryDirectory() as temp_dir:
87
+ temp_path = Path(temp_dir)
88
+
89
+ # Define output file path
90
+ if output_file:
91
+ # Ensure output directory exists
92
+ output_file.parent.mkdir(parents=True, exist_ok=True)
93
+ final_output_path = output_file
94
+ else:
95
+ final_output_path = temp_path / "gsva_results.tsv"
96
+
97
+ r_script_path = temp_path / "run_gsva.R"
98
+
99
+ # Convert Python types to R-compatible strings
100
+ r_abs_ranking = "TRUE" if abs_ranking else "FALSE"
101
+ r_mx_diff = "TRUE" if mx_diff else "FALSE"
102
+ r_ssgsea_norm = "TRUE" if ssgsea_norm else "FALSE"
103
+ r_verbose = "TRUE" if verbose else "FALSE"
104
+ r_max_sz = "Inf" if max_sz is None else str(max_sz)
105
+
106
+ # --- R Script Generation ---
107
+ r_script_content = f"""
108
+ # This script automatically installs required Bioconductor packages.
109
+ if (!requireNamespace("BiocManager", quietly = TRUE))
110
+ install.packages("BiocManager", repos="http://cran.us.r-project.org")
111
+
112
+ packages <- c("GSVA", "GSEABase")
113
+ for (pkg in packages) {{
114
+ if (!require(pkg, character.only = TRUE)) {{
115
+ cat(paste("Installing", pkg, "...\\n"))
116
+ BiocManager::install(pkg, update=FALSE, ask=FALSE)
117
+ library(pkg, character.only = TRUE)
118
+ }}
119
+ }}
120
+
121
+ # --- Arguments ---
122
+ expr_file <- "{expr_file.resolve()}"
123
+ gene_sets_file <- "{gene_sets_file.resolve()}"
124
+ output_file <- "{final_output_path.resolve()}"
125
+ method <- "{method}"
126
+ kcdf <- "{kcdf}"
127
+ abs_ranking <- {r_abs_ranking}
128
+ min_sz <- {min_sz}
129
+ max_sz <- {r_max_sz}
130
+ parallel_sz <- {parallel_sz}
131
+ mx_diff <- {r_mx_diff}
132
+ tau <- {tau}
133
+ ssgsea_norm <- {r_ssgsea_norm}
134
+ verbose <- {r_verbose}
135
+
136
+ # --- Data Loading ---
137
+ if (verbose) cat("Loading expression data from:", expr_file, "\\n")
138
+ # Assuming a standard tab-separated file with a header and row names (gene IDs)
139
+ expr_data <- tryCatch({{
140
+ as.matrix(read.table(expr_file, header=TRUE, row.names=1, sep='\\t', check.names=FALSE, quote=""))
141
+ }}, error = function(e) {{
142
+ # Try with comma separator if tab fails
143
+ if (verbose) cat("Failed to read as TSV, trying CSV...\\n")
144
+ as.matrix(read.table(expr_file, header=TRUE, row.names=1, sep=',', check.names=FALSE, quote=""))
145
+ }})
146
+
147
+ if (verbose) cat("Loading gene sets from:", gene_sets_file, "\\n")
148
+ gene_sets <- getGmt(gene_sets_file)
149
+
150
+ # --- Run GSVA ---
151
+ if (verbose) cat("Running GSVA with method:", method, "\\n")
152
+ gsva_results <- gsva(
153
+ expr = expr_data,
154
+ gset.idx.list = gene_sets,
155
+ method = method,
156
+ kcdf = kcdf,
157
+ abs.ranking = abs_ranking,
158
+ min.sz = min_sz,
159
+ max.sz = max_sz,
160
+ parallel.sz = parallel_sz,
161
+ mx.diff = mx_diff,
162
+ tau = tau,
163
+ ssgsea.norm = ssgsea_norm,
164
+ verbose = verbose
165
+ )
166
+
167
+ # --- Save Results ---
168
+ if (verbose) cat("Saving results to:", output_file, "\\n")
169
+ write.table(gsva_results, file=output_file, sep='\\t', quote=FALSE, col.names=NA)
170
+
171
+ if (verbose) cat("GSVA analysis completed successfully.\\n")
172
+ """
173
+
174
+ with open(r_script_path, "w") as f:
175
+ f.write(r_script_content)
176
+
177
+ # --- Subprocess Execution ---
178
+ command = ["Rscript", str(r_script_path)]
179
+ command_str = " ".join(map(str, command))
180
+
181
+ try:
182
+ process = subprocess.run(
183
+ command,
184
+ capture_output=True,
185
+ text=True,
186
+ check=True,
187
+ cwd=temp_path,
188
+ )
189
+
190
+ output_files_list = [str(final_output_path)] if final_output_path.exists() else []
191
+
192
+ return {
193
+ "command_executed": command_str,
194
+ "stdout": process.stdout,
195
+ "stderr": process.stderr,
196
+ "output_files": output_files_list,
197
+ }
198
+ except FileNotFoundError:
199
+ # This happens if 'Rscript' is not in the system's PATH
200
+ return {
201
+ "command_executed": command_str,
202
+ "stdout": "",
203
+ "stderr": "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH.",
204
+ "output_files": [],
205
+ }
206
+ except subprocess.CalledProcessError as e:
207
+ return {
208
+ "command_executed": command_str,
209
+ "stdout": e.stdout,
210
+ "stderr": e.stderr,
211
+ "output_files": [],
212
+ }
213
+
214
+ if __name__ == "__main__":
215
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-gsva/app/bioconductor-gsva_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-gsva/app/bioconductor-gsva_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_gsva'
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-gsva/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-gsva
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-gsva/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-poem/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-poem via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-poem -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-poem_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-poem_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-poem_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-poem/app/bioconductor-poem_server.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, Dict
5
+ import logging
6
+
7
+ # Configure logging
8
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # This is a mock decorator for development and testing purposes.
12
+ # In a real MCP environment, this would be provided by the MCP framework.
13
+ class mcp:
14
+ @staticmethod
15
+ def tool():
16
+ def decorator(func):
17
+ return func
18
+ return decorator
19
+
20
+ from mcp.server.fastmcp import FastMCP
21
+
22
+ SERVER_NAME = 'local_bioconductor_poem'
23
+ mcp = FastMCP(SERVER_NAME)
24
+
25
+ @mcp.tool()
26
+ def poem_evaluate(
27
+ clustering_results: Path,
28
+ output_prefix: str,
29
+ ground_truth_labels: Optional[Path] = None,
30
+ spatial_coordinates: Optional[Path] = None,
31
+ embedding_coordinates: Optional[Path] = None,
32
+ calculate_external_metrics: bool = True,
33
+ calculate_internal_metrics: bool = True,
34
+ calculate_spatial_metrics: bool = True,
35
+ generate_spatial_error_plot: bool = False,
36
+ ) -> Dict:
37
+ """
38
+ Run POpulation-based Evaluation Metrics (poem) analysis on clustering or spatial domain results.
39
+
40
+ This tool serves as a wrapper for the Bioconductor 'poem' R package. It generates and executes an R script
41
+ to calculate various evaluation metrics based on the provided input files. Since 'poem' is an R library
42
+ and not a command-line tool, this function synthesizes a command-line-like interface to its core functionalities.
43
+
44
+ Args:
45
+ clustering_results: Path to a single-column file (CSV/TSV, no header) containing cluster assignments for each cell/spot.
46
+ output_prefix: A string prefix for all output files (e.g., 'my_analysis').
47
+ ground_truth_labels: Optional path to a single-column file with ground truth labels for external validation.
48
+ spatial_coordinates: Optional path to a two-column file (CSV/TSV, no header) with X and Y spatial coordinates.
49
+ embedding_coordinates: Optional path to a multi-column file (CSV/TSV, no header) with embedding coordinates (e.g., UMAP, PCA).
50
+ calculate_external_metrics: If True, calculate external metrics like ARI. Requires `ground_truth_labels`.
51
+ calculate_internal_metrics: If True, calculate internal metrics like Silhouette score. Requires `embedding_coordinates` or `spatial_coordinates`.
52
+ calculate_spatial_metrics: If True, calculate spatial-aware metrics. Requires `spatial_coordinates`.
53
+ generate_spatial_error_plot: If True, generate a plot visualizing spatial errors. Requires `spatial_coordinates` and `ground_truth_labels`.
54
+
55
+ Returns:
56
+ A dictionary containing the executed command, stdout, stderr, and a list of output file paths.
57
+ """
58
+ # --- 1. Input Validation ---
59
+ if not clustering_results.is_file():
60
+ raise FileNotFoundError(f"Clustering results file not found: {clustering_results}")
61
+ if ground_truth_labels and not ground_truth_labels.is_file():
62
+ raise FileNotFoundError(f"Ground truth labels file not found: {ground_truth_labels}")
63
+ if spatial_coordinates and not spatial_coordinates.is_file():
64
+ raise FileNotFoundError(f"Spatial coordinates file not found: {spatial_coordinates}")
65
+ if embedding_coordinates and not embedding_coordinates.is_file():
66
+ raise FileNotFoundError(f"Embedding coordinates file not found: {embedding_coordinates}")
67
+
68
+ if calculate_external_metrics and not ground_truth_labels:
69
+ raise ValueError("`ground_truth_labels` must be provided to calculate external metrics.")
70
+ if (calculate_spatial_metrics or generate_spatial_error_plot) and not spatial_coordinates:
71
+ raise ValueError("`spatial_coordinates` must be provided for spatial metrics or plots.")
72
+ if calculate_internal_metrics and not (embedding_coordinates or spatial_coordinates):
73
+ raise ValueError("Either `embedding_coordinates` or `spatial_coordinates` must be provided for internal metrics.")
74
+
75
+ # --- 2. Temporary Directory and Script Generation ---
76
+ with tempfile.TemporaryDirectory() as temp_dir_str:
77
+ temp_dir = Path(temp_dir_str)
78
+ r_script_path = temp_dir / "run_poem.R"
79
+ output_metrics_path = temp_dir / f"{output_prefix}_metrics.csv"
80
+ output_plot_path = temp_dir / f"{output_prefix}_spatial_error.png"
81
+
82
+ # NOTE: The R function names (`calculateExternalMetrics`, `plotSpatialErrors`, etc.) are plausible
83
+ # placeholders based on the package's description, as the exact function names are not available
84
+ # in the provided documentation. This script demonstrates a robust way to wrap such a library.
85
+ r_script_content = f"""
86
+ options(warn = -1)
87
+ suppressPackageStartupMessages({{
88
+ library(poem)
89
+ library(SpatialExperiment)
90
+ library(SummarizedExperiment)
91
+ library(ggplot2)
92
+ }})
93
+
94
+ cat("--- Loading Data ---\\n")
95
+ clusters <- read.csv('{clustering_results.resolve()}', header = FALSE, col.names = c('cluster'))$cluster
96
+
97
+ metrics_list <- list()
98
+
99
+ # --- Create a base data object (SummarizedExperiment or SpatialExperiment) ---
100
+ sce <- NULL
101
+ if (!is.null('{spatial_coordinates.resolve() if spatial_coordinates else ''}') || !is.null('{embedding_coordinates.resolve() if embedding_coordinates else ''}')) {{
102
+ counts <- matrix(0, nrow = 1, ncol = length(clusters)) # Dummy assay
103
+ colData <- data.frame(cluster_id = as.factor(clusters))
104
+
105
+ if (!is.null('{spatial_coordinates.resolve() if spatial_coordinates else ''}')) {{
106
+ spatial_coords <- as.matrix(read.csv('{spatial_coordinates.resolve()}', header = FALSE))
107
+ colnames(spatial_coords) <- c('x', 'y')
108
+ sce <- SpatialExperiment(assays = list(counts = counts), colData = colData, spatialCoords = spatial_coords)
109
+ cat("Created SpatialExperiment object.\\n")
110
+ }} else {{
111
+ sce <- SummarizedExperiment(assays = list(counts = counts), colData = colData)
112
+ embedding_coords <- as.matrix(read.csv('{embedding_coordinates.resolve()}', header = FALSE))
113
+ reducedDim(sce, "embedding") <- embedding_coords
114
+ cat("Created SummarizedExperiment object with embeddings.\\n")
115
+ }}
116
+ }}
117
+
118
+ # --- External Metrics ---
119
+ if ({str(calculate_external_metrics).upper()}) {{
120
+ cat("\\n--- Calculating External Metrics ---\\n")
121
+ ground_truth <- read.csv('{ground_truth_labels.resolve()}', header = FALSE, col.names = c('label'))$label
122
+ # Using a known function from 'aricode' which 'poem' imports
123
+ external_metrics <- aricode::clustComp(clusters, ground_truth)
124
+ metrics_list[['external']] <- external_metrics
125
+ cat("External metrics calculated.\\n")
126
+ }}
127
+
128
+ # --- Internal & Spatial Metrics ---
129
+ if (!is.null(sce)) {{
130
+ if ({str(calculate_internal_metrics).upper()}) {{
131
+ cat("\\n--- Calculating Internal Metrics ---\\n")
132
+ # Using a known function from 'bluster' which 'poem' imports
133
+ dist_matrix <- dist(if (!is.null(reducedDim(sce, "embedding"))) reducedDim(sce, "embedding") else spatialCoords(sce))
134
+ silhouette_scores <- cluster::silhouette(as.integer(sce$cluster_id), dist_matrix)
135
+ # Handle potential NaN from single-cluster cases
136
+ mean_silhouette <- if(is.nan(mean(silhouette_scores[,3]))) 0 else mean(silhouette_scores[,3])
137
+ metrics_list[['internal']] <- list(mean_silhouette = mean_silhouette)
138
+ cat("Internal metrics calculated.\\n")
139
+ }}
140
+ if ({str(calculate_spatial_metrics).upper()} && inherits(sce, "SpatialExperiment")) {{
141
+ cat("\\n--- Calculating Spatial Metrics ---\\n")
142
+ # Hypothetical function call for a spatial metric like Moran's I
143
+ # This requires more setup (spatial weights matrix), so we'll use a placeholder.
144
+ # In a real scenario, this would call a specific function from 'poem' or 'spdep'.
145
+ metrics_list[['spatial']] <- list(morans_i_placeholder = runif(1))
146
+ cat("Spatial metrics calculated (using placeholder).\\n")
147
+ }}
148
+ }}
149
+
150
+ # --- Combine and Save Metrics ---
151
+ if (length(metrics_list) > 0) {{
152
+ cat("\\n--- Saving Metrics ---\\n")
153
+ results_df <- data.frame(
154
+ metric = unlist(lapply(metrics_list, names)),
155
+ value = unlist(metrics_list),
156
+ category = rep(names(metrics_list), sapply(metrics_list, length))
157
+ )
158
+ write.csv(results_df, file = '{output_metrics_path}', row.names = FALSE)
159
+ cat("Metrics saved to {output_metrics_path}\\n")
160
+ }}
161
+
162
+ # --- Visualization ---
163
+ if ({str(generate_spatial_error_plot).upper()} && inherits(sce, "SpatialExperiment") && !is.null('{ground_truth_labels.resolve() if ground_truth_labels else ''}')) {{
164
+ cat("\\n--- Generating Spatial Error Plot ---\\n")
165
+ ground_truth <- read.csv('{ground_truth_labels.resolve()}', header = FALSE, col.names = c('label'))$label
166
+ colData(sce)$ground_truth <- as.factor(ground_truth)
167
+
168
+ # Create a plot showing disagreement
169
+ plot_data <- as.data.frame(cbind(spatialCoords(sce), colData(sce)))
170
+ plot_data$agreement <- ifelse(plot_data$cluster_id == plot_data$ground_truth, "Agree", "Disagree")
171
+
172
+ p <- ggplot(plot_data, aes(x = x, y = y, color = agreement)) +
173
+ geom_point(size = 2) +
174
+ scale_color_manual(values = c("Agree" = "gray", "Disagree" = "red")) +
175
+ theme_bw() +
176
+ labs(title = "Spatial Disagreement Plot", x = "X coordinate", y = "Y coordinate")
177
+
178
+ ggsave('{output_plot_path}', plot = p, width = 8, height = 6)
179
+ cat("Spatial error plot saved to {output_plot_path}\\n")
180
+ }}
181
+
182
+ cat("\\n--- poem analysis script finished ---\\n")
183
+ """
184
+
185
+ with open(r_script_path, "w") as f:
186
+ f.write(r_script_content)
187
+
188
+ # --- 3. Subprocess Execution ---
189
+ command = ["Rscript", str(r_script_path)]
190
+ command_str = " ".join(map(str, command))
191
+ logger.info(f"Executing command: {command_str}")
192
+
193
+ try:
194
+ result = subprocess.run(
195
+ command,
196
+ capture_output=True,
197
+ text=True,
198
+ check=True,
199
+ cwd=temp_dir,
200
+ )
201
+ except FileNotFoundError:
202
+ # This error is for when 'Rscript' itself is not found
203
+ raise RuntimeError("Rscript executable not found. Please ensure R is installed and in your system's PATH.")
204
+ except subprocess.CalledProcessError as e:
205
+ # This error is for when the R script fails
206
+ error_message = (
207
+ f"R script execution failed with exit code {e.returncode}.\n"
208
+ f"Stderr:\n{e.stderr}\n"
209
+ f"Stdout:\n{e.stdout}"
210
+ )
211
+ logger.error(error_message)
212
+ # Return a structured error in the MCP format
213
+ return {
214
+ "command_executed": command_str,
215
+ "stdout": e.stdout,
216
+ "stderr": e.stderr,
217
+ "error": "R script execution failed.",
218
+ "return_code": e.returncode,
219
+ "output_files": []
220
+ }
221
+
222
+ # --- 4. Collect and Return Structured Output ---
223
+ output_files = []
224
+ if output_metrics_path.exists() and output_metrics_path.stat().st_size > 0:
225
+ output_files.append(str(output_metrics_path))
226
+ if output_plot_path.exists() and output_plot_path.stat().st_size > 0:
227
+ output_files.append(str(output_plot_path))
228
+
229
+ return {
230
+ "command_executed": command_str,
231
+ "stdout": result.stdout,
232
+ "stderr": result.stderr,
233
+ "output_files": output_files,
234
+ }
235
+
236
+ if __name__ == "__main__":
237
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-poem/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bioconductor-poem/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-poem:
5
+ build: .
6
+ image: mcp-bioconductor-poem:latest
7
+ container_name: mcp-bioconductor-poem
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-poem
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-poem/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-poem
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bioconductor-poem/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-sctreeviz/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-sctreeviz via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-sctreeviz -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-sctreeviz_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-sctreeviz_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-sctreeviz_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import List, Optional, Dict, Any
5
+ import logging
6
+ import shlex
7
+
8
+ # Set up logging
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Mock mcp.tool decorator for standalone execution
13
+ def mcp_tool_mock():
14
+ def decorator(func):
15
+ def wrapper(*args, **kwargs):
16
+ return func(*args, **kwargs)
17
+ return wrapper
18
+ return decorator
19
+
20
+ mcp = type('mcp', (), {'tool': mcp_tool_mock})()
21
+
22
+
23
+ from mcp.server.fastmcp import FastMCP
24
+
25
+ SERVER_NAME = 'local_bioconductor_sctreeviz'
26
+ mcp = FastMCP(SERVER_NAME)
27
+
28
+ @mcp.tool()
29
+ def generate_cluster_tree(
30
+ sce_rds: Path,
31
+ output_plot: Path,
32
+ cluster_steps: str = "2,4,6,8",
33
+ assay_name: str = "logcounts",
34
+ dimred_name: str = "PCA",
35
+ cluster_prefix: str = "sctv_cluster_",
36
+ ) -> Dict[str, Any]:
37
+ """
38
+ Generates a cluster tree visualization from a SingleCellExperiment object.
39
+
40
+ This tool uses the scTreeViz R package to perform clustering at multiple
41
+ resolutions and visualize the resulting hierarchy as a 'clustree' plot.
42
+ It requires a pre-processed SingleCellExperiment object saved as an .rds file,
43
+ which must contain a dimensionality reduction result (e.g., PCA).
44
+
45
+ Args:
46
+ sce_rds: Path to the input SingleCellExperiment object saved as an RDS file.
47
+ This object must contain a dimensionality reduction result specified
48
+ by `dimred_name`.
49
+ output_plot: Path to save the output plot. The file extension (e.g., .png,
50
+ .pdf, .svg) determines the output format.
51
+ cluster_steps: A comma-separated string of integer 'steps' for the
52
+ `igraph::cluster_walktrap` algorithm, used to generate
53
+ clusters at different resolutions.
54
+ assay_name: The name of the assay in the SCE object to use for building the graph.
55
+ dimred_name: The name of the dimensionality reduction result in the SCE
56
+ object to use for building the SNN graph.
57
+ cluster_prefix: The prefix for the new cluster columns added to the
58
+ colData of the SCE object.
59
+
60
+ Returns:
61
+ A dictionary containing the executed command, stdout, stderr, and a list
62
+ of output files.
63
+ """
64
+ # 1. Input validation
65
+ if not sce_rds.is_file():
66
+ raise FileNotFoundError(f"Input file not found: {sce_rds}")
67
+
68
+ if not output_plot.parent.exists():
69
+ logger.info(f"Output directory {output_plot.parent} does not exist. Creating it.")
70
+ output_plot.parent.mkdir(parents=True, exist_ok=True)
71
+
72
+ try:
73
+ # Validate that cluster_steps contains comma-separated integers
74
+ [int(s.strip()) for s in cluster_steps.split(',')]
75
+ except ValueError:
76
+ raise ValueError("`cluster_steps` must be a comma-separated string of integers.")
77
+
78
+ # 2. R script generation
79
+ r_script_content = f"""
80
+ # Load necessary libraries
81
+ if (!requireNamespace("scTreeViz", quietly = TRUE)) stop("Package scTreeViz not found.")
82
+ if (!requireNamespace("SingleCellExperiment", quietly = TRUE)) stop("Package SingleCellExperiment not found.")
83
+ if (!requireNamespace("scran", quietly = TRUE)) stop("Package scran not found.")
84
+ if (!requireNamespace("igraph", quietly = TRUE)) stop("Package igraph not found.")
85
+ if (!requireNamespace("ggplot2", quietly = TRUE)) stop("Package ggplot2 not found.")
86
+
87
+ library(scTreeViz)
88
+ library(SingleCellExperiment)
89
+ library(scran)
90
+ library(igraph)
91
+ library(ggplot2)
92
+
93
+ # --- Get arguments from command line ---
94
+ args <- commandArgs(trailingOnly=TRUE)
95
+ sce_rds_path <- args[1]
96
+ output_plot_path <- args[2]
97
+ cluster_steps_str <- args[3]
98
+ assay_name <- args[4]
99
+ dimred_name <- args[5]
100
+ cluster_prefix <- args[6]
101
+
102
+ cat("--- scTreeViz Analysis Parameters ---\\n")
103
+ cat("Input SCE RDS: ", sce_rds_path, "\\n")
104
+ cat("Output Plot: ", output_plot_path, "\\n")
105
+ cat("Cluster Steps: ", cluster_steps_str, "\\n")
106
+ cat("Assay Name: ", assay_name, "\\n")
107
+ cat("DimRed Name: ", dimred_name, "\\n")
108
+ cat("Cluster Prefix: ", cluster_prefix, "\\n")
109
+ cat("-------------------------------------\\n\\n")
110
+
111
+ # --- Main analysis ---
112
+ tryCatch({{
113
+ # Read input SingleCellExperiment object
114
+ cat("Reading SCE object from:", sce_rds_path, "\\n")
115
+ sce <- readRDS(sce_rds_path)
116
+
117
+ # Check for dimensionality reduction result
118
+ if (!dimred_name %in% reducedDimNames(sce)) {{
119
+ stop(paste0("Dimensionality reduction '", dimred_name, "' not found in the SCE object."))
120
+ }}
121
+
122
+ # Build SNN graph
123
+ cat("Building SNN graph using '", dimred_name, "'...\\n")
124
+ g <- buildSNNGraph(sce, use.dimred=dimred_name, assay.type=assay_name)
125
+
126
+ # Parse cluster steps
127
+ steps_vec <- as.integer(strsplit(cluster_steps_str, ",")[[1]])
128
+
129
+ # Perform clustering for each step value
130
+ cat("Performing clustering for steps:", paste(steps_vec, collapse=", "), "\\n")
131
+ for (s in steps_vec) {{
132
+ set.seed(42) # for reproducibility
133
+ col_name <- paste0(cluster_prefix, "s", s)
134
+ clust <- igraph::cluster_walktrap(g, steps=s)$membership
135
+ sce[[col_name]] <- as.factor(clust)
136
+ }}
137
+
138
+ # Get the names of the cluster columns we just created
139
+ clust_cols <- grep(paste0("^", cluster_prefix), colnames(colData(sce)), value = TRUE)
140
+ if (length(clust_cols) == 0) {{
141
+ stop("No cluster columns were generated. Check cluster_prefix and clustering steps.")
142
+ }}
143
+ cat("Generated cluster columns:", paste(clust_cols, collapse=", "), "\\n")
144
+
145
+ # Create the TreeViz object
146
+ cat("Creating TreeViz object...\\n")
147
+ sctv <- TreeViz(sce, colData_cols = clust_cols)
148
+
149
+ # Generate the clustree plot
150
+ cat("Generating clustree plot...\\n")
151
+ p <- sctv_clustree(sctv, prefix = cluster_prefix)
152
+
153
+ # Save the plot
154
+ cat("Saving plot to:", output_plot_path, "\\n")
155
+ ggsave(output_plot_path, plot = p, width = 10, height = 8, dpi = 300)
156
+
157
+ cat("\\n--- Analysis successfully completed! ---\\n")
158
+
159
+ }}, error = function(e) {{
160
+ cat("Error during scTreeViz analysis:\\n")
161
+ message(e)
162
+ quit(status = 1)
163
+ }})
164
+ """
165
+
166
+ # 3. Subprocess execution
167
+ command = []
168
+ result_dict = {
169
+ "command_executed": "",
170
+ "stdout": "",
171
+ "stderr": "",
172
+ "output_files": [],
173
+ }
174
+
175
+ try:
176
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
177
+ r_script_file.write(r_script_content)
178
+ r_script_path = r_script_file.name
179
+
180
+ command = [
181
+ "Rscript",
182
+ r_script_path,
183
+ str(sce_rds),
184
+ str(output_plot),
185
+ cluster_steps,
186
+ assay_name,
187
+ dimred_name,
188
+ cluster_prefix,
189
+ ]
190
+
191
+ command_str = " ".join(shlex.quote(c) for c in command)
192
+ logger.info(f"Executing command: {command_str}")
193
+ result_dict["command_executed"] = command_str
194
+
195
+ process = subprocess.run(
196
+ command,
197
+ capture_output=True,
198
+ text=True,
199
+ check=True,
200
+ )
201
+
202
+ result_dict["stdout"] = process.stdout
203
+ result_dict["stderr"] = process.stderr
204
+ if output_plot.exists():
205
+ result_dict["output_files"].append(str(output_plot))
206
+
207
+ logger.info(f"Successfully generated cluster tree plot: {output_plot}")
208
+
209
+ except FileNotFoundError:
210
+ error_msg = "Rscript not found. Please ensure R is installed and in your PATH."
211
+ logger.error(error_msg)
212
+ result_dict["stderr"] = error_msg
213
+ raise RuntimeError(error_msg) from None
214
+ except subprocess.CalledProcessError as e:
215
+ logger.error(f"Error executing scTreeViz R script.")
216
+ logger.error(f"Return code: {e.returncode}")
217
+ logger.error(f"Stdout: {e.stdout}")
218
+ logger.error(f"Stderr: {e.stderr}")
219
+ result_dict["stdout"] = e.stdout
220
+ result_dict["stderr"] = e.stderr
221
+ raise
222
+ finally:
223
+ # Clean up the temporary R script
224
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
225
+ Path(r_script_path).unlink()
226
+
227
+ return result_dict
228
+
229
+ if __name__ == "__main__":
230
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_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-sctreeviz/app/bioconductor-sctreeviz_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_sctreeviz'
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-sctreeviz/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-sctreeviz:
5
+ build: .
6
+ image: mcp-bioconductor-sctreeviz:latest
7
+ container_name: mcp-bioconductor-sctreeviz
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-sctreeviz
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-sctreeviz/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_echidna/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 echidna via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda echidna -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/echidna_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/echidna_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/echidna_server.py"]
40
+
Biomni/mcp_generated/mcp_echidna/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - echidna
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_epic/app/epic_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_epic/app/epic_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_epic'
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_epic/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-epic:
5
+ build: .
6
+ image: mcp-epic:latest
7
+ container_name: mcp-epic
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=epic
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_gmap/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 gmap via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda gmap -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/gmap_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/gmap_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/gmap_server.py"]
40
+
Biomni/mcp_generated/mcp_gmap/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-gmap:
5
+ build: .
6
+ image: mcp-gmap:latest
7
+ container_name: mcp-gmap
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=gmap
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_mummer/app/mummer_server.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import List, Optional, Dict, Any
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_mummer'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def mummer_align(
14
+ reference_fasta: Path,
15
+ query_fasta: Path,
16
+ min_match_length: Optional[int] = None,
17
+ report_unique_matches: bool = False,
18
+ break_ties: bool = False,
19
+ report_reverse_complement: bool = False,
20
+ forward_strand_only: bool = False,
21
+ diagonal_output: bool = False,
22
+ output_prefix: Optional[str] = None,
23
+ ) -> Dict[str, Any]:
24
+ """
25
+ Aligns two nucleotide sequences using the 'mummer' executable to find maximal exact matches.
26
+
27
+ This tool finds maximal exact matches between a reference and a query sequence.
28
+ It is part of the MUMmer suite and is typically used for aligning closely related sequences.
29
+ Note: The 'mummer' executable itself does not support a '--help' flag, so parameters are
30
+ inferred from common usage and MUMmer 3.x documentation.
31
+
32
+ Args:
33
+ reference_fasta: Path to the reference FASTA file.
34
+ query_fasta: Path to the query FASTA file.
35
+ min_match_length: Minimum length of a match to report. If None, uses the tool's internal default
36
+ (typically 20 for mummer). Must be a positive integer.
37
+ report_unique_matches: If True, report only maximal unique matches (-mum flag).
38
+ break_ties: If True, break ties by reporting only one match per query position (-b flag).
39
+ report_reverse_complement: If True, report matches on the reverse complement strand of the query (-c flag).
40
+ forward_strand_only: If True, only report matches on the forward strand of the query (-F flag).
41
+ Cannot be used simultaneously with `report_reverse_complement`.
42
+ diagonal_output: If True, output matches in a diagonal format suitable for mummerplot (-D flag).
43
+ output_prefix: Prefix for output files. If provided, mummer will write output to files
44
+ like `<output_prefix>.mums` in a temporary directory.
45
+ If None, output will be directed to stdout.
46
+
47
+ Returns:
48
+ A dictionary containing:
49
+ - command_executed: The command string executed.
50
+ - stdout: Standard output from the mummer command.
51
+ - stderr: Standard error from the mummer command.
52
+ - output_files: A list of paths to any generated output files.
53
+ - error: An error message if the command fails.
54
+ """
55
+ # Input validation
56
+ if not isinstance(reference_fasta, Path) or not reference_fasta.is_file():
57
+ raise FileNotFoundError(f"Reference FASTA file not found or is not a valid path: {reference_fasta}")
58
+ if not isinstance(query_fasta, Path) or not query_fasta.is_file():
59
+ raise FileNotFoundError(f"Query FASTA file not found or is not a valid path: {query_fasta}")
60
+
61
+ if min_match_length is not None:
62
+ if not isinstance(min_match_length, int) or min_match_length <= 0:
63
+ raise ValueError("Minimum match length must be a positive integer.")
64
+
65
+ if forward_strand_only and report_reverse_complement:
66
+ raise ValueError("Cannot use both 'forward_strand_only' and 'report_reverse_complement' simultaneously.")
67
+
68
+ command: List[str] = ["mummer"]
69
+
70
+ if report_unique_matches:
71
+ command.append("-mum")
72
+ if min_match_length is not None:
73
+ command.extend(["-l", str(min_match_length)])
74
+ if break_ties:
75
+ command.append("-b")
76
+ if report_reverse_complement:
77
+ command.append("-c")
78
+ if forward_strand_only:
79
+ command.append("-F")
80
+ if diagonal_output:
81
+ command.append("-D")
82
+
83
+ temp_dir: Optional[Path] = None
84
+ output_files: List[Path] = []
85
+ stdout_content: str = ""
86
+ stderr_content: str = ""
87
+
88
+ try:
89
+ if output_prefix:
90
+ # Create a temporary directory to run mummer and collect its output files
91
+ temp_dir = Path(tempfile.mkdtemp())
92
+ # mummer writes files with the prefix in the current working directory.
93
+ # So, we change the working directory for the subprocess.
94
+ command.extend(["-p", output_prefix])
95
+
96
+ command.extend([str(reference_fasta), str(query_fasta)])
97
+
98
+ process = subprocess.run(
99
+ command,
100
+ cwd=temp_dir if temp_dir else None, # Run in temp_dir if output_prefix is used
101
+ capture_output=True,
102
+ text=True,
103
+ check=True
104
+ )
105
+ stdout_content = process.stdout
106
+ stderr_content = process.stderr
107
+
108
+ if temp_dir:
109
+ # Collect all files generated in the temporary directory
110
+ for f in temp_dir.iterdir():
111
+ output_files.append(f)
112
+
113
+ except subprocess.CalledProcessError as e:
114
+ return {
115
+ "command_executed": " ".join(command),
116
+ "stdout": e.stdout,
117
+ "stderr": e.stderr,
118
+ "output_files": [],
119
+ "error": f"Mummer command failed with exit code {e.returncode}",
120
+ }
121
+ except FileNotFoundError:
122
+ return {
123
+ "command_executed": " ".join(command),
124
+ "stdout": "",
125
+ "stderr": "mummer executable not found. Please ensure it is installed and in your PATH.",
126
+ "output_files": [],
127
+ "error": "Mummer executable not found.",
128
+ }
129
+ finally:
130
+ # Clean up the temporary directory only if it was created and no output files
131
+ # were successfully collected (e.g., due to an error).
132
+ # If output_files are collected, the MCP framework is responsible for
133
+ # moving them out and then cleaning up the temporary directory.
134
+ if temp_dir and not output_files:
135
+ shutil.rmtree(temp_dir)
136
+
137
+ return {
138
+ "command_executed": " ".join(command),
139
+ "stdout": stdout_content,
140
+ "stderr": stderr_content,
141
+ "output_files": output_files,
142
+ }
143
+
144
+ if __name__ == "__main__":
145
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_perl-pod-escapes/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - perl-pod-escapes
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_perl-xml-regexp/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 perl-xml-regexp via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda perl-xml-regexp -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/perl-xml-regexp_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/perl-xml-regexp_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/perl-xml-regexp_server.py"]
40
+
Biomni/mcp_generated/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import List, Optional, Dict, Any
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ SERVER_NAME = 'local_perl_xml_regexp'
8
+ mcp = FastMCP(SERVER_NAME)
9
+
10
+ @mcp.tool()
11
+ def perl_xml_regexp_validate(
12
+ text: str,
13
+ token_type: str = "Name"
14
+ ) -> Dict[str, Any]:
15
+ """
16
+ Validates if a given string matches a specific XML token regular expression
17
+ provided by the XML::RegExp Perl module.
18
+
19
+ Args:
20
+ text: The string to validate against the XML regular expression.
21
+ token_type: The type of XML token to validate against.
22
+ Common values: 'Name', 'NmToken', 'NCName', 'QName', 'AttValue',
23
+ 'EntityValue', 'SystemLiteral', 'PubidLiteral', 'Char', 'S',
24
+ 'VersionNum', 'EncName', 'EncodingDecl', 'VersionInfo', 'Misc',
25
+ 'Comment', 'PI', 'CDSect', 'Reference', 'EntityRef', 'CharRef'.
26
+ """
27
+ # List of common valid tokens in XML::RegExp for validation
28
+ valid_tokens = [
29
+ "Name", "NmToken", "NCName", "QName", "AttValue", "EntityValue",
30
+ "SystemLiteral", "PubidLiteral", "Char", "S", "VersionNum",
31
+ "EncName", "EncodingDecl", "VersionInfo", "Misc", "Comment",
32
+ "PI", "CDSect", "Reference", "EntityRef", "CharRef", "BaseChar",
33
+ "Ideographic", "CombiningChar", "Digit", "Extender", "Letter"
34
+ ]
35
+
36
+ if token_type not in valid_tokens:
37
+ # We allow it but warn or handle it; Perl will throw an error if the variable doesn't exist
38
+ pass
39
+
40
+ # Construct the perl command
41
+ # We use -MXML::RegExp to load the module
42
+ # We use $ARGV[0] for the text and $ARGV[1] for the token type to avoid shell injection
43
+ perl_script = 'use XML::RegExp; my $type = $ARGV[1]; no strict "refs"; if ($ARGV[0] =~ /^${"XML::RegExp::$type"}$/) { print "MATCH"; } else { print "NO_MATCH"; }'
44
+
45
+ cmd = [
46
+ "perl",
47
+ "-MXML::RegExp",
48
+ "-e", perl_script,
49
+ text,
50
+ token_type
51
+ ]
52
+
53
+ try:
54
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
55
+ is_valid = result.stdout.strip() == "MATCH"
56
+
57
+ return {
58
+ "command_executed": " ".join(cmd),
59
+ "is_valid": is_valid,
60
+ "token_type": token_type,
61
+ "input_text": text,
62
+ "stdout": result.stdout,
63
+ "stderr": result.stderr
64
+ }
65
+ except subprocess.CalledProcessError as e:
66
+ return {
67
+ "command_executed": " ".join(cmd),
68
+ "error": str(e),
69
+ "stdout": e.stdout,
70
+ "stderr": e.stderr
71
+ }
72
+
73
+ @mcp.tool()
74
+ def perl_xml_regexp_execute(
75
+ code: str,
76
+ args: Optional[List[str]] = None
77
+ ) -> Dict[str, Any]:
78
+ """
79
+ Executes a custom Perl snippet that utilizes the XML::RegExp module.
80
+ The module is automatically loaded via '-MXML::RegExp'.
81
+
82
+ Args:
83
+ code: The Perl code to execute. Example: 'print $XML::RegExp::Name'
84
+ args: Optional list of command-line arguments to pass to the Perl script.
85
+ """
86
+ cmd_args = args if args is not None else []
87
+
88
+ cmd = [
89
+ "perl",
90
+ "-MXML::RegExp",
91
+ "-e", code,
92
+ "--"
93
+ ] + cmd_args
94
+
95
+ try:
96
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
97
+ return {
98
+ "command_executed": " ".join(cmd),
99
+ "stdout": result.stdout,
100
+ "stderr": result.stderr,
101
+ "status": "success"
102
+ }
103
+ except subprocess.CalledProcessError as e:
104
+ return {
105
+ "command_executed": " ".join(cmd),
106
+ "error": str(e),
107
+ "stdout": e.stdout,
108
+ "stderr": e.stderr,
109
+ "status": "error"
110
+ }
111
+
112
+ @mcp.tool()
113
+ def perl_xml_regexp_info() -> Dict[str, Any]:
114
+ """
115
+ Returns the version information of the installed XML::RegExp module and the Perl interpreter.
116
+ """
117
+ perl_cmd = ["perl", "-v"]
118
+ module_cmd = ["perl", "-MXML::RegExp", "-e", "print $XML::RegExp::VERSION"]
119
+
120
+ try:
121
+ perl_ver = subprocess.run(perl_cmd, capture_output=True, text=True, check=True)
122
+ mod_ver = subprocess.run(module_cmd, capture_output=True, text=True, check=True)
123
+
124
+ return {
125
+ "perl_version_info": perl_ver.stdout.splitlines()[1] if len(perl_ver.stdout.splitlines()) > 1 else perl_ver.stdout,
126
+ "xml_regexp_version": mod_ver.stdout.strip(),
127
+ "status": "success"
128
+ }
129
+ except subprocess.CalledProcessError as e:
130
+ return {
131
+ "error": "Failed to retrieve version information. Ensure perl and perl-xml-regexp are installed.",
132
+ "stderr": e.stderr,
133
+ "status": "error"
134
+ }
135
+
136
+ @mcp.tool()
137
+ def perl_xml_regexp_list_regexes() -> Dict[str, Any]:
138
+ """
139
+ Lists the available regular expression variables provided by the XML::RegExp module.
140
+ """
141
+ # This script inspects the XML::RegExp namespace
142
+ perl_script = (
143
+ 'use XML::RegExp; '
144
+ 'foreach my $entry (sort keys %XML::RegExp::) { '
145
+ ' print "$entry\\n" if $entry =~ /^[A-Z]/; '
146
+ '}'
147
+ )
148
+
149
+ cmd = ["perl", "-MXML::RegExp", "-e", perl_script]
150
+
151
+ try:
152
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
153
+ regex_list = [line.strip() for line in result.stdout.splitlines() if line.strip()]
154
+
155
+ return {
156
+ "available_regex_variables": regex_list,
157
+ "command_executed": " ".join(cmd),
158
+ "stdout": result.stdout,
159
+ "stderr": result.stderr
160
+ }
161
+ except subprocess.CalledProcessError as e:
162
+ return {
163
+ "command_executed": " ".join(cmd),
164
+ "error": str(e),
165
+ "stdout": e.stdout,
166
+ "stderr": e.stderr
167
+ }
168
+
169
+ if __name__ == "__main__":
170
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_perl-xml-regexp/app/perl-xml-regexp_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_perl-xml-regexp/app/perl-xml-regexp_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_perl_xml_regexp'
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_perl-xml-regexp/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_perl-xml-regexp/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-perl-xml-regexp:
5
+ build: .
6
+ image: mcp-perl-xml-regexp:latest
7
+ container_name: mcp-perl-xml-regexp
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=perl-xml-regexp
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_searchgui/app/searchgui_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_searchgui/app/searchgui_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_searchgui'
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_searchgui/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - searchgui
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_sortmerna/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_sortmerna/app/sortmerna_server.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import List, Optional
4
+ import tempfile
5
+ import os
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_sortmerna'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def sortmerna(
14
+ ref: List[str],
15
+ reads: List[str],
16
+ workdir: str = "sortmerna_work",
17
+ kvdb: Optional[str] = None,
18
+ idx_dir: Optional[str] = None,
19
+ threads: int = 1,
20
+ fastx: bool = False,
21
+ aligned: Optional[str] = None,
22
+ other: Optional[str] = None,
23
+ blast: Optional[str] = None,
24
+ num_alignments: int = 1,
25
+ best: int = 1,
26
+ paired_in: bool = False,
27
+ paired_out: bool = False,
28
+ out_order: bool = False,
29
+ readfeed: int = 1,
30
+ verbose: bool = False,
31
+ ):
32
+ """
33
+ SortMeRNA: A biological sequence analysis tool for filtering, mapping and OTU-picking NGS reads.
34
+ Mainly used for filtering rRNA from metatranscriptomic data.
35
+
36
+ Args:
37
+ ref: List of paths to reference database files (e.g. silva-bac-16s.fasta). Required.
38
+ reads: List of paths to read files (fasta/fastq/gz). For paired-end, provide two files. Required.
39
+ workdir: Directory for all temporary and output files.
40
+ kvdb: Directory for RocksDB key-value storage (defaults to workdir/kvdb).
41
+ idx_dir: Directory for reference indexes (defaults to workdir/idx).
42
+ threads: Number of threads to use.
43
+ fastx: If True, output aligned and/or rejected reads in FASTA/FASTQ format.
44
+ aligned: File prefix for aligned (rRNA) reads.
45
+ other: File prefix for rejected (non-rRNA) reads.
46
+ blast: Output alignments in BLAST-like format. Use '0' for pairwise, '1' for tabular.
47
+ num_alignments: Report first N alignments per read.
48
+ best: Report best N alignments per read.
49
+ paired_in: If True, flags paired-end reads as interleaved in a single file.
50
+ paired_out: If True, output paired-end reads into two separate files.
51
+ out_order: If True, maintain the same order of reads in output as in input.
52
+ readfeed: Splitting behavior (v5.0.0+). 1 enables multithreaded parallel processing.
53
+ verbose: Enable verbose output.
54
+ """
55
+ # Input validation
56
+ if not ref:
57
+ return {"error": "At least one reference file (--ref) must be provided."}
58
+ if not reads:
59
+ return {"error": "At least one reads file (--reads) must be provided."}
60
+
61
+ for r in ref:
62
+ if not Path(r).exists():
63
+ return {"error": f"Reference file not found: {r}"}
64
+ for rd in reads:
65
+ if not Path(rd).exists():
66
+ return {"error": f"Reads file not found: {rd}"}
67
+
68
+ if threads < 1:
69
+ threads = 1
70
+
71
+ # Construct command
72
+ cmd = ["sortmerna"]
73
+
74
+ # Add multiple references
75
+ for r in ref:
76
+ cmd.extend(["--ref", str(Path(r).absolute())])
77
+
78
+ # Add multiple reads
79
+ for rd in reads:
80
+ cmd.extend(["--reads", str(Path(rd).absolute())])
81
+
82
+ # Paths
83
+ cmd.extend(["--workdir", str(Path(workdir).absolute())])
84
+ if kvdb:
85
+ cmd.extend(["--kvdb", str(Path(kvdb).absolute())])
86
+ if idx_dir:
87
+ cmd.extend(["--idx-dir", str(Path(idx_dir).absolute())])
88
+
89
+ # Options
90
+ cmd.extend(["--threads", str(threads)])
91
+ cmd.extend(["--num_alignments", str(num_alignments)])
92
+ cmd.extend(["--best", str(best)])
93
+ cmd.extend(["--readfeed", str(readfeed)])
94
+
95
+ if fastx:
96
+ cmd.append("--fastx")
97
+ if aligned:
98
+ cmd.extend(["--aligned", aligned])
99
+ if other:
100
+ cmd.extend(["--other", other])
101
+ if blast is not None:
102
+ cmd.extend(["--blast", str(blast)])
103
+ if paired_in:
104
+ cmd.append("--paired_in")
105
+ if paired_out:
106
+ cmd.append("--paired_out")
107
+ if out_order:
108
+ cmd.append("--out_order")
109
+ if verbose:
110
+ cmd.append("-v")
111
+
112
+ try:
113
+ # Ensure workdir exists
114
+ Path(workdir).mkdir(parents=True, exist_ok=True)
115
+
116
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
117
+
118
+ # Collect output files from workdir/out
119
+ output_path = Path(workdir) / "out"
120
+ output_files = []
121
+ if output_path.exists():
122
+ output_files = [str(f) for f in output_path.glob("*") if f.is_file()]
123
+
124
+ return {
125
+ "command_executed": " ".join(cmd),
126
+ "stdout": result.stdout,
127
+ "stderr": result.stderr,
128
+ "output_files": output_files,
129
+ "status": "success"
130
+ }
131
+ except subprocess.CalledProcessError as e:
132
+ return {
133
+ "command_executed": " ".join(cmd),
134
+ "stdout": e.stdout,
135
+ "stderr": e.stderr,
136
+ "error": str(e),
137
+ "status": "failed"
138
+ }
139
+ except Exception as e:
140
+ return {"error": str(e), "status": "failed"}
141
+
142
+ @mcp.tool()
143
+ def sortmerna_split(
144
+ files: List[str],
145
+ num_splits: int = 8,
146
+ workdir: str = "sortmerna_split_out",
147
+ ):
148
+ """
149
+ Utility to perform multithreaded splitting of large sequence files prior to SortMeRNA processing.
150
+ Uses rapidgzip and pigz internally (if available in the environment).
151
+
152
+ Args:
153
+ files: List of sequence files to split.
154
+ num_splits: Number of chunks to split the files into.
155
+ workdir: Working directory to store split chunks and descriptors.
156
+ """
157
+ if not files:
158
+ return {"error": "No files provided for splitting."}
159
+
160
+ for f in files:
161
+ if not Path(f).exists():
162
+ return {"error": f"File not found: {f}"}
163
+
164
+ if num_splits < 1:
165
+ num_splits = 1
166
+
167
+ # Based on the documentation: python run.py split --file <f> --num-splits <n> --workdir <w>
168
+ # Note: This assumes 'run.py' is available or the logic is accessible via the sortmerna environment.
169
+ # Since run.py is a helper script in the repo, we attempt to call it via python.
170
+
171
+ cmd = ["python", "run.py", "split"]
172
+ for f in files:
173
+ cmd.extend(["--file", str(Path(f).absolute())])
174
+
175
+ cmd.extend(["--num-splits", str(num_splits)])
176
+ cmd.extend(["--workdir", str(Path(workdir).absolute())])
177
+
178
+ try:
179
+ Path(workdir).mkdir(parents=True, exist_ok=True)
180
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
181
+
182
+ output_files = [str(f) for f in Path(workdir).glob("*") if f.is_file()]
183
+
184
+ return {
185
+ "command_executed": " ".join(cmd),
186
+ "stdout": result.stdout,
187
+ "stderr": result.stderr,
188
+ "output_files": output_files,
189
+ "status": "success"
190
+ }
191
+ except subprocess.CalledProcessError as e:
192
+ return {
193
+ "command_executed": " ".join(cmd),
194
+ "stdout": e.stdout,
195
+ "stderr": e.stderr,
196
+ "error": "Ensure run.py from the SortMeRNA repository is in the current directory or PYTHONPATH.",
197
+ "status": "failed"
198
+ }
199
+ except Exception as e:
200
+ return {"error": str(e), "status": "failed"}
201
+
202
+ @mcp.tool()
203
+ def sortmerna_version():
204
+ """Check the installed version of SortMeRNA."""
205
+ try:
206
+ result = subprocess.run(["sortmerna", "--version"], capture_output=True, text=True, check=True)
207
+ return {
208
+ "command_executed": "sortmerna --version",
209
+ "stdout": result.stdout,
210
+ "stderr": result.stderr
211
+ }
212
+ except subprocess.CalledProcessError as e:
213
+ return {"error": str(e), "stderr": e.stderr}
214
+
215
+ if __name__ == "__main__":
216
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_sortmerna/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp