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

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Biomni/mcp_generated/mcp_abnumber/app/abnumber_server.py +124 -0
  2. Biomni/mcp_generated/mcp_abnumber/app/abnumber_shim_server.py +55 -0
  3. Biomni/mcp_generated/mcp_abnumber/app/requirements.txt +1 -0
  4. Biomni/mcp_generated/mcp_abnumber/environment.yaml +10 -0
  5. Biomni/mcp_generated/mcp_abnumber/requirements.txt +2 -0
  6. Biomni/mcp_generated/mcp_abricate/Dockerfile +40 -0
  7. Biomni/mcp_generated/mcp_abricate/app/abricate_server.py +312 -0
  8. Biomni/mcp_generated/mcp_abricate/app/abricate_shim_server.py +55 -0
  9. Biomni/mcp_generated/mcp_abricate/app/requirements.txt +1 -0
  10. Biomni/mcp_generated/mcp_abricate/docker-compose.yml +22 -0
  11. Biomni/mcp_generated/mcp_abricate/environment.yaml +10 -0
  12. Biomni/mcp_generated/mcp_abricate/requirements.txt +2 -0
  13. Biomni/mcp_generated/mcp_art/Dockerfile +40 -0
  14. Biomni/mcp_generated/mcp_art/app/art_server.py +302 -0
  15. Biomni/mcp_generated/mcp_art/app/art_shim_server.py +55 -0
  16. Biomni/mcp_generated/mcp_art/app/requirements.txt +1 -0
  17. Biomni/mcp_generated/mcp_art/docker-compose.yml +22 -0
  18. Biomni/mcp_generated/mcp_art/environment.yaml +10 -0
  19. Biomni/mcp_generated/mcp_art/requirements.txt +2 -0
  20. Biomni/mcp_generated/mcp_augustus/Dockerfile +40 -0
  21. Biomni/mcp_generated/mcp_augustus/app/augustus_server.py +149 -0
  22. Biomni/mcp_generated/mcp_augustus/app/augustus_shim_server.py +55 -0
  23. Biomni/mcp_generated/mcp_augustus/docker-compose.yml +22 -0
  24. Biomni/mcp_generated/mcp_augustus/environment.yaml +10 -0
  25. Biomni/mcp_generated/mcp_augustus/requirements.txt +2 -0
  26. Biomni/mcp_generated/mcp_bactopia/Dockerfile +40 -0
  27. Biomni/mcp_generated/mcp_bactopia/app/bactopia_server.py +287 -0
  28. Biomni/mcp_generated/mcp_bactopia/app/bactopia_shim_server.py +55 -0
  29. Biomni/mcp_generated/mcp_bactopia/app/requirements.txt +1 -0
  30. Biomni/mcp_generated/mcp_bactopia/docker-compose.yml +22 -0
  31. Biomni/mcp_generated/mcp_bactopia/environment.yaml +10 -0
  32. Biomni/mcp_generated/mcp_bactopia/requirements.txt +2 -0
  33. Biomni/mcp_generated/mcp_bedops/Dockerfile +40 -0
  34. Biomni/mcp_generated/mcp_bedops/app/bedops_server.py +827 -0
  35. Biomni/mcp_generated/mcp_bedops/app/bedops_shim_server.py +55 -0
  36. Biomni/mcp_generated/mcp_bedops/app/requirements.txt +1 -0
  37. Biomni/mcp_generated/mcp_bedops/docker-compose.yml +22 -0
  38. Biomni/mcp_generated/mcp_bedops/environment.yaml +10 -0
  39. Biomni/mcp_generated/mcp_bedops/requirements.txt +2 -0
  40. Biomni/mcp_generated/mcp_biobambam/Dockerfile +40 -0
  41. Biomni/mcp_generated/mcp_biobambam/app/biobambam_server.py +497 -0
  42. Biomni/mcp_generated/mcp_biobambam/app/biobambam_shim_server.py +55 -0
  43. Biomni/mcp_generated/mcp_biobambam/app/requirements.txt +1 -0
  44. Biomni/mcp_generated/mcp_biobambam/docker-compose.yml +22 -0
  45. Biomni/mcp_generated/mcp_biobambam/environment.yaml +10 -0
  46. Biomni/mcp_generated/mcp_biobambam/requirements.txt +2 -0
  47. Biomni/mcp_generated/mcp_bioconductor-affyio/Dockerfile +40 -0
  48. Biomni/mcp_generated/mcp_bioconductor-affyio/app/bioconductor-affyio_server.py +216 -0
  49. Biomni/mcp_generated/mcp_bioconductor-affyio/app/bioconductor-affyio_shim_server.py +55 -0
  50. Biomni/mcp_generated/mcp_bioconductor-affyio/app/requirements.txt +1 -0
Biomni/mcp_generated/mcp_abnumber/app/abnumber_server.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Literal
4
+
5
+ # from mcp import tool
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_abnumber'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def abnumber(
14
+ input_file: Path,
15
+ outfile: Optional[Path] = None,
16
+ scheme: Literal['imgt', 'kabat', 'chothia', 'contact', 'aho', 'martin'] = 'imgt',
17
+ chain_type: Literal['H', 'K', 'L'] = 'H',
18
+ header: bool = False,
19
+ regions: bool = False,
20
+ seq: bool = False,
21
+ scheme_out: bool = False,
22
+ chain_out: bool = False,
23
+ species_out: bool = False,
24
+ gene_out: bool = False,
25
+ score_out: bool = False,
26
+ bitscore_out: bool = False,
27
+ evalue_out: bool = False,
28
+ ):
29
+ """
30
+ Number antibody sequences from a FASTA file using ANARCI.
31
+
32
+ This tool is a command-line wrapper for the AbNumber Python library, which
33
+ uses ANARCI for antibody numbering. It takes a FASTA file as input and
34
+ produces a table with numbered sequences and other annotations.
35
+
36
+ Args:
37
+ input_file: FASTA file with sequences to number.
38
+ outfile: Output file path. If not provided, output is sent to stdout.
39
+ scheme: Numbering scheme to use.
40
+ chain_type: Chain type to assign if not determined by ANARCI.
41
+ header: Print a header in the output table.
42
+ regions: Print CDR/FR regions instead of the numbered sequence.
43
+ seq: Print the original sequence in the output.
44
+ scheme_out: Print the numbering scheme in the output.
45
+ chain_out: Print the chain type in the output.
46
+ species_out: Print the species in the output.
47
+ gene_out: Print the V/J genes in the output.
48
+ score_out: Print the ANARCI score in the output.
49
+ bitscore_out: Print the ANARCI bitscore in the output.
50
+ evalue_out: Print the ANARCI E-value in the output.
51
+ """
52
+ # Input validation
53
+ if not input_file.is_file():
54
+ raise FileNotFoundError(f"Input file not found: {input_file}")
55
+
56
+ # Command construction
57
+ cmd = ["abnumber", str(input_file)]
58
+
59
+ # Add optional arguments
60
+ cmd.extend(["--scheme", scheme])
61
+ cmd.extend(["--chain_type", chain_type])
62
+
63
+ if outfile:
64
+ cmd.extend(["--outfile", str(outfile)])
65
+
66
+ # Add boolean flags
67
+ if header:
68
+ cmd.append("--header")
69
+ if regions:
70
+ cmd.append("--regions")
71
+ if seq:
72
+ cmd.append("--seq")
73
+ if scheme_out:
74
+ cmd.append("--scheme-out")
75
+ if chain_out:
76
+ cmd.append("--chain-out")
77
+ if species_out:
78
+ cmd.append("--species-out")
79
+ if gene_out:
80
+ cmd.append("--gene-out")
81
+ if score_out:
82
+ cmd.append("--score-out")
83
+ if bitscore_out:
84
+ cmd.append("--bitscore-out")
85
+ if evalue_out:
86
+ cmd.append("--evalue-out")
87
+
88
+ command_executed = " ".join(cmd)
89
+
90
+ # Subprocess execution
91
+ try:
92
+ result = subprocess.run(
93
+ cmd,
94
+ capture_output=True,
95
+ text=True,
96
+ check=True
97
+ )
98
+ except FileNotFoundError:
99
+ return {
100
+ "command_executed": command_executed,
101
+ "stdout": "",
102
+ "stderr": "Error: 'abnumber' command not found. Ensure the tool is installed and in your system's PATH.",
103
+ "output_files": []
104
+ }
105
+ except subprocess.CalledProcessError as e:
106
+ return {
107
+ "command_executed": command_executed,
108
+ "stdout": e.stdout,
109
+ "stderr": e.stderr,
110
+ "output_files": []
111
+ }
112
+
113
+ # Structured result return
114
+ output_files = [str(outfile)] if outfile else []
115
+
116
+ return {
117
+ "command_executed": command_executed,
118
+ "stdout": result.stdout,
119
+ "stderr": result.stderr,
120
+ "output_files": output_files
121
+ }
122
+
123
+ if __name__ == "__main__":
124
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_abnumber/app/abnumber_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_abnumber/app/abnumber_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_abnumber'
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_abnumber/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_abnumber/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - abnumber
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_abnumber/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_abricate/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 abricate via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda abricate -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/abricate_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/abricate_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/abricate_server.py"]
40
+
Biomni/mcp_generated/mcp_abricate/app/abricate_server.py ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import subprocess
3
+ from pathlib import Path
4
+ from typing import List, Optional, Dict, Any
5
+
6
+ # Configure logging
7
+ logging.basicConfig(level=logging.INFO)
8
+ log = logging.getLogger(__name__)
9
+
10
+ # MCP decorator is not defined here, but the functions are structured
11
+ # to be compatible with it.
12
+ class mcp:
13
+ @staticmethod
14
+ def tool():
15
+ def decorator(f):
16
+ return f
17
+ return decorator
18
+
19
+ from mcp.server.fastmcp import FastMCP
20
+
21
+ SERVER_NAME = 'local_abricate'
22
+ mcp = FastMCP(SERVER_NAME)
23
+
24
+ @mcp.tool()
25
+ def abricate_run(
26
+ contigs: List[Path],
27
+ db: str = "resfinder",
28
+ minid: float = 80.0,
29
+ mincov: float = 80.0,
30
+ threads: int = 1,
31
+ report: Optional[Path] = None,
32
+ summary: Optional[Path] = None,
33
+ json_output: Optional[Path] = None,
34
+ gff: Optional[Path] = None,
35
+ fofn: Optional[Path] = None,
36
+ datadir: Optional[Path] = None,
37
+ mismatches: Optional[int] = None,
38
+ minlen: Optional[int] = None,
39
+ quiet: bool = False,
40
+ debug: bool = False,
41
+ nopathogen: bool = False,
42
+ csv: bool = False,
43
+ noheader: bool = False,
44
+ all_genes: bool = False,
45
+ nukem: bool = False,
46
+ long_report: bool = False,
47
+ pretty: bool = False,
48
+ agrvate: bool = False,
49
+ roary: bool = False,
50
+ prokka: bool = False,
51
+ plasmid: bool = False,
52
+ resistance: bool = False,
53
+ virulence: bool = False,
54
+ locus: bool = False,
55
+ seqid: bool = False,
56
+ ) -> Dict[str, Any]:
57
+ """
58
+ Mass screen contigs for antimicrobial resistance or virulence genes using Abricate.
59
+
60
+ This is the main analysis function of Abricate. It takes one or more FASTA files
61
+ and screens them against a specified database.
62
+ """
63
+ # --- Input Validation ---
64
+ if not contigs and not fofn:
65
+ raise ValueError("Either 'contigs' (a list of FASTA files) or 'fofn' (a file of FASTA paths) must be provided.")
66
+ if contigs and fofn:
67
+ raise ValueError("Provide either 'contigs' or 'fofn', but not both.")
68
+
69
+ if contigs:
70
+ for file_path in contigs:
71
+ if not file_path.exists():
72
+ raise FileNotFoundError(f"Input contig file not found: {file_path}")
73
+
74
+ if fofn and not fofn.exists():
75
+ raise FileNotFoundError(f"Input FOFN file not found: {fofn}")
76
+
77
+ if not 0.0 <= minid <= 100.0:
78
+ raise ValueError(f"'minid' must be between 0.0 and 100.0, but got {minid}")
79
+ if not 0.0 <= mincov <= 100.0:
80
+ raise ValueError(f"'mincov' must be between 0.0 and 100.0, but got {mincov}")
81
+ if threads < 1:
82
+ raise ValueError(f"'threads' must be a positive integer, but got {threads}")
83
+ if mismatches is not None and mismatches < 0:
84
+ raise ValueError(f"'mismatches' cannot be negative, but got {mismatches}")
85
+ if minlen is not None and minlen < 0:
86
+ raise ValueError(f"'minlen' cannot be negative, but got {minlen}")
87
+
88
+ # --- Command Construction ---
89
+ cmd = ["abricate"]
90
+ output_files = []
91
+
92
+ # Add options with values
93
+ cmd.extend(["--db", db])
94
+ cmd.extend(["--minid", str(minid)])
95
+ cmd.extend(["--mincov", str(mincov)])
96
+ cmd.extend(["--threads", str(threads)])
97
+
98
+ if report:
99
+ cmd.extend(["--report", str(report)])
100
+ output_files.append(str(report))
101
+ if summary:
102
+ cmd.extend(["--summary", str(summary)])
103
+ output_files.append(str(summary))
104
+ if json_output:
105
+ cmd.extend(["--json", str(json_output)])
106
+ output_files.append(str(json_output))
107
+ if gff:
108
+ cmd.extend(["--gff", str(gff)])
109
+ output_files.append(str(gff))
110
+ if datadir:
111
+ cmd.extend(["--datadir", str(datadir)])
112
+ if mismatches is not None:
113
+ cmd.extend(["--mismatches", str(mismatches)])
114
+ if minlen is not None:
115
+ cmd.extend(["--minlen", str(minlen)])
116
+ if fofn:
117
+ cmd.extend(["--fofn", str(fofn)])
118
+
119
+ # Add boolean flags
120
+ if quiet: cmd.append("--quiet")
121
+ if debug: cmd.append("--debug")
122
+ if nopathogen: cmd.append("--nopathogen")
123
+ if csv: cmd.append("--csv")
124
+ if noheader: cmd.append("--noheader")
125
+ if all_genes: cmd.append("--all")
126
+ if nukem: cmd.append("--nukem")
127
+ if long_report: cmd.append("--long")
128
+ if pretty: cmd.append("--pretty")
129
+ if agrvate: cmd.append("--agrvate")
130
+ if roary: cmd.append("--roary")
131
+ if prokka: cmd.append("--prokka")
132
+ if plasmid: cmd.append("--plasmid")
133
+ if resistance: cmd.append("--resistance")
134
+ if virulence: cmd.append("--virulence")
135
+ if locus: cmd.append("--locus")
136
+ if seqid: cmd.append("--seqid")
137
+
138
+ # Add positional arguments (input files)
139
+ if contigs:
140
+ cmd.extend([str(p) for p in contigs])
141
+
142
+ # --- Subprocess Execution ---
143
+ command_executed = " ".join(cmd)
144
+ log.info(f"Executing command: {command_executed}")
145
+
146
+ try:
147
+ result = subprocess.run(
148
+ cmd,
149
+ capture_output=True,
150
+ text=True,
151
+ check=True,
152
+ )
153
+ return {
154
+ "command_executed": command_executed,
155
+ "stdout": result.stdout,
156
+ "stderr": result.stderr,
157
+ "output_files": output_files,
158
+ }
159
+ except FileNotFoundError:
160
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
161
+ except subprocess.CalledProcessError as e:
162
+ log.error(f"Abricate execution failed with exit code {e.returncode}")
163
+ log.error(f"STDOUT: {e.stdout}")
164
+ log.error(f"STDERR: {e.stderr}")
165
+ raise RuntimeError(f"Abricate failed: {e.stderr}")
166
+
167
+
168
+ @mcp.tool()
169
+ def abricate_list_databases(
170
+ datadir: Optional[Path] = None,
171
+ quiet: bool = False,
172
+ debug: bool = False
173
+ ) -> Dict[str, Any]:
174
+ """Lists all available abricate databases."""
175
+ cmd = ["abricate", "--list"]
176
+ if datadir:
177
+ cmd.extend(["--datadir", str(datadir)])
178
+ if quiet:
179
+ cmd.append("--quiet")
180
+ if debug:
181
+ cmd.append("--debug")
182
+
183
+ command_executed = " ".join(cmd)
184
+ log.info(f"Executing command: {command_executed}")
185
+
186
+ try:
187
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
188
+ return {
189
+ "command_executed": command_executed,
190
+ "stdout": result.stdout,
191
+ "stderr": result.stderr,
192
+ "output_files": [],
193
+ }
194
+ except FileNotFoundError:
195
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
196
+ except subprocess.CalledProcessError as e:
197
+ raise RuntimeError(f"Abricate failed to list databases: {e.stderr}")
198
+
199
+
200
+ @mcp.tool()
201
+ def abricate_check_databases(
202
+ datadir: Optional[Path] = None,
203
+ quiet: bool = False,
204
+ debug: bool = False
205
+ ) -> Dict[str, Any]:
206
+ """Checks if the abricate databases are installed correctly."""
207
+ cmd = ["abricate", "--check"]
208
+ if datadir:
209
+ cmd.extend(["--datadir", str(datadir)])
210
+ if quiet:
211
+ cmd.append("--quiet")
212
+ if debug:
213
+ cmd.append("--debug")
214
+
215
+ command_executed = " ".join(cmd)
216
+ log.info(f"Executing command: {command_executed}")
217
+
218
+ try:
219
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
220
+ return {
221
+ "command_executed": command_executed,
222
+ "stdout": result.stdout,
223
+ "stderr": result.stderr,
224
+ "output_files": [],
225
+ }
226
+ except FileNotFoundError:
227
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
228
+ except subprocess.CalledProcessError as e:
229
+ raise RuntimeError(f"Abricate database check failed: {e.stderr}")
230
+
231
+
232
+ @mcp.tool()
233
+ def abricate_setup_databases(
234
+ datadir: Optional[Path] = None,
235
+ threads: int = 1,
236
+ quiet: bool = False,
237
+ debug: bool = False
238
+ ) -> Dict[str, Any]:
239
+ """Downloads and sets up all the abricate databases."""
240
+ if threads < 1:
241
+ raise ValueError(f"'threads' must be a positive integer, but got {threads}")
242
+
243
+ cmd = ["abricate", "--setupdb"]
244
+ if datadir:
245
+ cmd.extend(["--datadir", str(datadir)])
246
+ if threads > 1:
247
+ cmd.extend(["--threads", str(threads)])
248
+ if quiet:
249
+ cmd.append("--quiet")
250
+ if debug:
251
+ cmd.append("--debug")
252
+
253
+ command_executed = " ".join(cmd)
254
+ log.info(f"Executing command: {command_executed}")
255
+
256
+ try:
257
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
258
+ return {
259
+ "command_executed": command_executed,
260
+ "stdout": result.stdout,
261
+ "stderr": result.stderr,
262
+ "output_files": [],
263
+ }
264
+ except FileNotFoundError:
265
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
266
+ except subprocess.CalledProcessError as e:
267
+ raise RuntimeError(f"Abricate database setup failed: {e.stderr}")
268
+
269
+
270
+ @mcp.tool()
271
+ def abricate_get_version() -> Dict[str, Any]:
272
+ """Prints the abricate version."""
273
+ cmd = ["abricate", "--version"]
274
+ command_executed = " ".join(cmd)
275
+ log.info(f"Executing command: {command_executed}")
276
+
277
+ try:
278
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
279
+ return {
280
+ "command_executed": command_executed,
281
+ "stdout": result.stdout,
282
+ "stderr": result.stderr,
283
+ "output_files": [],
284
+ }
285
+ except FileNotFoundError:
286
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
287
+ except subprocess.CalledProcessError as e:
288
+ raise RuntimeError(f"Abricate version check failed: {e.stderr}")
289
+
290
+
291
+ @mcp.tool()
292
+ def abricate_get_citation() -> Dict[str, Any]:
293
+ """Prints the citation for abricate."""
294
+ cmd = ["abricate", "--citation"]
295
+ command_executed = " ".join(cmd)
296
+ log.info(f"Executing command: {command_executed}")
297
+
298
+ try:
299
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
300
+ return {
301
+ "command_executed": command_executed,
302
+ "stdout": result.stdout,
303
+ "stderr": result.stderr,
304
+ "output_files": [],
305
+ }
306
+ except FileNotFoundError:
307
+ raise RuntimeError("abricate command not found. Please ensure it is in your PATH.")
308
+ except subprocess.CalledProcessError as e:
309
+ raise RuntimeError(f"Abricate citation check failed: {e.stderr}")
310
+
311
+ if __name__ == "__main__":
312
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_abricate/app/abricate_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_abricate/app/abricate_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_abricate'
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_abricate/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_abricate/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-abricate:
5
+ build: .
6
+ image: mcp-abricate:latest
7
+ container_name: mcp-abricate
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=abricate
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_abricate/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - abricate
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_abricate/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_art/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 art via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda art -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/art_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/art_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/art_server.py"]
40
+
Biomni/mcp_generated/mcp_art/app/art_server.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Dict, Any
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ SERVER_NAME = 'local_art'
8
+ mcp = FastMCP(SERVER_NAME)
9
+
10
+ @mcp.tool()
11
+ def art_illumina(
12
+ input_fasta: str,
13
+ output_prefix: str,
14
+ read_len: int,
15
+ fold_coverage: Optional[float] = None,
16
+ read_count: Optional[int] = None,
17
+ paired: bool = False,
18
+ mean_frag_len: Optional[float] = None,
19
+ std_dev: Optional[float] = None,
20
+ seq_sys: Optional[str] = None,
21
+ no_aln: bool = False,
22
+ sam_output: bool = False,
23
+ random_seed: Optional[int] = None,
24
+ id_prefix: Optional[str] = None,
25
+ q_shift: Optional[float] = None,
26
+ q_shift2: Optional[float] = None,
27
+ error_free: bool = False,
28
+ cigar_m: bool = False,
29
+ quiet: bool = False
30
+ ):
31
+ """
32
+ ART_Illumina: Simulation of Illumina Next-Generation Sequencing Reads.
33
+
34
+ Args:
35
+ input_fasta: The name of DNA reference format file (FASTA).
36
+ output_prefix: The prefix of output files.
37
+ read_len: The length of reads to be simulated.
38
+ fold_coverage: The fold of read coverage to be simulated.
39
+ read_count: The number of reads to be simulated (alternative to fold_coverage).
40
+ paired: Indicate a paired-end read simulation.
41
+ mean_frag_len: The mean size of DNA fragments for paired-end simulations.
42
+ std_dev: The standard deviation of DNA fragment size for paired-end simulations.
43
+ seq_sys: The sequencing system (e.g., 'HS20', 'HS25', 'HSXn', 'MSv1', 'MSv3', 'NS50').
44
+ no_aln: Do not output alignment file.
45
+ sam_output: Generate SAM alignment file.
46
+ random_seed: The seed for random number generator.
47
+ id_prefix: The prefix of read ID.
48
+ q_shift: The amount to shift every quality score for read 1.
49
+ q_shift2: The amount to shift every quality score for read 2.
50
+ error_free: Generate error-free reads.
51
+ cigar_m: Use M instead of =/X in SAM CIGAR strings.
52
+ quiet: Do not print log messages.
53
+ """
54
+ # Input validation
55
+ input_path = Path(input_fasta)
56
+ if not input_path.exists():
57
+ return {"error": f"Input FASTA file not found: {input_fasta}"}
58
+
59
+ if fold_coverage is None and read_count is None:
60
+ return {"error": "Either fold_coverage (-f) or read_count (-n) must be specified."}
61
+
62
+ cmd = ["art_illumina", "-i", str(input_path), "-o", output_prefix, "-l", str(read_len)]
63
+
64
+ if fold_coverage is not None:
65
+ cmd.extend(["-f", str(fold_coverage)])
66
+ if read_count is not None:
67
+ cmd.extend(["-n", str(read_count)])
68
+
69
+ if paired:
70
+ cmd.append("-p")
71
+ if mean_frag_len is not None:
72
+ cmd.extend(["-m", str(mean_frag_len)])
73
+ if std_dev is not None:
74
+ cmd.extend(["-s", str(std_dev)])
75
+
76
+ if seq_sys:
77
+ cmd.extend(["-ss", seq_sys])
78
+ if no_aln:
79
+ cmd.append("-na")
80
+ if sam_output:
81
+ cmd.append("-sam")
82
+ if random_seed is not None:
83
+ cmd.extend(["-rs", str(random_seed)])
84
+ if id_prefix:
85
+ cmd.extend(["-id", id_prefix])
86
+ if q_shift is not None:
87
+ cmd.extend(["-qs", str(q_shift)])
88
+ if q_shift2 is not None:
89
+ cmd.extend(["-qs2", str(q_shift2)])
90
+ if error_free:
91
+ cmd.append("-ef")
92
+ if cigar_m:
93
+ cmd.append("-M")
94
+ if quiet:
95
+ cmd.append("-q")
96
+
97
+ try:
98
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
99
+
100
+ # Identify output files based on prefix
101
+ output_files = list(Path(".").glob(f"{output_prefix}*"))
102
+
103
+ return {
104
+ "command_executed": " ".join(cmd),
105
+ "stdout": result.stdout,
106
+ "stderr": result.stderr,
107
+ "output_files": [str(f) for f in output_files]
108
+ }
109
+ except subprocess.CalledProcessError as e:
110
+ return {
111
+ "command_executed": " ".join(cmd),
112
+ "error": str(e),
113
+ "stdout": e.stdout,
114
+ "stderr": e.stderr
115
+ }
116
+
117
+ @mcp.tool()
118
+ def art_454(
119
+ input_fasta: str,
120
+ output_prefix: str,
121
+ read_len: int,
122
+ fold_coverage: Optional[float] = None,
123
+ read_count: Optional[int] = None,
124
+ paired: bool = False,
125
+ mean_frag_len: Optional[float] = None,
126
+ std_dev: Optional[float] = None,
127
+ random_seed: Optional[int] = None,
128
+ quiet: bool = False
129
+ ):
130
+ """
131
+ ART_454: Simulation of 454 Next-Generation Sequencing Reads.
132
+
133
+ Args:
134
+ input_fasta: The name of DNA reference format file (FASTA).
135
+ output_prefix: The prefix of output files.
136
+ read_len: The length of reads to be simulated.
137
+ fold_coverage: The fold of read coverage to be simulated.
138
+ read_count: The number of reads to be simulated (alternative to fold_coverage).
139
+ paired: Indicate a paired-end read simulation.
140
+ mean_frag_len: The mean size of DNA fragments for paired-end simulations.
141
+ std_dev: The standard deviation of DNA fragment size for paired-end simulations.
142
+ random_seed: The seed for random number generator.
143
+ quiet: Do not print log messages.
144
+ """
145
+ input_path = Path(input_fasta)
146
+ if not input_path.exists():
147
+ return {"error": f"Input FASTA file not found: {input_fasta}"}
148
+
149
+ if fold_coverage is None and read_count is None:
150
+ return {"error": "Either fold_coverage (-f) or read_count (-n) must be specified."}
151
+
152
+ cmd = ["art_454", "-i", str(input_path), "-o", output_prefix, "-l", str(read_len)]
153
+
154
+ if fold_coverage is not None:
155
+ cmd.extend(["-f", str(fold_coverage)])
156
+ if read_count is not None:
157
+ cmd.extend(["-n", str(read_count)])
158
+
159
+ if paired:
160
+ cmd.append("-p")
161
+ if mean_frag_len is not None:
162
+ cmd.extend(["-m", str(mean_frag_len)])
163
+ if std_dev is not None:
164
+ cmd.extend(["-s", str(std_dev)])
165
+
166
+ if random_seed is not None:
167
+ cmd.extend(["-r", str(random_seed)])
168
+ if quiet:
169
+ cmd.append("-q")
170
+
171
+ try:
172
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
173
+ output_files = list(Path(".").glob(f"{output_prefix}*"))
174
+ return {
175
+ "command_executed": " ".join(cmd),
176
+ "stdout": result.stdout,
177
+ "stderr": result.stderr,
178
+ "output_files": [str(f) for f in output_files]
179
+ }
180
+ except subprocess.CalledProcessError as e:
181
+ return {
182
+ "command_executed": " ".join(cmd),
183
+ "error": str(e),
184
+ "stdout": e.stdout,
185
+ "stderr": e.stderr
186
+ }
187
+
188
+ @mcp.tool()
189
+ def art_solid(
190
+ input_fasta: str,
191
+ output_prefix: str,
192
+ read_len: int,
193
+ fold_coverage: Optional[float] = None,
194
+ read_count: Optional[int] = None,
195
+ paired: bool = False,
196
+ mean_frag_len: Optional[float] = None,
197
+ std_dev: Optional[float] = None,
198
+ random_seed: Optional[int] = None,
199
+ quiet: bool = False
200
+ ):
201
+ """
202
+ ART_SOLiD: Simulation of Applied Biosystems SOLiD Sequencing Reads.
203
+
204
+ Args:
205
+ input_fasta: The name of DNA reference format file (FASTA).
206
+ output_prefix: The prefix of output files.
207
+ read_len: The length of reads to be simulated.
208
+ fold_coverage: The fold of read coverage to be simulated.
209
+ read_count: The number of reads to be simulated (alternative to fold_coverage).
210
+ paired: Indicate a paired-end read simulation.
211
+ mean_frag_len: The mean size of DNA fragments for paired-end simulations.
212
+ std_dev: The standard deviation of DNA fragment size for paired-end simulations.
213
+ random_seed: The seed for random number generator.
214
+ quiet: Do not print log messages.
215
+ """
216
+ input_path = Path(input_fasta)
217
+ if not input_path.exists():
218
+ return {"error": f"Input FASTA file not found: {input_fasta}"}
219
+
220
+ if fold_coverage is None and read_count is None:
221
+ return {"error": "Either fold_coverage (-f) or read_count (-n) must be specified."}
222
+
223
+ cmd = ["art_solid", "-i", str(input_path), "-o", output_prefix, "-l", str(read_len)]
224
+
225
+ if fold_coverage is not None:
226
+ cmd.extend(["-f", str(fold_coverage)])
227
+ if read_count is not None:
228
+ cmd.extend(["-n", str(read_count)])
229
+
230
+ if paired:
231
+ cmd.append("-p")
232
+ if mean_frag_len is not None:
233
+ cmd.extend(["-m", str(mean_frag_len)])
234
+ if std_dev is not None:
235
+ cmd.extend(["-s", str(std_dev)])
236
+
237
+ if random_seed is not None:
238
+ cmd.extend(["-r", str(random_seed)])
239
+ if quiet:
240
+ cmd.append("-q")
241
+
242
+ try:
243
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
244
+ output_files = list(Path(".").glob(f"{output_prefix}*"))
245
+ return {
246
+ "command_executed": " ".join(cmd),
247
+ "stdout": result.stdout,
248
+ "stderr": result.stderr,
249
+ "output_files": [str(f) for f in output_files]
250
+ }
251
+ except subprocess.CalledProcessError as e:
252
+ return {
253
+ "command_executed": " ".join(cmd),
254
+ "error": str(e),
255
+ "stdout": e.stdout,
256
+ "stderr": e.stderr
257
+ }
258
+
259
+ @mcp.tool()
260
+ def art_profiler_illumina(
261
+ output_profile: str,
262
+ input_dir: str,
263
+ fastq_extension: str = "fastq",
264
+ threads: int = 1
265
+ ):
266
+ """
267
+ ART_Profiler_Illumina: Create a sequencing error profile from Illumina FASTQ files.
268
+
269
+ Args:
270
+ output_profile: The name of the output profile.
271
+ input_dir: The directory containing Illumina FASTQ files.
272
+ fastq_extension: The filename extension of FASTQ files (e.g., 'fastq' or 'fq').
273
+ threads: The number of threads to use.
274
+ """
275
+ input_path = Path(input_dir)
276
+ if not input_path.is_dir():
277
+ return {"error": f"Input directory not found: {input_dir}"}
278
+
279
+ cmd = ["art_profiler_illumina", output_profile, str(input_path), fastq_extension, str(threads)]
280
+
281
+ try:
282
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
283
+
284
+ # Profiles usually create multiple files with the profile name
285
+ output_files = list(Path(".").glob(f"{output_profile}*"))
286
+
287
+ return {
288
+ "command_executed": " ".join(cmd),
289
+ "stdout": result.stdout,
290
+ "stderr": result.stderr,
291
+ "output_files": [str(f) for f in output_files]
292
+ }
293
+ except subprocess.CalledProcessError as e:
294
+ return {
295
+ "command_executed": " ".join(cmd),
296
+ "error": str(e),
297
+ "stdout": e.stdout,
298
+ "stderr": e.stderr
299
+ }
300
+
301
+ if __name__ == "__main__":
302
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_art/app/art_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_art/app/art_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_art'
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_art/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_art/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-art:
5
+ build: .
6
+ image: mcp-art:latest
7
+ container_name: mcp-art
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=art
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_art/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - art
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_art/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_augustus/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 augustus via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda augustus -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 augustus_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/augustus_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/augustus_server.py"]
40
+
Biomni/mcp_generated/mcp_augustus/app/augustus_server.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Optional, List, Literal
5
+
6
+ # Assume mcp.tool is available in the execution environment.
7
+ # Since we are not importing it, we can define a dummy decorator
8
+ # to make the code syntactically valid.
9
+ class mcp:
10
+ def tool(func):
11
+ return func
12
+
13
+ @mcp.tool
14
+ def augustus(
15
+ query_file: Path,
16
+ species: str,
17
+ strand: Literal["both", "forward", "backward"] = "both",
18
+ genemodel: Literal["partial", "intronless", "complete", "atleastone", "exactlyone"] = "partial",
19
+ outfile: Optional[Path] = None,
20
+ hints_file: Optional[Path] = None,
21
+ gff3: bool = False,
22
+ utr: bool = False,
23
+ singlestrand: bool = False,
24
+ protein: bool = True,
25
+ introns: bool = True,
26
+ start: bool = True,
27
+ stop: bool = True,
28
+ cds: bool = True,
29
+ codingseq: bool = False,
30
+ no_in_frame_stop: bool = False,
31
+ alternatives_from_evidence: bool = True,
32
+ alternatives_from_sampling: bool = False,
33
+ sample: Optional[int] = None,
34
+ keep_viterbi: bool = False,
35
+ no_prediction: bool = False,
36
+ progress: bool = False,
37
+ unique_gene_id: bool = False,
38
+ softmasking: bool = False,
39
+ extrinsic_cfg_file: Optional[Path] = None,
40
+ augustus_config_path: Optional[Path] = None,
41
+ ) -> dict:
42
+ """
43
+ Runs AUGUSTUS, a tool for gene prediction in eukaryotes.
44
+
45
+ This tool predicts genes in a given input FASTA file (query_file) based on a species-specific model.
46
+ It supports various prediction models, extrinsic evidence (hints), and output formats.
47
+ """
48
+ # 1. Input validation
49
+ if not query_file.is_file():
50
+ raise FileNotFoundError(f"Input query file not found: {query_file}")
51
+ if hints_file and not hints_file.is_file():
52
+ raise FileNotFoundError(f"Hints file not found: {hints_file}")
53
+ if extrinsic_cfg_file and not extrinsic_cfg_file.is_file():
54
+ raise FileNotFoundError(f"Extrinsic config file not found: {extrinsic_cfg_file}")
55
+ if augustus_config_path and not augustus_config_path.is_dir():
56
+ raise NotADirectoryError(f"AUGUSTUS_CONFIG_PATH is not a valid directory: {augustus_config_path}")
57
+
58
+ # 2. Command construction
59
+ cmd = ["augustus"]
60
+
61
+ # Add parameters
62
+ cmd.append(f"--species={species}")
63
+ cmd.append(f"--strand={strand}")
64
+ cmd.append(f"--genemodel={genemodel}")
65
+
66
+ # Boolean flags with true/false values
67
+ if singlestrand:
68
+ cmd.append("--singlestrand=true")
69
+ if no_in_frame_stop:
70
+ cmd.append("--noInFrameStop=true")
71
+ if alternatives_from_evidence:
72
+ cmd.append("--alternatives-from-evidence=true")
73
+ if alternatives_from_sampling:
74
+ cmd.append("--alternatives-from-sampling=true")
75
+ if keep_viterbi:
76
+ cmd.append("--keep_viterbi=true")
77
+ if no_prediction:
78
+ cmd.append("--noprediction=true")
79
+ if progress:
80
+ cmd.append("--progress=true")
81
+ if unique_gene_id:
82
+ cmd.append("--uniqueGeneId=true")
83
+
84
+ # Boolean flags with on/off values
85
+ cmd.append(f"--gff3={'on' if gff3 else 'off'}")
86
+ cmd.append(f"--UTR={'on' if utr else 'off'}")
87
+ cmd.append(f"--protein={'on' if protein else 'off'}")
88
+ cmd.append(f"--introns={'on' if introns else 'off'}")
89
+ cmd.append(f"--start={'on' if start else 'off'}")
90
+ cmd.append(f"--stop={'on' if stop else 'off'}")
91
+ cmd.append(f"--cds={'on' if cds else 'off'}")
92
+ cmd.append(f"--codingseq={'on' if codingseq else 'off'}")
93
+
94
+ # Boolean flag with 1/0 value
95
+ if softmasking:
96
+ cmd.append("--softmasking=1")
97
+
98
+ # Optional file/directory paths and other values
99
+ if hints_file:
100
+ cmd.append(f"--hintsfile={hints_file}")
101
+ if extrinsic_cfg_file:
102
+ cmd.append(f"--extrinsicCfgFile={extrinsic_cfg_file}")
103
+ if augustus_config_path:
104
+ cmd.append(f"--AUGUSTUS_CONFIG_PATH={augustus_config_path}")
105
+ if sample is not None:
106
+ cmd.append(f"--sample={sample}")
107
+ if outfile:
108
+ # Ensure parent directory exists for the output file
109
+ outfile.parent.mkdir(parents=True, exist_ok=True)
110
+ cmd.append(f"--outfile={outfile}")
111
+
112
+ # Positional argument (must be last for some versions)
113
+ cmd.append(str(query_file))
114
+
115
+ command_executed = " ".join(cmd)
116
+ logging.info(f"Executing command: {command_executed}")
117
+
118
+ # 3. Subprocess execution
119
+ try:
120
+ result = subprocess.run(
121
+ cmd,
122
+ capture_output=True,
123
+ text=True,
124
+ check=True,
125
+ )
126
+ except FileNotFoundError:
127
+ raise RuntimeError("augustus executable not found. Please ensure it is in your system's PATH.")
128
+ except subprocess.CalledProcessError as e:
129
+ logging.error(f"AUGUSTUS execution failed with exit code {e.returncode}")
130
+ logging.error(f"Stderr: {e.stderr}")
131
+ logging.error(f"Stdout: {e.stdout}")
132
+ return {
133
+ "command_executed": command_executed,
134
+ "stdout": e.stdout,
135
+ "stderr": e.stderr,
136
+ "error": "AUGUSTUS execution failed.",
137
+ "return_code": e.returncode,
138
+ "output_files": []
139
+ }
140
+
141
+ # 4. Structured result return
142
+ output_files = [str(outfile)] if outfile else []
143
+
144
+ return {
145
+ "command_executed": command_executed,
146
+ "stdout": result.stdout,
147
+ "stderr": result.stderr,
148
+ "output_files": output_files
149
+ }
Biomni/mcp_generated/mcp_augustus/app/augustus_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_augustus/app/augustus_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_augustus'
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_augustus/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-augustus:
5
+ build: .
6
+ image: mcp-augustus:latest
7
+ container_name: mcp-augustus
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=augustus
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_augustus/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - augustus
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_augustus/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bactopia/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 bactopia via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bactopia -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/bactopia_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/bactopia_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/bactopia_server.py"]
40
+
Biomni/mcp_generated/mcp_bactopia/app/bactopia_server.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List, Union
4
+ import os
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ SERVER_NAME = 'local_bactopia'
9
+ mcp = FastMCP(SERVER_NAME)
10
+
11
+ @mcp.tool()
12
+ def bactopia_run(
13
+ sample: str,
14
+ outdir: str,
15
+ datasets: str,
16
+ r1: Optional[str] = None,
17
+ r2: Optional[str] = None,
18
+ se: Optional[str] = None,
19
+ fastqs: Optional[str] = None,
20
+ accession: Optional[str] = None,
21
+ accessions: Optional[str] = None,
22
+ profile: str = "conda",
23
+ cpus: int = 2,
24
+ max_memory: int = 8,
25
+ resume: bool = False,
26
+ ) -> dict:
27
+ """
28
+ Run the main Bactopia analysis pipeline for a bacterial genome.
29
+
30
+ Args:
31
+ sample: Name of the sample.
32
+ outdir: Directory to store output results.
33
+ datasets: Path to the Bactopia datasets directory.
34
+ r1: Path to forward reads (Paired-end).
35
+ r2: Path to reverse reads (Paired-end).
36
+ se: Path to single-end reads.
37
+ fastqs: Path to a FOFN (file-of-filenames) for multiple samples.
38
+ accession: A single ENA/SRA accession (e.g., SRX000000).
39
+ accessions: A file containing a list of ENA/SRA accessions.
40
+ profile: Nextflow profile to use (e.g., conda, docker, singularity).
41
+ cpus: Number of CPUs to allocate.
42
+ max_memory: Maximum memory in GB to allocate.
43
+ resume: Whether to resume a previous run.
44
+ """
45
+ # Input validation
46
+ out_path = Path(outdir)
47
+ ds_path = Path(datasets)
48
+
49
+ if not ds_path.exists():
50
+ return {"error": f"Datasets directory not found at {datasets}"}
51
+
52
+ cmd = ["bactopia", "--sample", sample, "--outdir", str(out_path), "--datasets", str(ds_path)]
53
+
54
+ # Input source logic
55
+ if r1 and r2:
56
+ if not Path(r1).exists() or not Path(r2).exists():
57
+ return {"error": "R1 or R2 file does not exist"}
58
+ cmd += ["--R1", r1, "--R2", r2]
59
+ elif se:
60
+ if not Path(se).exists():
61
+ return {"error": "Single-end file does not exist"}
62
+ cmd += ["--SE", se]
63
+ elif fastqs:
64
+ if not Path(fastqs).exists():
65
+ return {"error": "Fastqs list file does not exist"}
66
+ cmd += ["--fastqs", fastqs]
67
+ elif accession:
68
+ cmd += ["--accession", accession]
69
+ elif accessions:
70
+ if not Path(accessions).exists():
71
+ return {"error": "Accessions list file does not exist"}
72
+ cmd += ["--accessions", accessions]
73
+ else:
74
+ return {"error": "No input source provided (R1/R2, SE, fastqs, accession, or accessions required)"}
75
+
76
+ # Performance and Nextflow options
77
+ cmd += ["-profile", profile]
78
+ cmd += ["--max_cpus", str(cpus)]
79
+ cmd += ["--max_memory", f"{max_memory}.GB"]
80
+
81
+ if resume:
82
+ cmd.append("-resume")
83
+
84
+ try:
85
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
86
+ return {
87
+ "command_executed": " ".join(cmd),
88
+ "stdout": result.stdout,
89
+ "stderr": result.stderr,
90
+ "output_directory": str(out_path.absolute())
91
+ }
92
+ except subprocess.CalledProcessError as e:
93
+ return {
94
+ "command_executed": " ".join(cmd),
95
+ "error": str(e),
96
+ "stdout": e.stdout,
97
+ "stderr": e.stderr
98
+ }
99
+
100
+ @mcp.tool()
101
+ def bactopia_prepare(
102
+ path: str,
103
+ recursive: bool = False,
104
+ extension: str = ".fastq.gz",
105
+ output_file: Optional[str] = None
106
+ ) -> dict:
107
+ """
108
+ Prepare a FASTQ list (FOFN) for Bactopia from a directory of sequencing files.
109
+
110
+ Args:
111
+ path: Directory containing FASTQ files.
112
+ recursive: Search for FASTQs in subdirectories.
113
+ extension: File extension to look for.
114
+ output_file: Optional path to save the generated list.
115
+ """
116
+ input_path = Path(path)
117
+ if not input_path.is_dir():
118
+ return {"error": f"Path {path} is not a directory"}
119
+
120
+ cmd = ["bactopia", "prepare", str(input_path), "--extension", extension]
121
+ if recursive:
122
+ cmd.append("--recursive")
123
+
124
+ try:
125
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
126
+
127
+ if output_file:
128
+ out_p = Path(output_file)
129
+ out_p.write_text(result.stdout)
130
+ return {
131
+ "command_executed": " ".join(cmd),
132
+ "stdout": "File list generated and saved.",
133
+ "output_file": str(out_p.absolute())
134
+ }
135
+
136
+ return {
137
+ "command_executed": " ".join(cmd),
138
+ "stdout": result.stdout,
139
+ "stderr": result.stderr
140
+ }
141
+ except subprocess.CalledProcessError as e:
142
+ return {
143
+ "command_executed": " ".join(cmd),
144
+ "error": str(e),
145
+ "stdout": e.stdout,
146
+ "stderr": e.stderr
147
+ }
148
+
149
+ @mcp.tool()
150
+ def bactopia_search(
151
+ query: str,
152
+ limit: int = 10,
153
+ output_file: Optional[str] = None
154
+ ) -> dict:
155
+ """
156
+ Search for bacterial genome accessions in ENA/SRA.
157
+
158
+ Args:
159
+ query: Search query (e.g., "Staphylococcus aureus").
160
+ limit: Maximum number of results to return.
161
+ output_file: Optional path to save the accession list.
162
+ """
163
+ cmd = ["bactopia", "search", query, "--limit", str(limit)]
164
+
165
+ try:
166
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
167
+
168
+ if output_file:
169
+ out_p = Path(output_file)
170
+ out_p.write_text(result.stdout)
171
+ return {
172
+ "command_executed": " ".join(cmd),
173
+ "stdout": f"Search results saved to {output_file}",
174
+ "output_file": str(out_p.absolute())
175
+ }
176
+
177
+ return {
178
+ "command_executed": " ".join(cmd),
179
+ "stdout": result.stdout,
180
+ "stderr": result.stderr
181
+ }
182
+ except subprocess.CalledProcessError as e:
183
+ return {
184
+ "command_executed": " ".join(cmd),
185
+ "error": str(e),
186
+ "stdout": e.stdout,
187
+ "stderr": e.stderr
188
+ }
189
+
190
+ @mcp.tool()
191
+ def bactopia_datasets(
192
+ outdir: str,
193
+ species: Optional[str] = None,
194
+ include_genus: bool = False,
195
+ limit: int = 100
196
+ ) -> dict:
197
+ """
198
+ Download and setup Bactopia datasets/databases.
199
+
200
+ Args:
201
+ outdir: Directory to save the datasets.
202
+ species: Optional species name to download specific datasets (e.g., "Salmonella enterica").
203
+ include_genus: Include genus-level datasets.
204
+ limit: Limit the number of genomes used for building datasets.
205
+ """
206
+ out_path = Path(outdir)
207
+ cmd = ["bactopia", "datasets", "--outdir", str(out_path)]
208
+
209
+ if species:
210
+ cmd += ["--species", species]
211
+ if include_genus:
212
+ cmd.append("--include_genus")
213
+ cmd += ["--limit", str(limit)]
214
+
215
+ try:
216
+ # This can be a long-running process
217
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
218
+ return {
219
+ "command_executed": " ".join(cmd),
220
+ "stdout": result.stdout,
221
+ "stderr": result.stderr,
222
+ "datasets_path": str(out_path.absolute())
223
+ }
224
+ except subprocess.CalledProcessError as e:
225
+ return {
226
+ "command_executed": " ".join(cmd),
227
+ "error": str(e),
228
+ "stdout": e.stdout,
229
+ "stderr": e.stderr
230
+ }
231
+
232
+ @mcp.tool()
233
+ def bactopia_tool(
234
+ tool_name: str,
235
+ bactopia_dir: str,
236
+ outdir: str,
237
+ profile: str = "conda",
238
+ cpus: int = 2,
239
+ extra_params: Optional[List[str]] = None
240
+ ) -> dict:
241
+ """
242
+ Run a Bactopia Tool (comparative analysis) on existing Bactopia outputs.
243
+ Common tools include: pangenome, roary, iqtree, pirate, summary, etc.
244
+
245
+ Args:
246
+ tool_name: Name of the Bactopia Tool to run.
247
+ bactopia_dir: Directory containing previous Bactopia results.
248
+ outdir: Directory to store tool results.
249
+ profile: Nextflow profile to use.
250
+ cpus: Number of CPUs to allocate.
251
+ extra_params: List of additional command line arguments for the specific tool.
252
+ """
253
+ b_path = Path(bactopia_dir)
254
+ o_path = Path(outdir)
255
+
256
+ if not b_path.exists():
257
+ return {"error": f"Bactopia results directory not found at {bactopia_dir}"}
258
+
259
+ cmd = [
260
+ "bactopia", tool_name,
261
+ "--bactopia", str(b_path),
262
+ "--outdir", str(o_path),
263
+ "-profile", profile,
264
+ "--max_cpus", str(cpus)
265
+ ]
266
+
267
+ if extra_params:
268
+ cmd.extend(extra_params)
269
+
270
+ try:
271
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
272
+ return {
273
+ "command_executed": " ".join(cmd),
274
+ "stdout": result.stdout,
275
+ "stderr": result.stderr,
276
+ "output_directory": str(o_path.absolute())
277
+ }
278
+ except subprocess.CalledProcessError as e:
279
+ return {
280
+ "command_executed": " ".join(cmd),
281
+ "error": str(e),
282
+ "stdout": e.stdout,
283
+ "stderr": e.stderr
284
+ }
285
+
286
+ if __name__ == "__main__":
287
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bactopia/app/bactopia_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_bactopia/app/bactopia_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bactopia'
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_bactopia/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bactopia/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bactopia:
5
+ build: .
6
+ image: mcp-bactopia:latest
7
+ container_name: mcp-bactopia
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bactopia
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_bactopia/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bactopia
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bactopia/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bedops/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 bedops via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bedops -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/bedops_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/bedops_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/bedops_server.py"]
40
+
Biomni/mcp_generated/mcp_bedops/app/bedops_server.py ADDED
@@ -0,0 +1,827 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import List, Optional
5
+
6
+ # MCP decorator is commented out as per instructions
7
+ # import mcp
8
+
9
+ from mcp.server.fastmcp import FastMCP
10
+
11
+ SERVER_NAME = 'local_bedops'
12
+ mcp = FastMCP(SERVER_NAME)
13
+
14
+ @mcp.tool()
15
+ def bedops_complement(
16
+ files: List[Path],
17
+ chop_to_limits: bool = False,
18
+ chrom: Optional[str] = None,
19
+ ec: bool = False,
20
+ header: bool = False,
21
+ range_str: Optional[str] = None,
22
+ output_file: Optional[Path] = None,
23
+ ) -> dict:
24
+ """
25
+ Computes the complement of one or more BED files.
26
+
27
+ This corresponds to the `bedops -c` or `bedops --complement` operation.
28
+ It finds the regions within chromosome boundaries that are not covered by
29
+ any intervals in the input file(s).
30
+
31
+ Args:
32
+ files: A list of one or more input BED/Starch files. Must be sorted.
33
+ chop_to_limits: If True, chop complementary regions to chromosome limits
34
+ defined by the first input file (-L flag).
35
+ chrom: Process data for the given chromosome only.
36
+ ec: Error check input files (slower).
37
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
38
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
39
+ single value 'S' for symmetric padding (e.g., '100').
40
+ output_file: Optional path to save the output. If not provided,
41
+ output is returned as a string in the result dictionary.
42
+
43
+ Returns:
44
+ A dictionary containing the command executed, stdout, stderr, and a
45
+ list of output files generated.
46
+ """
47
+ if not files:
48
+ raise ValueError("At least one input file must be provided for the complement operation.")
49
+ for file_path in files:
50
+ if not file_path.exists():
51
+ raise FileNotFoundError(f"Input file not found: {file_path}")
52
+
53
+ cmd = ["bedops"]
54
+
55
+ if chrom:
56
+ cmd.extend(["--chrom", chrom])
57
+ if ec:
58
+ cmd.append("--ec")
59
+ if header:
60
+ cmd.append("--header")
61
+ if range_str:
62
+ cmd.extend(["--range", range_str])
63
+
64
+ cmd.append("--complement")
65
+ if chop_to_limits:
66
+ cmd.append("-L")
67
+
68
+ cmd.extend([str(p) for p in files])
69
+
70
+ try:
71
+ if output_file:
72
+ with open(output_file, "w") as f:
73
+ result = subprocess.run(
74
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
75
+ )
76
+ stdout_capture = ""
77
+ output_files_list = [str(output_file)]
78
+ else:
79
+ result = subprocess.run(
80
+ cmd, check=True, text=True, capture_output=True
81
+ )
82
+ stdout_capture = result.stdout
83
+ output_files_list = []
84
+
85
+ return {
86
+ "command_executed": " ".join(cmd),
87
+ "stdout": stdout_capture,
88
+ "stderr": result.stderr,
89
+ "output_files": output_files_list,
90
+ }
91
+ except subprocess.CalledProcessError as e:
92
+ raise RuntimeError(
93
+ f"bedops complement failed with exit code {e.returncode}\n"
94
+ f"Stderr: {e.stderr}\n"
95
+ f"Stdout: {e.stdout}\n"
96
+ f"Command: {' '.join(cmd)}"
97
+ ) from e
98
+
99
+ @mcp.tool()
100
+ def bedops_difference(
101
+ files: List[Path],
102
+ chrom: Optional[str] = None,
103
+ ec: bool = False,
104
+ header: bool = False,
105
+ range_str: Optional[str] = None,
106
+ output_file: Optional[Path] = None,
107
+ ) -> dict:
108
+ """
109
+ Computes the difference between a reference BED file and one or more other BED files.
110
+
111
+ This corresponds to the `bedops -d` or `bedops --difference` operation.
112
+ It returns regions from the first (reference) file that do not overlap
113
+ with any regions in the subsequent files.
114
+
115
+ Args:
116
+ files: A list of two or more input BED/Starch files. The first file is
117
+ the reference. All files must be sorted.
118
+ chrom: Process data for the given chromosome only.
119
+ ec: Error check input files (slower).
120
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
121
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
122
+ single value 'S' for symmetric padding (e.g., '100').
123
+ output_file: Optional path to save the output. If not provided,
124
+ output is returned as a string in the result dictionary.
125
+
126
+ Returns:
127
+ A dictionary containing the command executed, stdout, stderr, and a
128
+ list of output files generated.
129
+ """
130
+ if len(files) < 2:
131
+ raise ValueError("At least two input files (a reference and one other) must be provided for the difference operation.")
132
+ for file_path in files:
133
+ if not file_path.exists():
134
+ raise FileNotFoundError(f"Input file not found: {file_path}")
135
+
136
+ cmd = ["bedops"]
137
+
138
+ if chrom:
139
+ cmd.extend(["--chrom", chrom])
140
+ if ec:
141
+ cmd.append("--ec")
142
+ if header:
143
+ cmd.append("--header")
144
+ if range_str:
145
+ cmd.extend(["--range", range_str])
146
+
147
+ cmd.append("--difference")
148
+ cmd.extend([str(p) for p in files])
149
+
150
+ try:
151
+ if output_file:
152
+ with open(output_file, "w") as f:
153
+ result = subprocess.run(
154
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
155
+ )
156
+ stdout_capture = ""
157
+ output_files_list = [str(output_file)]
158
+ else:
159
+ result = subprocess.run(
160
+ cmd, check=True, text=True, capture_output=True
161
+ )
162
+ stdout_capture = result.stdout
163
+ output_files_list = []
164
+
165
+ return {
166
+ "command_executed": " ".join(cmd),
167
+ "stdout": stdout_capture,
168
+ "stderr": result.stderr,
169
+ "output_files": output_files_list,
170
+ }
171
+ except subprocess.CalledProcessError as e:
172
+ raise RuntimeError(
173
+ f"bedops difference failed with exit code {e.returncode}\n"
174
+ f"Stderr: {e.stderr}\n"
175
+ f"Stdout: {e.stdout}\n"
176
+ f"Command: {' '.join(cmd)}"
177
+ ) from e
178
+
179
+ @mcp.tool()
180
+ def bedops_element_of(
181
+ files: List[Path],
182
+ overlap_criterion: str = "100%",
183
+ chrom: Optional[str] = None,
184
+ ec: bool = False,
185
+ header: bool = False,
186
+ range_str: Optional[str] = None,
187
+ output_file: Optional[Path] = None,
188
+ ) -> dict:
189
+ """
190
+ Finds elements in the reference file that overlap other files by a specified amount.
191
+
192
+ This corresponds to the `bedops -e` or `bedops --element-of` operation.
193
+ It returns elements from the first (reference) file that overlap elements
194
+ in any of the other files by at least the specified amount.
195
+
196
+ Args:
197
+ files: A list of two or more input BED/Starch files. The first file is
198
+ the reference. All files must be sorted.
199
+ overlap_criterion: The required overlap, as base pairs (e.g., '1') or
200
+ percentage (e.g., '50%'). Defaults to '100%'.
201
+ chrom: Process data for the given chromosome only.
202
+ ec: Error check input files (slower).
203
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
204
+ range_str: Pad coordinates. Use 'L:R' or 'S' format. The first (reference)
205
+ file is NOT padded with this operation.
206
+ output_file: Optional path to save the output. If not provided,
207
+ output is returned as a string in the result dictionary.
208
+
209
+ Returns:
210
+ A dictionary containing the command executed, stdout, stderr, and a
211
+ list of output files generated.
212
+ """
213
+ if len(files) < 2:
214
+ raise ValueError("At least two input files (a reference and one other) must be provided for the element-of operation.")
215
+ for file_path in files:
216
+ if not file_path.exists():
217
+ raise FileNotFoundError(f"Input file not found: {file_path}")
218
+
219
+ cmd = ["bedops"]
220
+
221
+ if chrom:
222
+ cmd.extend(["--chrom", chrom])
223
+ if ec:
224
+ cmd.append("--ec")
225
+ if header:
226
+ cmd.append("--header")
227
+ if range_str:
228
+ cmd.extend(["--range", range_str])
229
+
230
+ cmd.extend(["--element-of", overlap_criterion])
231
+ cmd.extend([str(p) for p in files])
232
+
233
+ try:
234
+ if output_file:
235
+ with open(output_file, "w") as f:
236
+ result = subprocess.run(
237
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
238
+ )
239
+ stdout_capture = ""
240
+ output_files_list = [str(output_file)]
241
+ else:
242
+ result = subprocess.run(
243
+ cmd, check=True, text=True, capture_output=True
244
+ )
245
+ stdout_capture = result.stdout
246
+ output_files_list = []
247
+
248
+ return {
249
+ "command_executed": " ".join(cmd),
250
+ "stdout": stdout_capture,
251
+ "stderr": result.stderr,
252
+ "output_files": output_files_list,
253
+ }
254
+ except subprocess.CalledProcessError as e:
255
+ raise RuntimeError(
256
+ f"bedops element-of failed with exit code {e.returncode}\n"
257
+ f"Stderr: {e.stderr}\n"
258
+ f"Stdout: {e.stdout}\n"
259
+ f"Command: {' '.join(cmd)}"
260
+ ) from e
261
+
262
+ @mcp.tool()
263
+ def bedops_intersect(
264
+ files: List[Path],
265
+ chrom: Optional[str] = None,
266
+ ec: bool = False,
267
+ header: bool = False,
268
+ range_str: Optional[str] = None,
269
+ output_file: Optional[Path] = None,
270
+ ) -> dict:
271
+ """
272
+ Computes the intersection of two or more BED files.
273
+
274
+ This corresponds to the `bedops -i` or `bedops --intersect` operation.
275
+ It returns regions that are common to all input files.
276
+
277
+ Args:
278
+ files: A list of two or more input BED/Starch files. All files must be sorted.
279
+ chrom: Process data for the given chromosome only.
280
+ ec: Error check input files (slower).
281
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
282
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
283
+ single value 'S' for symmetric padding (e.g., '100').
284
+ output_file: Optional path to save the output. If not provided,
285
+ output is returned as a string in the result dictionary.
286
+
287
+ Returns:
288
+ A dictionary containing the command executed, stdout, stderr, and a
289
+ list of output files generated.
290
+ """
291
+ if len(files) < 2:
292
+ raise ValueError("At least two input files must be provided for the intersect operation.")
293
+ for file_path in files:
294
+ if not file_path.exists():
295
+ raise FileNotFoundError(f"Input file not found: {file_path}")
296
+
297
+ cmd = ["bedops"]
298
+
299
+ if chrom:
300
+ cmd.extend(["--chrom", chrom])
301
+ if ec:
302
+ cmd.append("--ec")
303
+ if header:
304
+ cmd.append("--header")
305
+ if range_str:
306
+ cmd.extend(["--range", range_str])
307
+
308
+ cmd.append("--intersect")
309
+ cmd.extend([str(p) for p in files])
310
+
311
+ try:
312
+ if output_file:
313
+ with open(output_file, "w") as f:
314
+ result = subprocess.run(
315
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
316
+ )
317
+ stdout_capture = ""
318
+ output_files_list = [str(output_file)]
319
+ else:
320
+ result = subprocess.run(
321
+ cmd, check=True, text=True, capture_output=True
322
+ )
323
+ stdout_capture = result.stdout
324
+ output_files_list = []
325
+
326
+ return {
327
+ "command_executed": " ".join(cmd),
328
+ "stdout": stdout_capture,
329
+ "stderr": result.stderr,
330
+ "output_files": output_files_list,
331
+ }
332
+ except subprocess.CalledProcessError as e:
333
+ raise RuntimeError(
334
+ f"bedops intersect failed with exit code {e.returncode}\n"
335
+ f"Stderr: {e.stderr}\n"
336
+ f"Stdout: {e.stdout}\n"
337
+ f"Command: {' '.join(cmd)}"
338
+ ) from e
339
+
340
+ @mcp.tool()
341
+ def bedops_merge(
342
+ files: List[Path],
343
+ chrom: Optional[str] = None,
344
+ ec: bool = False,
345
+ header: bool = False,
346
+ range_str: Optional[str] = None,
347
+ output_file: Optional[Path] = None,
348
+ ) -> dict:
349
+ """
350
+ Merges overlapping regions from one or more BED files.
351
+
352
+ This corresponds to the `bedops -m` or `bedops --merge` operation.
353
+ It combines overlapping or adjacent intervals into a single, larger interval.
354
+
355
+ Args:
356
+ files: A list of one or more input BED/Starch files. Must be sorted.
357
+ chrom: Process data for the given chromosome only.
358
+ ec: Error check input files (slower).
359
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
360
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
361
+ single value 'S' for symmetric padding (e.g., '100').
362
+ output_file: Optional path to save the output. If not provided,
363
+ output is returned as a string in the result dictionary.
364
+
365
+ Returns:
366
+ A dictionary containing the command executed, stdout, stderr, and a
367
+ list of output files generated.
368
+ """
369
+ if not files:
370
+ raise ValueError("At least one input file must be provided for the merge operation.")
371
+ for file_path in files:
372
+ if not file_path.exists():
373
+ raise FileNotFoundError(f"Input file not found: {file_path}")
374
+
375
+ cmd = ["bedops"]
376
+
377
+ if chrom:
378
+ cmd.extend(["--chrom", chrom])
379
+ if ec:
380
+ cmd.append("--ec")
381
+ if header:
382
+ cmd.append("--header")
383
+ if range_str:
384
+ cmd.extend(["--range", range_str])
385
+
386
+ cmd.append("--merge")
387
+ cmd.extend([str(p) for p in files])
388
+
389
+ try:
390
+ if output_file:
391
+ with open(output_file, "w") as f:
392
+ result = subprocess.run(
393
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
394
+ )
395
+ stdout_capture = ""
396
+ output_files_list = [str(output_file)]
397
+ else:
398
+ result = subprocess.run(
399
+ cmd, check=True, text=True, capture_output=True
400
+ )
401
+ stdout_capture = result.stdout
402
+ output_files_list = []
403
+
404
+ return {
405
+ "command_executed": " ".join(cmd),
406
+ "stdout": stdout_capture,
407
+ "stderr": result.stderr,
408
+ "output_files": output_files_list,
409
+ }
410
+ except subprocess.CalledProcessError as e:
411
+ raise RuntimeError(
412
+ f"bedops merge failed with exit code {e.returncode}\n"
413
+ f"Stderr: {e.stderr}\n"
414
+ f"Stdout: {e.stdout}\n"
415
+ f"Command: {' '.join(cmd)}"
416
+ ) from e
417
+
418
+ @mcp.tool()
419
+ def bedops_not_element_of(
420
+ files: List[Path],
421
+ overlap_criterion: str = "100%",
422
+ chrom: Optional[str] = None,
423
+ ec: bool = False,
424
+ header: bool = False,
425
+ range_str: Optional[str] = None,
426
+ output_file: Optional[Path] = None,
427
+ ) -> dict:
428
+ """
429
+ Finds elements in the reference file that DO NOT overlap other files by a specified amount.
430
+
431
+ This corresponds to the `bedops -n` or `bedops --not-element-of` operation.
432
+ It is the inverse of the `element-of` operation.
433
+
434
+ Args:
435
+ files: A list of two or more input BED/Starch files. The first file is
436
+ the reference. All files must be sorted.
437
+ overlap_criterion: The required overlap, as base pairs (e.g., '1') or
438
+ percentage (e.g., '50%'). Defaults to '100%'.
439
+ chrom: Process data for the given chromosome only.
440
+ ec: Error check input files (slower).
441
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
442
+ range_str: Pad coordinates. Use 'L:R' or 'S' format. The first (reference)
443
+ file is NOT padded with this operation.
444
+ output_file: Optional path to save the output. If not provided,
445
+ output is returned as a string in the result dictionary.
446
+
447
+ Returns:
448
+ A dictionary containing the command executed, stdout, stderr, and a
449
+ list of output files generated.
450
+ """
451
+ if len(files) < 2:
452
+ raise ValueError("At least two input files (a reference and one other) must be provided for the not-element-of operation.")
453
+ for file_path in files:
454
+ if not file_path.exists():
455
+ raise FileNotFoundError(f"Input file not found: {file_path}")
456
+
457
+ cmd = ["bedops"]
458
+
459
+ if chrom:
460
+ cmd.extend(["--chrom", chrom])
461
+ if ec:
462
+ cmd.append("--ec")
463
+ if header:
464
+ cmd.append("--header")
465
+ if range_str:
466
+ cmd.extend(["--range", range_str])
467
+
468
+ cmd.extend(["--not-element-of", overlap_criterion])
469
+ cmd.extend([str(p) for p in files])
470
+
471
+ try:
472
+ if output_file:
473
+ with open(output_file, "w") as f:
474
+ result = subprocess.run(
475
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
476
+ )
477
+ stdout_capture = ""
478
+ output_files_list = [str(output_file)]
479
+ else:
480
+ result = subprocess.run(
481
+ cmd, check=True, text=True, capture_output=True
482
+ )
483
+ stdout_capture = result.stdout
484
+ output_files_list = []
485
+
486
+ return {
487
+ "command_executed": " ".join(cmd),
488
+ "stdout": stdout_capture,
489
+ "stderr": result.stderr,
490
+ "output_files": output_files_list,
491
+ }
492
+ except subprocess.CalledProcessError as e:
493
+ raise RuntimeError(
494
+ f"bedops not-element-of failed with exit code {e.returncode}\n"
495
+ f"Stderr: {e.stderr}\n"
496
+ f"Stdout: {e.stdout}\n"
497
+ f"Command: {' '.join(cmd)}"
498
+ ) from e
499
+
500
+ @mcp.tool()
501
+ def bedops_partition(
502
+ files: List[Path],
503
+ chrom: Optional[str] = None,
504
+ ec: bool = False,
505
+ header: bool = False,
506
+ range_str: Optional[str] = None,
507
+ output_file: Optional[Path] = None,
508
+ ) -> dict:
509
+ """
510
+ Partitions the input BED file(s) into disjoint segments.
511
+
512
+ This corresponds to the `bedops -p` or `bedops --partition` operation.
513
+ It breaks the input regions into non-overlapping segments, reporting each
514
+ new segment and which input files it came from.
515
+
516
+ Args:
517
+ files: A list of one or more input BED/Starch files. Must be sorted.
518
+ chrom: Process data for the given chromosome only.
519
+ ec: Error check input files (slower).
520
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
521
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
522
+ single value 'S' for symmetric padding (e.g., '100').
523
+ output_file: Optional path to save the output. If not provided,
524
+ output is returned as a string in the result dictionary.
525
+
526
+ Returns:
527
+ A dictionary containing the command executed, stdout, stderr, and a
528
+ list of output files generated.
529
+ """
530
+ if not files:
531
+ raise ValueError("At least one input file must be provided for the partition operation.")
532
+ for file_path in files:
533
+ if not file_path.exists():
534
+ raise FileNotFoundError(f"Input file not found: {file_path}")
535
+
536
+ cmd = ["bedops"]
537
+
538
+ if chrom:
539
+ cmd.extend(["--chrom", chrom])
540
+ if ec:
541
+ cmd.append("--ec")
542
+ if header:
543
+ cmd.append("--header")
544
+ if range_str:
545
+ cmd.extend(["--range", range_str])
546
+
547
+ cmd.append("--partition")
548
+ cmd.extend([str(p) for p in files])
549
+
550
+ try:
551
+ if output_file:
552
+ with open(output_file, "w") as f:
553
+ result = subprocess.run(
554
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
555
+ )
556
+ stdout_capture = ""
557
+ output_files_list = [str(output_file)]
558
+ else:
559
+ result = subprocess.run(
560
+ cmd, check=True, text=True, capture_output=True
561
+ )
562
+ stdout_capture = result.stdout
563
+ output_files_list = []
564
+
565
+ return {
566
+ "command_executed": " ".join(cmd),
567
+ "stdout": stdout_capture,
568
+ "stderr": result.stderr,
569
+ "output_files": output_files_list,
570
+ }
571
+ except subprocess.CalledProcessError as e:
572
+ raise RuntimeError(
573
+ f"bedops partition failed with exit code {e.returncode}\n"
574
+ f"Stderr: {e.stderr}\n"
575
+ f"Stdout: {e.stdout}\n"
576
+ f"Command: {' '.join(cmd)}"
577
+ ) from e
578
+
579
+ @mcp.tool()
580
+ def bedops_symmdiff(
581
+ files: List[Path],
582
+ chrom: Optional[str] = None,
583
+ ec: bool = False,
584
+ header: bool = False,
585
+ range_str: Optional[str] = None,
586
+ output_file: Optional[Path] = None,
587
+ ) -> dict:
588
+ """
589
+ Computes the symmetric difference of two or more BED files.
590
+
591
+ This corresponds to the `bedops -s` or `bedops --symmdiff` operation.
592
+ It returns regions that are unique to any of the input files (i.e., not
593
+ present in their intersection).
594
+
595
+ Args:
596
+ files: A list of two or more input BED/Starch files. All files must be sorted.
597
+ chrom: Process data for the given chromosome only.
598
+ ec: Error check input files (slower).
599
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
600
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
601
+ single value 'S' for symmetric padding (e.g., '100').
602
+ output_file: Optional path to save the output. If not provided,
603
+ output is returned as a string in the result dictionary.
604
+
605
+ Returns:
606
+ A dictionary containing the command executed, stdout, stderr, and a
607
+ list of output files generated.
608
+ """
609
+ if len(files) < 2:
610
+ raise ValueError("At least two input files must be provided for the symmetric difference operation.")
611
+ for file_path in files:
612
+ if not file_path.exists():
613
+ raise FileNotFoundError(f"Input file not found: {file_path}")
614
+
615
+ cmd = ["bedops"]
616
+
617
+ if chrom:
618
+ cmd.extend(["--chrom", chrom])
619
+ if ec:
620
+ cmd.append("--ec")
621
+ if header:
622
+ cmd.append("--header")
623
+ if range_str:
624
+ cmd.extend(["--range", range_str])
625
+
626
+ cmd.append("--symmdiff")
627
+ cmd.extend([str(p) for p in files])
628
+
629
+ try:
630
+ if output_file:
631
+ with open(output_file, "w") as f:
632
+ result = subprocess.run(
633
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
634
+ )
635
+ stdout_capture = ""
636
+ output_files_list = [str(output_file)]
637
+ else:
638
+ result = subprocess.run(
639
+ cmd, check=True, text=True, capture_output=True
640
+ )
641
+ stdout_capture = result.stdout
642
+ output_files_list = []
643
+
644
+ return {
645
+ "command_executed": " ".join(cmd),
646
+ "stdout": stdout_capture,
647
+ "stderr": result.stderr,
648
+ "output_files": output_files_list,
649
+ }
650
+ except subprocess.CalledProcessError as e:
651
+ raise RuntimeError(
652
+ f"bedops symmdiff failed with exit code {e.returncode}\n"
653
+ f"Stderr: {e.stderr}\n"
654
+ f"Stdout: {e.stdout}\n"
655
+ f"Command: {' '.join(cmd)}"
656
+ ) from e
657
+
658
+ @mcp.tool()
659
+ def bedops_everything(
660
+ files: List[Path],
661
+ chrom: Optional[str] = None,
662
+ ec: bool = False,
663
+ header: bool = False,
664
+ range_str: Optional[str] = None,
665
+ output_file: Optional[Path] = None,
666
+ ) -> dict:
667
+ """
668
+ Returns the union of all elements from input files without merging.
669
+
670
+ This corresponds to the `bedops -u` or `bedops --everything` operation.
671
+ It effectively concatenates the input files while maintaining sort order
672
+ and preserving all original columns.
673
+
674
+ Args:
675
+ files: A list of one or more input BED/Starch files. Must be sorted.
676
+ chrom: Process data for the given chromosome only.
677
+ ec: Error check input files (slower).
678
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
679
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
680
+ single value 'S' for symmetric padding (e.g., '100').
681
+ output_file: Optional path to save the output. If not provided,
682
+ output is returned as a string in the result dictionary.
683
+
684
+ Returns:
685
+ A dictionary containing the command executed, stdout, stderr, and a
686
+ list of output files generated.
687
+ """
688
+ if not files:
689
+ raise ValueError("At least one input file must be provided for the everything operation.")
690
+ for file_path in files:
691
+ if not file_path.exists():
692
+ raise FileNotFoundError(f"Input file not found: {file_path}")
693
+
694
+ cmd = ["bedops"]
695
+
696
+ if chrom:
697
+ cmd.extend(["--chrom", chrom])
698
+ if ec:
699
+ cmd.append("--ec")
700
+ if header:
701
+ cmd.append("--header")
702
+ if range_str:
703
+ cmd.extend(["--range", range_str])
704
+
705
+ cmd.append("--everything")
706
+ cmd.extend([str(p) for p in files])
707
+
708
+ try:
709
+ if output_file:
710
+ with open(output_file, "w") as f:
711
+ result = subprocess.run(
712
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
713
+ )
714
+ stdout_capture = ""
715
+ output_files_list = [str(output_file)]
716
+ else:
717
+ result = subprocess.run(
718
+ cmd, check=True, text=True, capture_output=True
719
+ )
720
+ stdout_capture = result.stdout
721
+ output_files_list = []
722
+
723
+ return {
724
+ "command_executed": " ".join(cmd),
725
+ "stdout": stdout_capture,
726
+ "stderr": result.stderr,
727
+ "output_files": output_files_list,
728
+ }
729
+ except subprocess.CalledProcessError as e:
730
+ raise RuntimeError(
731
+ f"bedops everything failed with exit code {e.returncode}\n"
732
+ f"Stderr: {e.stderr}\n"
733
+ f"Stdout: {e.stdout}\n"
734
+ f"Command: {' '.join(cmd)}"
735
+ ) from e
736
+
737
+ @mcp.tool()
738
+ def bedops_chop(
739
+ files: List[Path],
740
+ bp: int = 1,
741
+ stagger: Optional[int] = None,
742
+ exclusive_chop: bool = False,
743
+ chrom: Optional[str] = None,
744
+ ec: bool = False,
745
+ header: bool = False,
746
+ range_str: Optional[str] = None,
747
+ output_file: Optional[Path] = None,
748
+ ) -> dict:
749
+ """
750
+ Chops elements into fixed-size, potentially staggered sub-elements.
751
+
752
+ This corresponds to the `bedops -w` or `bedops --chop` operation.
753
+
754
+ Args:
755
+ files: A list of one or more input BED/Starch files. Must be sorted.
756
+ bp: The size in base pairs of each chopped element. Defaults to 1.
757
+ stagger: The stagger distance in nucleotides. If not set, no staggering is done.
758
+ exclusive_chop: If True, removes single-base elements that can result
759
+ from chopping (-x flag).
760
+ chrom: Process data for the given chromosome only.
761
+ ec: Error check input files (slower).
762
+ header: Accept headers (VCF, GFF, SAM, BED, WIG) in any input file.
763
+ range_str: Pad coordinates. Use 'L:R' format (e.g., '-10:20') or a
764
+ single value 'S' for symmetric padding (e.g., '100').
765
+ output_file: Optional path to save the output. If not provided,
766
+ output is returned as a string in the result dictionary.
767
+
768
+ Returns:
769
+ A dictionary containing the command executed, stdout, stderr, and a
770
+ list of output files generated.
771
+ """
772
+ if not files:
773
+ raise ValueError("At least one input file must be provided for the chop operation.")
774
+ for file_path in files:
775
+ if not file_path.exists():
776
+ raise FileNotFoundError(f"Input file not found: {file_path}")
777
+
778
+ cmd = ["bedops"]
779
+
780
+ if chrom:
781
+ cmd.extend(["--chrom", chrom])
782
+ if ec:
783
+ cmd.append("--ec")
784
+ if header:
785
+ cmd.append("--header")
786
+ if range_str:
787
+ cmd.extend(["--range", range_str])
788
+
789
+ cmd.extend(["--chop", str(bp)])
790
+ if stagger is not None:
791
+ cmd.extend(["--stagger", str(stagger)])
792
+ if exclusive_chop:
793
+ cmd.append("-x")
794
+
795
+ cmd.extend([str(p) for p in files])
796
+
797
+ try:
798
+ if output_file:
799
+ with open(output_file, "w") as f:
800
+ result = subprocess.run(
801
+ cmd, check=True, text=True, stdout=f, stderr=subprocess.PIPE
802
+ )
803
+ stdout_capture = ""
804
+ output_files_list = [str(output_file)]
805
+ else:
806
+ result = subprocess.run(
807
+ cmd, check=True, text=True, capture_output=True
808
+ )
809
+ stdout_capture = result.stdout
810
+ output_files_list = []
811
+
812
+ return {
813
+ "command_executed": " ".join(cmd),
814
+ "stdout": stdout_capture,
815
+ "stderr": result.stderr,
816
+ "output_files": output_files_list,
817
+ }
818
+ except subprocess.CalledProcessError as e:
819
+ raise RuntimeError(
820
+ f"bedops chop failed with exit code {e.returncode}\n"
821
+ f"Stderr: {e.stderr}\n"
822
+ f"Stdout: {e.stdout}\n"
823
+ f"Command: {' '.join(cmd)}"
824
+ ) from e
825
+
826
+ if __name__ == "__main__":
827
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bedops/app/bedops_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_bedops/app/bedops_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bedops'
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_bedops/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_bedops/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bedops:
5
+ build: .
6
+ image: mcp-bedops:latest
7
+ container_name: mcp-bedops
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bedops
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_bedops/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bedops
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_bedops/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_biobambam/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 biobambam via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda biobambam -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/biobambam_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/biobambam_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/biobambam_server.py"]
40
+
Biomni/mcp_generated/mcp_biobambam/app/biobambam_server.py ADDED
@@ -0,0 +1,497 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import List, Optional, Union
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ SERVER_NAME = 'local_biobambam'
8
+ mcp = FastMCP(SERVER_NAME)
9
+
10
+ @mcp.tool()
11
+ def bamsort(
12
+ input_file: str,
13
+ output_file: str,
14
+ memory_limit: str = "1G",
15
+ tmp_prefix: Optional[str] = None,
16
+ create_index: bool = False,
17
+ index_filename: Optional[str] = None,
18
+ sort_order: str = "coordinate",
19
+ threads: int = 1,
20
+ compression_level: int = 1,
21
+ recalculate_md_nm: bool = False,
22
+ reference_file: Optional[str] = None,
23
+ verbose: bool = False,
24
+ ) -> dict:
25
+ """
26
+ Sorts BAM files by coordinate or query name using bamsort.
27
+
28
+ Args:
29
+ input_file: Path to the input BAM file.
30
+ output_file: Path for the sorted output BAM file.
31
+ memory_limit: Main memory to use (e.g., '1G', '512M').
32
+ tmp_prefix: Temporary file prefix.
33
+ create_index: Whether to create an index for the output BAM file.
34
+ index_filename: Name for the index file.
35
+ sort_order: Sort order ('coordinate' or 'queryname').
36
+ threads: Number of threads to use.
37
+ compression_level: Compression level (0-9).
38
+ recalculate_md_nm: Recalculate MD and NM tags (requires reference).
39
+ reference_file: Reference FASTA file for MD/NM recalculation.
40
+ verbose: Enable verbose output.
41
+ """
42
+ in_path = Path(input_file)
43
+ out_path = Path(output_file)
44
+
45
+ if not in_path.exists():
46
+ return {"error": f"Input file {input_file} does not exist"}
47
+ if sort_order not in ["coordinate", "queryname"]:
48
+ return {"error": "sort_order must be 'coordinate' or 'queryname'"}
49
+ if not (0 <= compression_level <= 9):
50
+ return {"error": "compression_level must be between 0 and 9"}
51
+
52
+ cmd = ["bamsort", f"I={input_file}", f"O={output_file}"]
53
+ cmd.append(f"M={memory_limit}")
54
+ cmd.append(f"sortorder={sort_order}")
55
+ cmd.append(f"threads={threads}")
56
+ cmd.append(f"level={compression_level}")
57
+
58
+ if tmp_prefix: cmd.append(f"T={tmp_prefix}")
59
+ if create_index: cmd.append("index=1")
60
+ if index_filename: cmd.append(f"indexfilename={index_filename}")
61
+ if recalculate_md_nm:
62
+ if not reference_file:
63
+ return {"error": "reference_file is required for recalculate_md_nm"}
64
+ cmd.append("calmd=1")
65
+ cmd.append(f"reference={reference_file}")
66
+ if verbose: cmd.append("verbose=1")
67
+
68
+ try:
69
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
70
+ return {
71
+ "command_executed": " ".join(cmd),
72
+ "stdout": result.stdout,
73
+ "stderr": result.stderr,
74
+ "output_files": [output_file]
75
+ }
76
+ except subprocess.CalledProcessError as e:
77
+ return {"error": e.stderr, "command_executed": " ".join(cmd), "return_code": e.returncode}
78
+
79
+ @mcp.tool()
80
+ def bammarkduplicates(
81
+ input_file: str,
82
+ output_file: str,
83
+ metrics_file: Optional[str] = None,
84
+ tmp_prefix: Optional[str] = None,
85
+ create_index: bool = False,
86
+ index_filename: Optional[str] = None,
87
+ remove_duplicates: bool = False,
88
+ compression_level: int = 1,
89
+ verbose: bool = False,
90
+ ) -> dict:
91
+ """
92
+ Marks or removes duplicate reads in a coordinate-sorted BAM file.
93
+
94
+ Args:
95
+ input_file: Input BAM file (must be coordinate sorted).
96
+ output_file: Output BAM file with duplicates marked.
97
+ metrics_file: Path for the output metrics file.
98
+ tmp_prefix: Temporary file prefix.
99
+ create_index: Create index for output BAM.
100
+ index_filename: Name for the index file.
101
+ remove_duplicates: Remove duplicate reads instead of marking.
102
+ compression_level: Compression level (0-9).
103
+ verbose: Enable verbose output.
104
+ """
105
+ if not Path(input_file).exists():
106
+ return {"error": f"Input file {input_file} does not exist"}
107
+
108
+ cmd = ["bammarkduplicates", f"I={input_file}", f"O={output_file}"]
109
+ cmd.append(f"level={compression_level}")
110
+
111
+ if metrics_file: cmd.append(f"M={metrics_file}")
112
+ if tmp_prefix: cmd.append(f"T={tmp_prefix}")
113
+ if create_index: cmd.append("index=1")
114
+ if index_filename: cmd.append(f"indexfilename={index_filename}")
115
+ if remove_duplicates: cmd.append("rmdup=1")
116
+ if verbose: cmd.append("verbose=1")
117
+
118
+ try:
119
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
120
+ outputs = [output_file]
121
+ if metrics_file: outputs.append(metrics_file)
122
+ return {
123
+ "command_executed": " ".join(cmd),
124
+ "stdout": result.stdout,
125
+ "stderr": result.stderr,
126
+ "output_files": outputs
127
+ }
128
+ except subprocess.CalledProcessError as e:
129
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
130
+
131
+ @mcp.tool()
132
+ def bamsormadup(
133
+ input_file: str,
134
+ output_file: str,
135
+ tmp_prefix: Optional[str] = None,
136
+ memory_limit: str = "1G",
137
+ threads: int = 1,
138
+ compression_level: int = 1,
139
+ create_index: bool = False,
140
+ index_filename: Optional[str] = None,
141
+ remove_duplicates: bool = False,
142
+ verbose: bool = False,
143
+ ) -> dict:
144
+ """
145
+ Combined sorting and duplicate marking in a single pass.
146
+
147
+ Args:
148
+ input_file: Input BAM file.
149
+ output_file: Output BAM file.
150
+ tmp_prefix: Temporary file prefix.
151
+ memory_limit: Memory limit.
152
+ threads: Number of threads.
153
+ compression_level: Compression level (0-9).
154
+ create_index: Create index for output.
155
+ index_filename: Index filename.
156
+ remove_duplicates: Remove duplicates.
157
+ verbose: Enable verbose output.
158
+ """
159
+ if not Path(input_file).exists():
160
+ return {"error": f"Input file {input_file} does not exist"}
161
+
162
+ cmd = ["bamsormadup", f"I={input_file}", f"O={output_file}"]
163
+ cmd.append(f"M={memory_limit}")
164
+ cmd.append(f"threads={threads}")
165
+ cmd.append(f"level={compression_level}")
166
+
167
+ if tmp_prefix: cmd.append(f"T={tmp_prefix}")
168
+ if create_index: cmd.append("index=1")
169
+ if index_filename: cmd.append(f"indexfilename={index_filename}")
170
+ if remove_duplicates: cmd.append("rmdup=1")
171
+ if verbose: cmd.append("verbose=1")
172
+
173
+ try:
174
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
175
+ return {
176
+ "command_executed": " ".join(cmd),
177
+ "stdout": result.stdout,
178
+ "stderr": result.stderr,
179
+ "output_files": [output_file]
180
+ }
181
+ except subprocess.CalledProcessError as e:
182
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
183
+
184
+ @mcp.tool()
185
+ def bamtofastq(
186
+ input_file: str,
187
+ fastq_1: str,
188
+ fastq_2: Optional[str] = None,
189
+ singles: Optional[str] = None,
190
+ orphans_1: Optional[str] = None,
191
+ orphans_2: Optional[str] = None,
192
+ collate: bool = False,
193
+ gzip: bool = False,
194
+ compression_level: int = 1,
195
+ reads_per_file: int = 0,
196
+ verbose: bool = False,
197
+ ) -> dict:
198
+ """
199
+ Converts BAM files to FASTQ format.
200
+
201
+ Args:
202
+ input_file: Input BAM file.
203
+ fastq_1: Output FASTQ file (Read 1).
204
+ fastq_2: Output FASTQ file (Read 2).
205
+ singles: Output FASTQ for single-end reads.
206
+ orphans_1: Output FASTQ for orphan Read 1.
207
+ orphans_2: Output FASTQ for orphan Read 2.
208
+ collate: Collate reads by name before conversion.
209
+ gzip: Compress output with gzip.
210
+ compression_level: Gzip compression level (1-9).
211
+ reads_per_file: Split output into files with N reads (0 = no split).
212
+ verbose: Enable verbose output.
213
+ """
214
+ if not Path(input_file).exists():
215
+ return {"error": f"Input file {input_file} does not exist"}
216
+
217
+ cmd = ["bamtofastq", f"filename={input_file}", f"F={fastq_1}"]
218
+ outputs = [fastq_1]
219
+
220
+ if fastq_2:
221
+ cmd.append(f"F2={fastq_2}")
222
+ outputs.append(fastq_2)
223
+ if singles:
224
+ cmd.append(f"S={singles}")
225
+ outputs.append(singles)
226
+ if orphans_1:
227
+ cmd.append(f"O={orphans_1}")
228
+ outputs.append(orphans_1)
229
+ if orphans_2:
230
+ cmd.append(f"O2={orphans_2}")
231
+ outputs.append(orphans_2)
232
+ if collate: cmd.append("collate=1")
233
+ if gzip: cmd.append("gz=1")
234
+ if compression_level != 1: cmd.append(f"level={compression_level}")
235
+ if reads_per_file > 0: cmd.append(f"readsperfile={reads_per_file}")
236
+ if verbose: cmd.append("verbose=1")
237
+
238
+ try:
239
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
240
+ return {
241
+ "command_executed": " ".join(cmd),
242
+ "stdout": result.stdout,
243
+ "stderr": result.stderr,
244
+ "output_files": outputs
245
+ }
246
+ except subprocess.CalledProcessError as e:
247
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
248
+
249
+ @mcp.tool()
250
+ def bamcollate2(
251
+ input_file: str,
252
+ output_file: str,
253
+ tmp_prefix: Optional[str] = None,
254
+ compression_level: int = 1,
255
+ verbose: bool = False,
256
+ ) -> dict:
257
+ """
258
+ Collates a BAM file by read name.
259
+
260
+ Args:
261
+ input_file: Input BAM file.
262
+ output_file: Output collated BAM file.
263
+ tmp_prefix: Temporary file prefix.
264
+ compression_level: Compression level (0-9).
265
+ verbose: Enable verbose output.
266
+ """
267
+ if not Path(input_file).exists():
268
+ return {"error": f"Input file {input_file} does not exist"}
269
+
270
+ cmd = ["bamcollate2", f"I={input_file}", f"O={output_file}"]
271
+ cmd.append(f"level={compression_level}")
272
+ if tmp_prefix: cmd.append(f"T={tmp_prefix}")
273
+ if verbose: cmd.append("verbose=1")
274
+
275
+ try:
276
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
277
+ return {
278
+ "command_executed": " ".join(cmd),
279
+ "stdout": result.stdout,
280
+ "stderr": result.stderr,
281
+ "output_files": [output_file]
282
+ }
283
+ except subprocess.CalledProcessError as e:
284
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
285
+
286
+ @mcp.tool()
287
+ def bamadapterclip(
288
+ input_file: str,
289
+ output_file: str,
290
+ threads: int = 1,
291
+ compression_level: int = 1,
292
+ verbose: bool = False,
293
+ ) -> dict:
294
+ """
295
+ Clips adapter sequences from reads in a BAM file.
296
+
297
+ Args:
298
+ input_file: Input BAM file.
299
+ output_file: Output clipped BAM file.
300
+ threads: Number of threads.
301
+ compression_level: Compression level (0-9).
302
+ verbose: Enable verbose output.
303
+ """
304
+ if not Path(input_file).exists():
305
+ return {"error": f"Input file {input_file} does not exist"}
306
+
307
+ cmd = ["bamadapterclip", f"I={input_file}", f"O={output_file}"]
308
+ cmd.append(f"threads={threads}")
309
+ cmd.append(f"level={compression_level}")
310
+ if verbose: cmd.append("verbose=1")
311
+
312
+ try:
313
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
314
+ return {
315
+ "command_executed": " ".join(cmd),
316
+ "stdout": result.stdout,
317
+ "stderr": result.stderr,
318
+ "output_files": [output_file]
319
+ }
320
+ except subprocess.CalledProcessError as e:
321
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
322
+
323
+ @mcp.tool()
324
+ def bamreset(
325
+ input_file: str,
326
+ output_file: str,
327
+ compression_level: int = 1,
328
+ reset_flags: bool = False,
329
+ verbose: bool = False,
330
+ ) -> dict:
331
+ """
332
+ Resets alignment information in a BAM file.
333
+
334
+ Args:
335
+ input_file: Input BAM file.
336
+ output_file: Output reset BAM file.
337
+ compression_level: Compression level (0-9).
338
+ reset_flags: Reset flags to 0.
339
+ verbose: Enable verbose output.
340
+ """
341
+ if not Path(input_file).exists():
342
+ return {"error": f"Input file {input_file} does not exist"}
343
+
344
+ cmd = ["bamreset", f"I={input_file}", f"O={output_file}"]
345
+ cmd.append(f"level={compression_level}")
346
+ if reset_flags: cmd.append("resetflags=1")
347
+ if verbose: cmd.append("verbose=1")
348
+
349
+ try:
350
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
351
+ return {
352
+ "command_executed": " ".join(cmd),
353
+ "stdout": result.stdout,
354
+ "stderr": result.stderr,
355
+ "output_files": [output_file]
356
+ }
357
+ except subprocess.CalledProcessError as e:
358
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
359
+
360
+ @mcp.tool()
361
+ def bamchecksort(
362
+ input_file: str,
363
+ verbose: bool = False,
364
+ ) -> dict:
365
+ """
366
+ Checks if a BAM file is sorted.
367
+
368
+ Args:
369
+ input_file: Input BAM file.
370
+ verbose: Enable verbose output.
371
+ """
372
+ if not Path(input_file).exists():
373
+ return {"error": f"Input file {input_file} does not exist"}
374
+
375
+ cmd = ["bamchecksort", f"I={input_file}"]
376
+ if verbose: cmd.append("verbose=1")
377
+
378
+ try:
379
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
380
+ return {
381
+ "command_executed": " ".join(cmd),
382
+ "stdout": result.stdout,
383
+ "stderr": result.stderr,
384
+ "is_sorted": "sorted" in result.stdout.lower() or result.returncode == 0
385
+ }
386
+ except subprocess.CalledProcessError as e:
387
+ return {"error": e.stderr, "command_executed": " ".join(cmd), "is_sorted": False}
388
+
389
+ @mcp.tool()
390
+ def bamrecompress(
391
+ input_file: str,
392
+ output_file: str,
393
+ compression_level: int = 1,
394
+ threads: int = 1,
395
+ verbose: bool = False,
396
+ ) -> dict:
397
+ """
398
+ Recompresses a BAM file.
399
+
400
+ Args:
401
+ input_file: Input BAM file.
402
+ output_file: Output recompressed BAM file.
403
+ compression_level: Compression level (0-9).
404
+ threads: Number of threads.
405
+ verbose: Enable verbose output.
406
+ """
407
+ if not Path(input_file).exists():
408
+ return {"error": f"Input file {input_file} does not exist"}
409
+
410
+ cmd = ["bamrecompress", f"I={input_file}", f"O={output_file}"]
411
+ cmd.append(f"level={compression_level}")
412
+ cmd.append(f"threads={threads}")
413
+ if verbose: cmd.append("verbose=1")
414
+
415
+ try:
416
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
417
+ return {
418
+ "command_executed": " ".join(cmd),
419
+ "stdout": result.stdout,
420
+ "stderr": result.stderr,
421
+ "output_files": [output_file]
422
+ }
423
+ except subprocess.CalledProcessError as e:
424
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
425
+
426
+ @mcp.tool()
427
+ def bamfilterrg(
428
+ input_file: str,
429
+ output_file: str,
430
+ read_group: str,
431
+ compression_level: int = 1,
432
+ verbose: bool = False,
433
+ ) -> dict:
434
+ """
435
+ Filters a BAM file by read group.
436
+
437
+ Args:
438
+ input_file: Input BAM file.
439
+ output_file: Output filtered BAM file.
440
+ read_group: Read group ID to keep.
441
+ compression_level: Compression level (0-9).
442
+ verbose: Enable verbose output.
443
+ """
444
+ if not Path(input_file).exists():
445
+ return {"error": f"Input file {input_file} does not exist"}
446
+
447
+ cmd = ["bamfilterrg", f"I={input_file}", f"O={output_file}", f"rg={read_group}"]
448
+ cmd.append(f"level={compression_level}")
449
+ if verbose: cmd.append("verbose=1")
450
+
451
+ try:
452
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
453
+ return {
454
+ "command_executed": " ".join(cmd),
455
+ "stdout": result.stdout,
456
+ "stderr": result.stderr,
457
+ "output_files": [output_file]
458
+ }
459
+ except subprocess.CalledProcessError as e:
460
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
461
+
462
+ @mcp.tool()
463
+ def bamfillquery(
464
+ input_file: str,
465
+ output_file: str,
466
+ compression_level: int = 1,
467
+ verbose: bool = False,
468
+ ) -> dict:
469
+ """
470
+ Fills query information in a BAM file.
471
+
472
+ Args:
473
+ input_file: Input BAM file.
474
+ output_file: Output BAM file.
475
+ compression_level: Compression level (0-9).
476
+ verbose: Enable verbose output.
477
+ """
478
+ if not Path(input_file).exists():
479
+ return {"error": f"Input file {input_file} does not exist"}
480
+
481
+ cmd = ["bamfillquery", f"I={input_file}", f"O={output_file}"]
482
+ cmd.append(f"level={compression_level}")
483
+ if verbose: cmd.append("verbose=1")
484
+
485
+ try:
486
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
487
+ return {
488
+ "command_executed": " ".join(cmd),
489
+ "stdout": result.stdout,
490
+ "stderr": result.stderr,
491
+ "output_files": [output_file]
492
+ }
493
+ except subprocess.CalledProcessError as e:
494
+ return {"error": e.stderr, "command_executed": " ".join(cmd)}
495
+
496
+ if __name__ == "__main__":
497
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_biobambam/app/biobambam_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_biobambam/app/biobambam_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_biobambam'
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_biobambam/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
Biomni/mcp_generated/mcp_biobambam/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-biobambam:
5
+ build: .
6
+ image: mcp-biobambam:latest
7
+ container_name: mcp-biobambam
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=biobambam
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_biobambam/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - biobambam
9
+ - python=3.10
10
+
Biomni/mcp_generated/mcp_biobambam/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
Biomni/mcp_generated/mcp_bioconductor-affyio/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-affyio via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-affyio -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-affyio_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-affyio_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-affyio_server.py"]
40
+
Biomni/mcp_generated/mcp_bioconductor-affyio/app/bioconductor-affyio_server.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, List, Dict, Any
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ SERVER_NAME = 'local_bioconductor_affyio'
10
+ mcp = FastMCP(SERVER_NAME)
11
+
12
+ @mcp.tool()
13
+ def affyio_read_celfile_header(
14
+ filename: str,
15
+ info: str = "full"
16
+ ) -> Dict[str, Any]:
17
+ """
18
+ Read the header information from an Affymetrix CEL file.
19
+
20
+ Args:
21
+ filename: Path to the CEL file.
22
+ info: Level of information to extract ('full' or 'minimal').
23
+ """
24
+ file_path = Path(filename)
25
+ if not file_path.exists():
26
+ return {"error": f"File not found: {filename}"}
27
+
28
+ if info not in ["full", "minimal"]:
29
+ info = "full"
30
+
31
+ # R script to extract header and convert to JSON
32
+ r_command = f"""
33
+ library(affyio)
34
+ library(jsonlite)
35
+ header <- read.celfile.header("{str(file_path)}", info="{info}")
36
+ cat(toJSON(header, auto_unbox = TRUE))
37
+ """
38
+
39
+ try:
40
+ result = subprocess.run(
41
+ ["Rscript", "-e", r_command],
42
+ capture_output=True,
43
+ text=True,
44
+ check=True
45
+ )
46
+ return {
47
+ "command_executed": f"read.celfile.header('{filename}')",
48
+ "header_data": json.loads(result.stdout),
49
+ "stderr": result.stderr
50
+ }
51
+ except subprocess.CalledProcessError as e:
52
+ return {
53
+ "error": "R execution failed",
54
+ "stdout": e.stdout,
55
+ "stderr": e.stderr,
56
+ "command_executed": e.cmd
57
+ }
58
+ except json.JSONDecodeError:
59
+ return {"error": "Failed to parse R output", "raw_stdout": result.stdout}
60
+
61
+ @mcp.tool()
62
+ def affyio_read_celfile(
63
+ filename: str,
64
+ output_csv: str,
65
+ intensity_only: bool = True
66
+ ) -> Dict[str, Any]:
67
+ """
68
+ Read an Affymetrix CEL file and export the intensity data to a CSV file.
69
+
70
+ Args:
71
+ filename: Path to the CEL file.
72
+ output_csv: Path where the intensity data should be saved.
73
+ intensity_only: If True, only the intensity values are extracted.
74
+ """
75
+ input_path = Path(filename)
76
+ output_path = Path(output_csv)
77
+
78
+ if not input_path.exists():
79
+ return {"error": f"Input file not found: {filename}"}
80
+
81
+ intensity_val = "TRUE" if intensity_only else "FALSE"
82
+
83
+ r_command = f"""
84
+ library(affyio)
85
+ data <- read.celfile("{str(input_path)}", intensity.only={intensity_val})
86
+ # If intensity.only is TRUE, it returns a list with INTENSITY
87
+ # If FALSE, it returns a list with INTENSITY, MASKS, OUTLIERS
88
+ write.csv(data$INTENSITY, "{str(output_path)}")
89
+ """
90
+
91
+ try:
92
+ subprocess.run(
93
+ ["Rscript", "-e", r_command],
94
+ capture_output=True,
95
+ text=True,
96
+ check=True
97
+ )
98
+ return {
99
+ "command_executed": f"read.celfile('{filename}')",
100
+ "status": "Success",
101
+ "output_file": str(output_path)
102
+ }
103
+ except subprocess.CalledProcessError as e:
104
+ return {
105
+ "error": "R execution failed",
106
+ "stderr": e.stderr,
107
+ "stdout": e.stdout
108
+ }
109
+
110
+ @mcp.tool()
111
+ def affyio_check_cdf_type(
112
+ filename: str
113
+ ) -> Dict[str, Any]:
114
+ """
115
+ Identify the type of an Affymetrix CDF file (e.g., text, binary, gzipped).
116
+
117
+ Args:
118
+ filename: Path to the CDF file.
119
+ """
120
+ file_path = Path(filename)
121
+ if not file_path.exists():
122
+ return {"error": f"File not found: {filename}"}
123
+
124
+ r_command = f"""
125
+ library(affyio)
126
+ type <- check.cdf.type("{str(file_path)}")
127
+ cat(type)
128
+ """
129
+
130
+ try:
131
+ result = subprocess.run(
132
+ ["Rscript", "-e", r_command],
133
+ capture_output=True,
134
+ text=True,
135
+ check=True
136
+ )
137
+ return {
138
+ "command_executed": f"check.cdf.type('{filename}')",
139
+ "cdf_type": result.stdout.strip(),
140
+ "stderr": result.stderr
141
+ }
142
+ except subprocess.CalledProcessError as e:
143
+ return {"error": "R execution failed", "stderr": e.stderr}
144
+
145
+ @mcp.tool()
146
+ def affyio_read_cdffile_summary(
147
+ filename: str
148
+ ) -> Dict[str, Any]:
149
+ """
150
+ Read an Affymetrix CDF file and return a summary of its contents.
151
+ Note: Full CDF lists are extremely large; this tool returns structural metadata.
152
+
153
+ Args:
154
+ filename: Path to the CDF file.
155
+ """
156
+ file_path = Path(filename)
157
+ if not file_path.exists():
158
+ return {"error": f"File not found: {filename}"}
159
+
160
+ r_command = f"""
161
+ library(affyio)
162
+ library(jsonlite)
163
+ cdf_data <- read.cdffile.list("{str(file_path)}")
164
+ summary_info <- list(
165
+ filename = "{filename}",
166
+ num_probesets = length(cdf_data),
167
+ probeset_names = head(names(cdf_data), 10)
168
+ )
169
+ cat(toJSON(summary_info, auto_unbox = TRUE))
170
+ """
171
+
172
+ try:
173
+ result = subprocess.run(
174
+ ["Rscript", "-e", r_command],
175
+ capture_output=True,
176
+ text=True,
177
+ check=True
178
+ )
179
+ return {
180
+ "command_executed": f"read.cdffile.list('{filename}')",
181
+ "summary": json.loads(result.stdout),
182
+ "stderr": result.stderr
183
+ }
184
+ except subprocess.CalledProcessError as e:
185
+ return {"error": "R execution failed", "stderr": e.stderr}
186
+
187
+ @mcp.tool()
188
+ def affyio_get_package_version() -> Dict[str, Any]:
189
+ """
190
+ Check the installed version of the affyio package and its dependencies.
191
+ """
192
+ r_command = """
193
+ library(affyio)
194
+ v <- as.character(packageVersion("affyio"))
195
+ cat(v)
196
+ """
197
+ try:
198
+ result = subprocess.run(
199
+ ["Rscript", "-e", r_command],
200
+ capture_output=True,
201
+ text=True,
202
+ check=True
203
+ )
204
+ return {
205
+ "package": "affyio",
206
+ "version": result.stdout.strip(),
207
+ "r_version": subprocess.run(["Rscript", "--version"], capture_output=True, text=True).stderr.strip()
208
+ }
209
+ except subprocess.CalledProcessError as e:
210
+ return {
211
+ "error": "Could not determine package version. Ensure bioconductor-affyio is installed.",
212
+ "stderr": e.stderr
213
+ }
214
+
215
+ if __name__ == "__main__":
216
+ mcp.run(transport="stdio")
Biomni/mcp_generated/mcp_bioconductor-affyio/app/bioconductor-affyio_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-affyio/app/bioconductor-affyio_server.py')
11
+ LOCAL_SERVER = Path(__file__).with_name(SOURCE_SERVER.name)
12
+ SERVER_NAME = 'biosci_bioconductor_affyio'
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-affyio/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+