czty commited on
Commit
faccee7
·
verified ·
1 Parent(s): eb88e90

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/Dockerfile +40 -0
  2. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_server.py +270 -0
  3. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_shim_server.py +45 -0
  4. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/requirements.txt +1 -0
  5. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/docker-compose.yml +22 -0
  6. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/environment.yaml +10 -0
  7. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/requirements.txt +2 -0
  8. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/Dockerfile +40 -0
  9. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_server.py +398 -0
  10. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_shim_server.py +45 -0
  11. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/requirements.txt +1 -0
  12. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/docker-compose.yml +22 -0
  13. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/environment.yaml +10 -0
  14. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/requirements.txt +2 -0
  15. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/Dockerfile +40 -0
  16. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_server.py +336 -0
  17. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_shim_server.py +45 -0
  18. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/requirements.txt +1 -0
  19. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/docker-compose.yml +22 -0
  20. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/environment.yaml +10 -0
  21. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/requirements.txt +2 -0
  22. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/Dockerfile +40 -0
  23. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_server.py +278 -0
  24. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_shim_server.py +45 -0
  25. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/requirements.txt +1 -0
  26. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/docker-compose.yml +22 -0
  27. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/environment.yaml +10 -0
  28. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/requirements.txt +2 -0
  29. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocbaseutils/environment.yaml +10 -0
  30. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocbaseutils/requirements.txt +2 -0
  31. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/Dockerfile +40 -0
  32. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/bioconductor-curatedatlasqueryr_shim_server.py +45 -0
  33. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/requirements.txt +1 -0
  34. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/docker-compose.yml +22 -0
  35. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/requirements.txt +2 -0
  36. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-delayedmatrixstats/requirements.txt +2 -0
  37. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ebseq/requirements.txt +2 -0
  38. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/Dockerfile +40 -0
  39. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_server.py +178 -0
  40. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_shim_server.py +45 -0
  41. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/requirements.txt +1 -0
  42. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/docker-compose.yml +22 -0
  43. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/environment.yaml +10 -0
  44. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/requirements.txt +2 -0
  45. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genefilter/app/requirements.txt +1 -0
  46. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genefilter/docker-compose.yml +22 -0
  47. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/Dockerfile +40 -0
  48. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_server.py +360 -0
  49. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_shim_server.py +45 -0
  50. BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/docker-compose.yml +22 -0
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/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 arvados-cwl-runner via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda arvados-cwl-runner -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/arvados-cwl-runner_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/arvados-cwl-runner_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/arvados-cwl-runner_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_server.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional, List, Union
5
+
6
+ @mcp.tool()
7
+ def arvados_cwl_runner(
8
+ workflow: str,
9
+ job_order: Optional[str] = None,
10
+ project_uuid: Optional[str] = None,
11
+ name: Optional[str] = None,
12
+ output_name: Optional[str] = None,
13
+ local: bool = False,
14
+ api: str = "containers",
15
+ eval_timeout: float = 20.0,
16
+ log_level: str = "INFO",
17
+ enable_reuse: bool = True,
18
+ submit: bool = True,
19
+ wait: bool = True,
20
+ priority: int = 1,
21
+ cluster_id: Optional[str] = None,
22
+ storage_classes: str = "default",
23
+ tmpdir_prefix: Optional[str] = None,
24
+ debug: bool = False,
25
+ ):
26
+ """
27
+ Run a CWL workflow on Arvados or locally.
28
+
29
+ Args:
30
+ workflow: Path to the CWL workflow file (.cwl).
31
+ job_order: Path to the input parameters file (YAML or JSON).
32
+ project_uuid: Arvados project UUID where the workflow should run.
33
+ name: Name for the pipeline instance or container request.
34
+ output_name: Name for the output collection.
35
+ local: Run the workflow locally instead of on the Arvados cluster.
36
+ api: Arvados API to use (containers or jobs). Default is containers.
37
+ eval_timeout: Time to wait for CWL expression evaluation (seconds).
38
+ log_level: Logging level (DEBUG, INFO, WARNING, ERROR).
39
+ enable_reuse: Enable job/container reuse.
40
+ submit: Submit the workflow to Arvados (True) or run it in the foreground (False).
41
+ wait: Wait for the workflow to complete before exiting.
42
+ priority: Workflow priority (1-1000).
43
+ cluster_id: Specific Arvados cluster ID to submit to.
44
+ storage_classes: Comma-separated list of storage classes for outputs.
45
+ tmpdir_prefix: Path prefix for temporary directories.
46
+ debug: Enable debug logging and keep temporary files.
47
+ """
48
+
49
+ # Input validation
50
+ workflow_path = Path(workflow)
51
+ if not workflow_path.exists():
52
+ return {"error": f"Workflow file not found: {workflow}"}
53
+
54
+ cmd = ["arvados-cwl-runner"]
55
+
56
+ # Boolean flags
57
+ if local:
58
+ cmd.append("--local")
59
+ if not enable_reuse:
60
+ cmd.append("--disable-reuse")
61
+ if not submit:
62
+ cmd.append("--no-submit")
63
+ if not wait:
64
+ cmd.append("--no-wait")
65
+ if debug:
66
+ cmd.append("--debug")
67
+
68
+ # String/Value parameters
69
+ cmd.extend(["--api", api])
70
+ cmd.extend(["--eval-timeout", str(eval_timeout)])
71
+ cmd.extend(["--log-level", log_level])
72
+ cmd.extend(["--priority", str(priority)])
73
+ cmd.extend(["--collection-storage-classes", storage_classes])
74
+
75
+ if project_uuid:
76
+ cmd.extend(["--project-uuid", project_uuid])
77
+ if name:
78
+ cmd.extend(["--name", name])
79
+ if output_name:
80
+ cmd.extend(["--output-name", output_name])
81
+ if cluster_id:
82
+ cmd.extend(["--cluster-id", cluster_id])
83
+ if tmpdir_prefix:
84
+ cmd.extend(["--tmpdir-prefix", tmpdir_prefix])
85
+
86
+ # Positional arguments
87
+ cmd.append(str(workflow_path))
88
+
89
+ if job_order:
90
+ job_order_path = Path(job_order)
91
+ if not job_order_path.exists():
92
+ return {"error": f"Job order file not found: {job_order}"}
93
+ cmd.append(str(job_order_path))
94
+
95
+ try:
96
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
97
+ return {
98
+ "command_executed": " ".join(cmd),
99
+ "stdout": result.stdout,
100
+ "stderr": result.stderr,
101
+ "status": "success"
102
+ }
103
+ except subprocess.CalledProcessError as e:
104
+ return {
105
+ "command_executed": " ".join(cmd),
106
+ "stdout": e.stdout,
107
+ "stderr": e.stderr,
108
+ "error": str(e),
109
+ "status": "failed"
110
+ }
111
+
112
+ @mcp.tool()
113
+ def arvados_cwl_validate(
114
+ workflow: str,
115
+ ):
116
+ """
117
+ Validate a CWL workflow file for syntax and Arvados compatibility.
118
+
119
+ Args:
120
+ workflow: Path to the CWL workflow file.
121
+ """
122
+ workflow_path = Path(workflow)
123
+ if not workflow_path.exists():
124
+ return {"error": f"Workflow file not found: {workflow}"}
125
+
126
+ cmd = ["arvados-cwl-runner", "--validate", str(workflow_path)]
127
+
128
+ try:
129
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
130
+ return {
131
+ "command_executed": " ".join(cmd),
132
+ "stdout": result.stdout,
133
+ "stderr": result.stderr,
134
+ "status": "valid"
135
+ }
136
+ except subprocess.CalledProcessError as e:
137
+ return {
138
+ "command_executed": " ".join(cmd),
139
+ "stdout": e.stdout,
140
+ "stderr": e.stderr,
141
+ "error": "Validation failed",
142
+ "status": "invalid"
143
+ }
144
+
145
+ @mcp.tool()
146
+ def arvados_cwl_create_workflow(
147
+ workflow: str,
148
+ project_uuid: Optional[str] = None,
149
+ name: Optional[str] = None,
150
+ description: Optional[str] = None,
151
+ ):
152
+ """
153
+ Register a CWL workflow in Arvados as a reusable Workflow object.
154
+
155
+ Args:
156
+ workflow: Path to the CWL workflow file.
157
+ project_uuid: Arvados project UUID where the workflow should be stored.
158
+ name: Name for the workflow object in Arvados.
159
+ description: Description for the workflow.
160
+ """
161
+ workflow_path = Path(workflow)
162
+ if not workflow_path.exists():
163
+ return {"error": f"Workflow file not found: {workflow}"}
164
+
165
+ cmd = ["arvados-cwl-runner", "--create-workflow"]
166
+
167
+ if project_uuid:
168
+ cmd.extend(["--project-uuid", project_uuid])
169
+ if name:
170
+ cmd.extend(["--name", name])
171
+ if description:
172
+ cmd.extend(["--description", description])
173
+
174
+ cmd.append(str(workflow_path))
175
+
176
+ try:
177
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
178
+ return {
179
+ "command_executed": " ".join(cmd),
180
+ "stdout": result.stdout,
181
+ "stderr": result.stderr,
182
+ "status": "success"
183
+ }
184
+ except subprocess.CalledProcessError as e:
185
+ return {
186
+ "command_executed": " ".join(cmd),
187
+ "stdout": e.stdout,
188
+ "stderr": e.stderr,
189
+ "error": str(e),
190
+ "status": "failed"
191
+ }
192
+
193
+ @mcp.tool()
194
+ def arvados_cwl_update_workflow(
195
+ workflow_uuid: str,
196
+ workflow: str,
197
+ name: Optional[str] = None,
198
+ description: Optional[str] = None,
199
+ ):
200
+ """
201
+ Update an existing Arvados Workflow object with a new CWL definition.
202
+
203
+ Args:
204
+ workflow_uuid: The UUID of the Arvados Workflow object to update.
205
+ workflow: Path to the new CWL workflow file.
206
+ name: New name for the workflow object.
207
+ description: New description for the workflow.
208
+ """
209
+ workflow_path = Path(workflow)
210
+ if not workflow_path.exists():
211
+ return {"error": f"Workflow file not found: {workflow}"}
212
+
213
+ cmd = ["arvados-cwl-runner", "--update-workflow", workflow_uuid]
214
+
215
+ if name:
216
+ cmd.extend(["--name", name])
217
+ if description:
218
+ cmd.extend(["--description", description])
219
+
220
+ cmd.append(str(workflow_path))
221
+
222
+ try:
223
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
224
+ return {
225
+ "command_executed": " ".join(cmd),
226
+ "stdout": result.stdout,
227
+ "stderr": result.stderr,
228
+ "status": "success"
229
+ }
230
+ except subprocess.CalledProcessError as e:
231
+ return {
232
+ "command_executed": " ".join(cmd),
233
+ "stdout": e.stdout,
234
+ "stderr": e.stderr,
235
+ "error": str(e),
236
+ "status": "failed"
237
+ }
238
+
239
+ @mcp.tool()
240
+ def arvados_cwl_create_template(
241
+ workflow: str,
242
+ ):
243
+ """
244
+ Create an Arvados Pipeline Template from a CWL workflow (legacy API).
245
+
246
+ Args:
247
+ workflow: Path to the CWL workflow file.
248
+ """
249
+ workflow_path = Path(workflow)
250
+ if not workflow_path.exists():
251
+ return {"error": f"Workflow file not found: {workflow}"}
252
+
253
+ cmd = ["arvados-cwl-runner", "--create-template", str(workflow_path)]
254
+
255
+ try:
256
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
257
+ return {
258
+ "command_executed": " ".join(cmd),
259
+ "stdout": result.stdout,
260
+ "stderr": result.stderr,
261
+ "status": "success"
262
+ }
263
+ except subprocess.CalledProcessError as e:
264
+ return {
265
+ "command_executed": " ".join(cmd),
266
+ "stdout": e.stdout,
267
+ "stderr": e.stderr,
268
+ "error": str(e),
269
+ "status": "failed"
270
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_server.py')
11
+ SERVER_NAME = 'biosci_arvados_cwl_runner'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-arvados-cwl-runner:
5
+ build: .
6
+ image: mcp-arvados-cwl-runner:latest
7
+ container_name: mcp-arvados-cwl-runner
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=arvados-cwl-runner
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - arvados-cwl-runner
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/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 auspice via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda auspice -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/auspice_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/auspice_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/auspice_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_server.py ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Dict, List, Optional, Any
4
+
5
+ # This is a placeholder for the MCP decorator.
6
+ # In a real MCP environment, this would be provided by the MCP framework.
7
+ def tool():
8
+ def decorator(f):
9
+ return f
10
+ return decorator
11
+
12
+ mcp = type("mcp", (), {"tool": tool})()
13
+
14
+
15
+ @mcp.tool()
16
+ def view(
17
+ dataset_dir: Optional[List[Path]] = None,
18
+ host: str = "localhost",
19
+ port: int = 4000,
20
+ verbose: bool = False,
21
+ allow_remote_access: bool = False,
22
+ handlers: Optional[Path] = None,
23
+ config: Optional[Path] = None,
24
+ extend: Optional[Path] = None,
25
+ ) -> Dict[str, str]:
26
+ """
27
+ Starts the Auspice server to view and explore phylogenomic data.
28
+ Note: This command starts a long-running server process and may not be suitable
29
+ for automated workflows that expect a command to terminate.
30
+
31
+ Args:
32
+ dataset_dir: Directory of datasets to serve. Can be specified multiple times.
33
+ host: Host IP address to listen on.
34
+ port: Port to listen on.
35
+ verbose: Print more information to the console.
36
+ allow_remote_access: Allow remote connections to the server.
37
+ handlers: Path to a Javascript file with custom API handlers.
38
+ config: Path to a custom config JSON file.
39
+ extend: Path to a directory containing custom client code.
40
+
41
+ Returns:
42
+ A dictionary containing the command executed, stdout, and stderr.
43
+ """
44
+ cmd = ["auspice", "view"]
45
+
46
+ if dataset_dir:
47
+ for d in dataset_dir:
48
+ if not d.is_dir():
49
+ raise ValueError(f"Dataset directory not found: {d}")
50
+ cmd.extend(["--datasetDir", str(d)])
51
+
52
+ cmd.extend(["--host", host])
53
+ cmd.extend(["--port", str(port)])
54
+
55
+ if verbose:
56
+ cmd.append("--verbose")
57
+ if allow_remote_access:
58
+ cmd.append("--allow-remote-access")
59
+
60
+ if handlers:
61
+ if not handlers.is_file():
62
+ raise FileNotFoundError(f"Handlers file not found: {handlers}")
63
+ cmd.extend(["--handlers", str(handlers)])
64
+
65
+ if config:
66
+ if not config.is_file():
67
+ raise FileNotFoundError(f"Config file not found: {config}")
68
+ cmd.extend(["--config", str(config)])
69
+
70
+ if extend:
71
+ if not extend.is_dir():
72
+ raise ValueError(f"Extend directory not found: {extend}")
73
+ cmd.extend(["--extend", str(extend)])
74
+
75
+ try:
76
+ # This will block until the server is manually stopped.
77
+ result = subprocess.run(
78
+ cmd, capture_output=True, text=True, check=True
79
+ )
80
+ return {
81
+ "command_executed": " ".join(cmd),
82
+ "stdout": result.stdout,
83
+ "stderr": result.stderr,
84
+ }
85
+ except FileNotFoundError:
86
+ return {
87
+ "command_executed": " ".join(cmd),
88
+ "stdout": "",
89
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
90
+ }
91
+ except subprocess.CalledProcessError as e:
92
+ return {
93
+ "command_executed": " ".join(cmd),
94
+ "stdout": e.stdout,
95
+ "stderr": e.stderr,
96
+ }
97
+
98
+
99
+ @mcp.tool()
100
+ def build(
101
+ verbose: bool = False,
102
+ extend: Optional[Path] = None,
103
+ deploy_path: Optional[str] = None,
104
+ ) -> Dict[str, Any]:
105
+ """
106
+ Creates a production bundle of the Auspice client-side app.
107
+ The output is typically created in a './dist' directory.
108
+
109
+ Args:
110
+ verbose: Print more information to the console.
111
+ extend: Path to a directory containing custom client code.
112
+ deploy_path: Path to deploy the app to (e.g., for GitHub pages).
113
+
114
+ Returns:
115
+ A dictionary containing the command executed, stdout, stderr, and output files.
116
+ """
117
+ cmd = ["auspice", "build"]
118
+
119
+ if verbose:
120
+ cmd.append("--verbose")
121
+
122
+ if extend:
123
+ if not extend.is_dir():
124
+ raise ValueError(f"Extend directory not found: {extend}")
125
+ cmd.extend(["--extend", str(extend)])
126
+
127
+ if deploy_path:
128
+ cmd.extend(["--deploy-path", deploy_path])
129
+
130
+ try:
131
+ result = subprocess.run(
132
+ cmd, capture_output=True, text=True, check=True
133
+ )
134
+ output_dir = Path("./dist")
135
+ return {
136
+ "command_executed": " ".join(cmd),
137
+ "stdout": result.stdout,
138
+ "stderr": result.stderr,
139
+ "output_files": {
140
+ "build_directory": str(output_dir) if output_dir.exists() else "Not created"
141
+ }
142
+ }
143
+ except FileNotFoundError:
144
+ return {
145
+ "command_executed": " ".join(cmd),
146
+ "stdout": "",
147
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
148
+ "output_files": {}
149
+ }
150
+ except subprocess.CalledProcessError as e:
151
+ return {
152
+ "command_executed": " ".join(cmd),
153
+ "stdout": e.stdout,
154
+ "stderr": e.stderr,
155
+ "output_files": {}
156
+ }
157
+
158
+
159
+ @mcp.tool()
160
+ def export_v1(
161
+ dataset_dir: Path,
162
+ output_dir: Path,
163
+ verbose: bool = False,
164
+ ) -> Dict[str, Any]:
165
+ """
166
+ Exports auspice v1 JSONs to create a static site.
167
+ This is a deprecated command and will be removed in a future version.
168
+
169
+ Args:
170
+ dataset_dir: Directory of datasets to export.
171
+ output_dir: Directory to export the auspice client and datasets to.
172
+ verbose: Print more information to the console.
173
+
174
+ Returns:
175
+ A dictionary containing the command executed, stdout, stderr, and output directory.
176
+ """
177
+ cmd = ["auspice", "export", "v1"]
178
+
179
+ if not dataset_dir.is_dir():
180
+ raise ValueError(f"Dataset directory not found: {dataset_dir}")
181
+ cmd.extend(["--dataset-dir", str(dataset_dir)])
182
+
183
+ output_dir.mkdir(parents=True, exist_ok=True)
184
+ cmd.extend(["--output-dir", str(output_dir)])
185
+
186
+ if verbose:
187
+ cmd.append("--verbose")
188
+
189
+ try:
190
+ result = subprocess.run(
191
+ cmd, capture_output=True, text=True, check=True
192
+ )
193
+ return {
194
+ "command_executed": " ".join(cmd),
195
+ "stdout": result.stdout,
196
+ "stderr": result.stderr,
197
+ "output_files": {
198
+ "export_directory": str(output_dir)
199
+ }
200
+ }
201
+ except FileNotFoundError:
202
+ return {
203
+ "command_executed": " ".join(cmd),
204
+ "stdout": "",
205
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
206
+ "output_files": {}
207
+ }
208
+ except subprocess.CalledProcessError as e:
209
+ return {
210
+ "command_executed": " ".join(cmd),
211
+ "stdout": e.stdout,
212
+ "stderr": e.stderr,
213
+ "output_files": {}
214
+ }
215
+
216
+
217
+ @mcp.tool()
218
+ def export_v2(
219
+ dataset_dir: Path,
220
+ output_dir: Path,
221
+ verbose: bool = False,
222
+ config: Optional[Path] = None,
223
+ extend: Optional[Path] = None,
224
+ deploy_path: Optional[str] = None,
225
+ ) -> Dict[str, Any]:
226
+ """
227
+ Exports auspice v2 JSONs to create a static site.
228
+
229
+ Args:
230
+ dataset_dir: Directory of datasets to export.
231
+ output_dir: Directory to export the auspice client and datasets to.
232
+ verbose: Print more information to the console.
233
+ config: Path to a custom config JSON file.
234
+ extend: Path to a directory containing custom client code.
235
+ deploy_path: Path to deploy the app to (e.g., for GitHub pages).
236
+
237
+ Returns:
238
+ A dictionary containing the command executed, stdout, stderr, and output directory.
239
+ """
240
+ cmd = ["auspice", "export", "v2"]
241
+
242
+ if not dataset_dir.is_dir():
243
+ raise ValueError(f"Dataset directory not found: {dataset_dir}")
244
+ cmd.extend(["--dataset-dir", str(dataset_dir)])
245
+
246
+ output_dir.mkdir(parents=True, exist_ok=True)
247
+ cmd.extend(["--output-dir", str(output_dir)])
248
+
249
+ if verbose:
250
+ cmd.append("--verbose")
251
+
252
+ if config:
253
+ if not config.is_file():
254
+ raise FileNotFoundError(f"Config file not found: {config}")
255
+ cmd.extend(["--config", str(config)])
256
+
257
+ if extend:
258
+ if not extend.is_dir():
259
+ raise ValueError(f"Extend directory not found: {extend}")
260
+ cmd.extend(["--extend", str(extend)])
261
+
262
+ if deploy_path:
263
+ cmd.extend(["--deploy-path", deploy_path])
264
+
265
+ try:
266
+ result = subprocess.run(
267
+ cmd, capture_output=True, text=True, check=True
268
+ )
269
+ return {
270
+ "command_executed": " ".join(cmd),
271
+ "stdout": result.stdout,
272
+ "stderr": result.stderr,
273
+ "output_files": {
274
+ "export_directory": str(output_dir)
275
+ }
276
+ }
277
+ except FileNotFoundError:
278
+ return {
279
+ "command_executed": " ".join(cmd),
280
+ "stdout": "",
281
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
282
+ "output_files": {}
283
+ }
284
+ except subprocess.CalledProcessError as e:
285
+ return {
286
+ "command_executed": " ".join(cmd),
287
+ "stdout": e.stdout,
288
+ "stderr": e.stderr,
289
+ "output_files": {}
290
+ }
291
+
292
+
293
+ @mcp.tool()
294
+ def develop(
295
+ verbose: bool = False,
296
+ extend: Optional[Path] = None,
297
+ config: Optional[Path] = None,
298
+ dataset_dir: Optional[List[Path]] = None,
299
+ port: int = 4000,
300
+ host: str = "localhost",
301
+ ) -> Dict[str, str]:
302
+ """
303
+ Starts the Auspice development server with hot-reloading.
304
+ Note: This command starts a long-running server process and may not be suitable
305
+ for automated workflows that expect a command to terminate.
306
+
307
+ Args:
308
+ verbose: Print more information to the console.
309
+ extend: Path to a directory containing custom client code.
310
+ config: Path to a custom config JSON file.
311
+ dataset_dir: Directory of datasets to serve. Can be specified multiple times.
312
+ port: Port to listen on.
313
+ host: Host IP address to listen on.
314
+
315
+ Returns:
316
+ A dictionary containing the command executed, stdout, and stderr.
317
+ """
318
+ cmd = ["auspice", "develop"]
319
+
320
+ if verbose:
321
+ cmd.append("--verbose")
322
+
323
+ if extend:
324
+ if not extend.is_dir():
325
+ raise ValueError(f"Extend directory not found: {extend}")
326
+ cmd.extend(["--extend", str(extend)])
327
+
328
+ if config:
329
+ if not config.is_file():
330
+ raise FileNotFoundError(f"Config file not found: {config}")
331
+ cmd.extend(["--config", str(config)])
332
+
333
+ if dataset_dir:
334
+ for d in dataset_dir:
335
+ if not d.is_dir():
336
+ raise ValueError(f"Dataset directory not found: {d}")
337
+ cmd.extend(["--datasetDir", str(d)])
338
+
339
+ cmd.extend(["--port", str(port)])
340
+ cmd.extend(["--host", host])
341
+
342
+ try:
343
+ # This will block until the server is manually stopped.
344
+ result = subprocess.run(
345
+ cmd, capture_output=True, text=True, check=True
346
+ )
347
+ return {
348
+ "command_executed": " ".join(cmd),
349
+ "stdout": result.stdout,
350
+ "stderr": result.stderr,
351
+ }
352
+ except FileNotFoundError:
353
+ return {
354
+ "command_executed": " ".join(cmd),
355
+ "stdout": "",
356
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
357
+ }
358
+ except subprocess.CalledProcessError as e:
359
+ return {
360
+ "command_executed": " ".join(cmd),
361
+ "stdout": e.stdout,
362
+ "stderr": e.stderr,
363
+ }
364
+
365
+
366
+ @mcp.tool()
367
+ def version() -> Dict[str, str]:
368
+ """
369
+ Prints the version number of auspice and exits.
370
+
371
+ Returns:
372
+ A dictionary containing the command executed, stdout, stderr, and parsed version.
373
+ """
374
+ cmd = ["auspice", "version"]
375
+ try:
376
+ result = subprocess.run(
377
+ cmd, capture_output=True, text=True, check=True
378
+ )
379
+ return {
380
+ "command_executed": " ".join(cmd),
381
+ "stdout": result.stdout,
382
+ "stderr": result.stderr,
383
+ "version": result.stdout.strip(),
384
+ }
385
+ except FileNotFoundError:
386
+ return {
387
+ "command_executed": " ".join(cmd),
388
+ "stdout": "",
389
+ "stderr": "Error: 'auspice' command not found. Please ensure it is installed and in your PATH.",
390
+ "version": "",
391
+ }
392
+ except subprocess.CalledProcessError as e:
393
+ return {
394
+ "command_executed": " ".join(cmd),
395
+ "stdout": e.stdout,
396
+ "stderr": e.stderr,
397
+ "version": "",
398
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_server.py')
11
+ SERVER_NAME = 'biosci_auspice'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-auspice:
5
+ build: .
6
+ image: mcp-auspice:latest
7
+ container_name: mcp-auspice
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=auspice
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - auspice
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/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 barrnap via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda barrnap -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/barrnap_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/barrnap_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/barrnap_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_server.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # Helper function to execute barrnap commands and handle output.
7
+ # This function is internal and not exposed as an MCP tool.
8
+ def _run_barrnap_command(
9
+ command_args: List[str],
10
+ output_gff_path: Optional[Path] = None,
11
+ debug: bool = False,
12
+ quiet: bool = False,
13
+ ) -> dict:
14
+ """
15
+ Internal helper to execute barrnap commands.
16
+ Captures stdout, stderr, and handles CalledProcessError.
17
+ If output_gff_path is provided, stdout is written to that file.
18
+ """
19
+ cmd = ["barrnap"] + command_args
20
+
21
+ if debug:
22
+ cmd.append("--debug")
23
+ if quiet:
24
+ cmd.append("--quiet")
25
+
26
+ stdout_capture = ""
27
+ stderr_capture = ""
28
+ output_files_generated = []
29
+
30
+ try:
31
+ # barrnap writes GFF to stdout by default.
32
+ # If output_gff_path is provided, we capture stdout and write it to the file.
33
+ process = subprocess.run(
34
+ cmd,
35
+ capture_output=True,
36
+ text=True,
37
+ check=True
38
+ )
39
+ stdout_capture = process.stdout
40
+ stderr_capture = process.stderr
41
+
42
+ if output_gff_path:
43
+ output_gff_path.write_text(stdout_capture)
44
+ output_files_generated.append(str(output_gff_path))
45
+ stdout_capture = f"GFF output written to {output_gff_path}"
46
+
47
+ except subprocess.CalledProcessError as e:
48
+ return {
49
+ "command_executed": " ".join(e.cmd),
50
+ "stdout": e.stdout,
51
+ "stderr": e.stderr,
52
+ "error": str(e),
53
+ "returncode": e.returncode,
54
+ "output_files": [],
55
+ }
56
+ except FileNotFoundError:
57
+ return {
58
+ "command_executed": " ".join(cmd),
59
+ "stdout": "",
60
+ "stderr": "barrnap command not found. Please ensure barrnap is installed and in your PATH.",
61
+ "error": "barrnap not found",
62
+ "returncode": 127,
63
+ "output_files": [],
64
+ }
65
+
66
+ return {
67
+ "command_executed": " ".join(cmd),
68
+ "stdout": stdout_capture,
69
+ "stderr": stderr_capture,
70
+ "output_files": output_files_generated,
71
+ }
72
+
73
+ @mcp.tool()
74
+ def barrnap_annotate(
75
+ fasta_file: Path,
76
+ output_gff_file: Path,
77
+ kingdom: Optional[str] = "bac",
78
+ enable_all_rna: bool = False,
79
+ disable_rrna: bool = False,
80
+ enable_trna: bool = False,
81
+ enable_ncrna: bool = False,
82
+ enable_mrna: bool = False,
83
+ threads: int = 1,
84
+ fast: bool = False,
85
+ evalue: Optional[float] = None,
86
+ incseq: bool = False,
87
+ incseqreg: bool = False,
88
+ outseq_file: Optional[Path] = None,
89
+ add_ids: bool = False,
90
+ db_directory: Optional[Path] = None,
91
+ debug: bool = False,
92
+ quiet: bool = False,
93
+ ) -> dict:
94
+ """
95
+ Annotates RNA features (rRNA, tRNA, tmRNA, ncRNA, mRNA) in microbial genomes
96
+ (bacteria, archaea, fungi) from an input FASTA file.
97
+ Outputs results in GFF3 format to the specified output file.
98
+
99
+ Args:
100
+ fasta_file: Path to the input FASTA file containing genomic sequences.
101
+ output_gff_file: Path to the output GFF3 file where annotations will be written.
102
+ kingdom: The database to use for annotation. Choices: "bac" (Bacteria),
103
+ "arc" (Archaea), "fun" (Fungi). Defaults to "bac".
104
+ enable_all_rna: If True, enables scanning for all RNA types (rRNA, tRNA, tmRNA, ncRNA, mRNA).
105
+ This is equivalent to `--all`.
106
+ disable_rrna: If True, disables rRNA scanning. This is equivalent to `--no-rrna`.
107
+ enable_trna: If True, enables tRNA scanning. This is equivalent to `--trna`.
108
+ enable_ncrna: If True, enables ncRNA scanning. This is equivalent to `--ncrna`.
109
+ enable_mrna: If True, enables mRNA scanning (including CDS, RBS, sig_pep, terminator).
110
+ This is equivalent to `--mrna`.
111
+ threads: Number of CPUs to use for the search. Must be at least 1. Defaults to 1.
112
+ fast: If True, uses simpler HMMs instead of CMs, which is faster but less accurate.
113
+ This is equivalent to `--fast`.
114
+ evalue: E-value cutoff for hits to keep. Must be greater than 0 if provided.
115
+ This is equivalent to `--evalue`.
116
+ incseq: If True, includes the full input sequences in the output GFF.
117
+ This is equivalent to `--incseq`.
118
+ incseqreg: If True, includes `##sequence-region` headers in the GFF.
119
+ This is equivalent to `--incseqreg`.
120
+ outseq_file: Path to a FASTA file where hit sequences will be written.
121
+ This is equivalent to `--outseq`.
122
+ add_ids: If True, adds unique ID= tags to each GFF3 feature.
123
+ This is equivalent to `--addids`.
124
+ db_directory: Path to a different database folder to use.
125
+ This is equivalent to `--dbdir`.
126
+ debug: If True, writes all temporary files to '.' and prints debug information.
127
+ This is equivalent to `--debug`.
128
+ quiet: If True, suppresses all messages to stderr. This is equivalent to `--quiet`.
129
+
130
+ Returns:
131
+ A dictionary containing:
132
+ - "command_executed": The full command string executed.
133
+ - "stdout": Standard output from the tool.
134
+ - "stderr": Standard error from the tool.
135
+ - "output_files": A list of paths to generated output files (GFF3 and optional FASTA).
136
+ """
137
+ # Input validation
138
+ if not fasta_file.is_file():
139
+ raise ValueError(f"Input FASTA file not found: {fasta_file}")
140
+ if not output_gff_file.parent.is_dir():
141
+ raise ValueError(f"Output GFF directory does not exist: {output_gff_file.parent}")
142
+
143
+ valid_kingdoms = {"bac", "arc", "fun"}
144
+ if kingdom is not None and kingdom not in valid_kingdoms:
145
+ raise ValueError(f"Invalid kingdom: '{kingdom}'. Must be one of {', '.join(valid_kingdoms)}.")
146
+
147
+ if threads < 1:
148
+ raise ValueError(f"Number of threads must be at least 1, got {threads}.")
149
+
150
+ if evalue is not None and evalue <= 0:
151
+ raise ValueError(f"E-value cutoff must be greater than 0, got {evalue}.")
152
+
153
+ if outseq_file and not outseq_file.parent.is_dir():
154
+ raise ValueError(f"Output FASTA directory for hit sequences does not exist: {outseq_file.parent}")
155
+
156
+ if db_directory and not db_directory.is_dir():
157
+ raise ValueError(f"Database directory not found: {db_directory}")
158
+
159
+ command_args = [str(fasta_file)]
160
+
161
+ # Database management
162
+ if db_directory:
163
+ command_args.extend(["--dbdir", str(db_directory)])
164
+
165
+ # Search options
166
+ if kingdom:
167
+ command_args.extend(["--kingdom", kingdom])
168
+ if enable_all_rna:
169
+ command_args.append("--all")
170
+ if disable_rrna:
171
+ command_args.append("--no-rrna")
172
+ if enable_trna:
173
+ command_args.append("--trna")
174
+ if enable_ncrna:
175
+ command_args.append("--ncrna")
176
+ if enable_mrna:
177
+ command_args.append("--mrna")
178
+
179
+ # Speed options
180
+ if threads > 1: # barrnap default is 1 thread, so only add if > 1
181
+ command_args.extend(["--threads", str(threads)])
182
+ if fast:
183
+ command_args.append("--fast")
184
+
185
+ # Filtering options
186
+ if evalue is not None:
187
+ command_args.extend(["--evalue", str(evalue)])
188
+
189
+ # Output options
190
+ if incseq:
191
+ command_args.append("--incseq")
192
+ if incseqreg:
193
+ command_args.append("--incseqreg")
194
+ if outseq_file:
195
+ command_args.extend(["--outseq", str(outseq_file)])
196
+ if add_ids:
197
+ command_args.append("--addids")
198
+
199
+ result = _run_barrnap_command(
200
+ command_args=command_args,
201
+ output_gff_path=output_gff_file,
202
+ debug=debug,
203
+ quiet=quiet,
204
+ )
205
+
206
+ # Add outseq_file to output_files if it was generated by barrnap
207
+ if outseq_file and outseq_file.exists() and str(outseq_file) not in result["output_files"]:
208
+ result["output_files"].append(str(outseq_file))
209
+
210
+ return result
211
+
212
+ @mcp.tool()
213
+ def barrnap_list_databases(
214
+ db_directory: Optional[Path] = None,
215
+ debug: bool = False,
216
+ quiet: bool = False,
217
+ ) -> dict:
218
+ """
219
+ Lists the installed barrnap databases and their contents.
220
+
221
+ Args:
222
+ db_directory: Path to a different database folder to use.
223
+ This is equivalent to `--dbdir`.
224
+ debug: If True, writes all temporary files to '.' and prints debug information.
225
+ This is equivalent to `--debug`.
226
+ quiet: If True, suppresses all messages to stderr. This is equivalent to `--quiet`.
227
+
228
+ Returns:
229
+ A dictionary containing:
230
+ - "command_executed": The full command string executed.
231
+ - "stdout": Standard output from the tool, listing databases.
232
+ - "stderr": Standard error from the tool.
233
+ - "output_files": An empty list, as no files are generated.
234
+ """
235
+ command_args = ["--listdb"]
236
+
237
+ if db_directory:
238
+ if not db_directory.is_dir():
239
+ raise ValueError(f"Database directory not found: {db_directory}")
240
+ command_args.extend(["--dbdir", str(db_directory)])
241
+
242
+ return _run_barrnap_command(
243
+ command_args=command_args,
244
+ debug=debug,
245
+ quiet=quiet,
246
+ )
247
+
248
+ @mcp.tool()
249
+ def barrnap_update_databases(
250
+ db_directory: Optional[Path] = None,
251
+ debug: bool = False,
252
+ quiet: bool = False,
253
+ ) -> dict:
254
+ """
255
+ Updates barrnap databases from the internet.
256
+
257
+ Args:
258
+ db_directory: Path to a different database folder to use.
259
+ This is equivalent to `--dbdir`.
260
+ debug: If True, writes all temporary files to '.' and prints debug information.
261
+ This is equivalent to `--debug`.
262
+ quiet: If True, suppresses all messages to stderr. This is equivalent to `--quiet`.
263
+
264
+ Returns:
265
+ A dictionary containing:
266
+ - "command_executed": The full command string executed.
267
+ - "stdout": Standard output from the tool, typically update messages.
268
+ - "stderr": Standard error from the tool.
269
+ - "output_files": An empty list, as no files are generated.
270
+ """
271
+ command_args = ["--updatedb"]
272
+
273
+ if db_directory:
274
+ if not db_directory.is_dir():
275
+ raise ValueError(f"Database directory not found: {db_directory}")
276
+ command_args.extend(["--dbdir", str(db_directory)])
277
+
278
+ return _run_barrnap_command(
279
+ command_args=command_args,
280
+ debug=debug,
281
+ quiet=quiet,
282
+ )
283
+
284
+ @mcp.tool()
285
+ def barrnap_get_version(
286
+ debug: bool = False,
287
+ quiet: bool = False,
288
+ ) -> dict:
289
+ """
290
+ Prints the barrnap version in the format 'barrnap X.Y'.
291
+
292
+ Args:
293
+ debug: If True, writes all temporary files to '.' and prints debug information.
294
+ This is equivalent to `--debug`.
295
+ quiet: If True, suppresses all messages to stderr. This is equivalent to `--quiet`.
296
+
297
+ Returns:
298
+ A dictionary containing:
299
+ - "command_executed": The full command string executed.
300
+ - "stdout": Standard output from the tool, containing the version string.
301
+ - "stderr": Standard error from the tool.
302
+ - "output_files": An empty list, as no files are generated.
303
+ """
304
+ command_args = ["--version"]
305
+ return _run_barrnap_command(
306
+ command_args=command_args,
307
+ debug=debug,
308
+ quiet=quiet,
309
+ )
310
+
311
+ @mcp.tool()
312
+ def barrnap_get_citation(
313
+ debug: bool = False,
314
+ quiet: bool = False,
315
+ ) -> dict:
316
+ """
317
+ Prints the barrnap citation information.
318
+
319
+ Args:
320
+ debug: If True, writes all temporary files to '.' and prints debug information.
321
+ This is equivalent to `--debug`.
322
+ quiet: If True, suppresses all messages to stderr. This is equivalent to `--quiet`.
323
+
324
+ Returns:
325
+ A dictionary containing:
326
+ - "command_executed": The full command string executed.
327
+ - "stdout": Standard output from the tool, containing the citation.
328
+ - "stderr": Standard error from the tool.
329
+ - "output_files": An empty list, as no files are generated.
330
+ """
331
+ command_args = ["--citation"]
332
+ return _run_barrnap_command(
333
+ command_args=command_args,
334
+ debug=debug,
335
+ quiet=quiet,
336
+ )
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_server.py')
11
+ SERVER_NAME = 'biosci_barrnap'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-barrnap:
5
+ build: .
6
+ image: mcp-barrnap:latest
7
+ container_name: mcp-barrnap
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=barrnap
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - barrnap
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/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 bin2cell via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bin2cell -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/bin2cell_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/bin2cell_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/bin2cell_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_server.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ from pathlib import Path
3
+ from typing import Optional, List
4
+
5
+ @mcp.tool()
6
+ def bin2cell_prepare_bins(
7
+ input_dir: str,
8
+ output_h5ad: str,
9
+ bin_size: int = 2,
10
+ destripe: bool = True,
11
+ ):
12
+ """
13
+ Reads Visium HD data from a SpaceRanger output directory and optionally performs destriping
14
+ to correct for technical effects in 2um bin data.
15
+
16
+ Args:
17
+ input_dir: Path to the SpaceRanger output directory (containing 'outs' folder).
18
+ output_h5ad: Path where the processed bin-level AnnData object will be saved.
19
+ bin_size: Resolution of bins to load (default is 2um).
20
+ destripe: Whether to apply the destriping correction for variable bin dimensions.
21
+ """
22
+ # Input validation
23
+ input_path = Path(input_dir)
24
+ if not input_path.exists():
25
+ return {"error": f"Input directory {input_dir} does not exist."}
26
+
27
+ output_path = Path(output_h5ad)
28
+ if not output_path.parent.exists():
29
+ output_path.parent.mkdir(parents=True, exist_ok=True)
30
+
31
+ if bin_size <= 0:
32
+ return {"error": "bin_size must be a positive integer."}
33
+
34
+ # Construct Python command
35
+ # We use a python script string to execute the library functions
36
+ destripe_cmd = "b2c.pp.destripe(adata)" if destripe else "pass"
37
+ python_script = f"""
38
+ import bin2cell as b2c
39
+ import scanpy as sc
40
+ import os
41
+
42
+ try:
43
+ # Load Visium HD data
44
+ adata = b2c.pp.read_visium_hd_folder('{input_dir}', bin_size={bin_size})
45
+
46
+ # Perform destriping if requested
47
+ if {destripe}:
48
+ b2c.pp.destripe(adata)
49
+
50
+ # Save the result
51
+ adata.write('{output_h5ad}')
52
+ print("Successfully prepared bin data.")
53
+ except Exception as e:
54
+ print(f"Error: {{str(e)}}")
55
+ exit(1)
56
+ """
57
+
58
+ try:
59
+ result = subprocess.run(
60
+ ["python", "-c", python_script],
61
+ capture_output=True,
62
+ text=True,
63
+ check=True
64
+ )
65
+ return {
66
+ "command_executed": f"bin2cell.pp.read_visium_hd_folder and destripe={destripe}",
67
+ "stdout": result.stdout,
68
+ "stderr": result.stderr,
69
+ "output_files": [output_h5ad]
70
+ }
71
+ except subprocess.CalledProcessError as e:
72
+ return {
73
+ "error": "Failed to prepare bin data",
74
+ "stdout": e.stdout,
75
+ "stderr": e.stderr,
76
+ "command_executed": e.cmd
77
+ }
78
+
79
+ @mcp.tool()
80
+ def bin2cell_run_stardist(
81
+ image_path: str,
82
+ output_mask_path: str,
83
+ model_name: str = "2D_versatile_he",
84
+ prob_thresh: float = 0.5,
85
+ nms_thresh: float = 0.3,
86
+ ):
87
+ """
88
+ Performs cell segmentation on a morphology image using StarDist.
89
+
90
+ Args:
91
+ image_path: Path to the high-resolution morphology image (e.g., tissue_hires_image.png).
92
+ output_mask_path: Path where the resulting segmentation mask (.tif) will be saved.
93
+ model_name: StarDist model to use (default: '2D_versatile_he').
94
+ prob_thresh: Probability threshold for StarDist detection.
95
+ nms_thresh: Non-maximum suppression threshold for StarDist.
96
+ """
97
+ # Input validation
98
+ img_path = Path(image_path)
99
+ if not img_path.exists():
100
+ return {"error": f"Image file {image_path} does not exist."}
101
+
102
+ out_mask = Path(output_mask_path)
103
+ if not out_mask.parent.exists():
104
+ out_mask.parent.mkdir(parents=True, exist_ok=True)
105
+
106
+ python_script = f"""
107
+ import bin2cell as b2c
108
+ import cv2
109
+ import numpy as np
110
+
111
+ try:
112
+ # Run StarDist segmentation via bin2cell wrapper
113
+ # Note: bin2cell.tl.stardist handles the model loading and prediction
114
+ b2c.tl.stardist(
115
+ '{image_path}',
116
+ '{output_mask_path}',
117
+ model='{model_name}',
118
+ prob_thresh={prob_thresh},
119
+ nms_thresh={nms_thresh}
120
+ )
121
+ print("Successfully generated segmentation mask.")
122
+ except Exception as e:
123
+ print(f"Error: {{str(e)}}")
124
+ exit(1)
125
+ """
126
+
127
+ try:
128
+ result = subprocess.run(
129
+ ["python", "-c", python_script],
130
+ capture_output=True,
131
+ text=True,
132
+ check=True
133
+ )
134
+ return {
135
+ "command_executed": f"bin2cell.tl.stardist on {image_path}",
136
+ "stdout": result.stdout,
137
+ "stderr": result.stderr,
138
+ "output_files": [output_mask_path]
139
+ }
140
+ except subprocess.CalledProcessError as e:
141
+ return {
142
+ "error": "StarDist segmentation failed",
143
+ "stdout": e.stdout,
144
+ "stderr": e.stderr,
145
+ "command_executed": e.cmd
146
+ }
147
+
148
+ @mcp.tool()
149
+ def bin2cell_extract_cells(
150
+ bin_h5ad_path: str,
151
+ mask_path: str,
152
+ output_cell_h5ad_path: str,
153
+ qc_metrics: bool = True,
154
+ ):
155
+ """
156
+ Groups subcellular bins into cells based on a segmentation mask and generates a cell-level AnnData object.
157
+
158
+ Args:
159
+ bin_h5ad_path: Path to the bin-level AnnData object (output from bin2cell_prepare_bins).
160
+ mask_path: Path to the segmentation mask file (output from bin2cell_run_stardist).
161
+ output_cell_h5ad_path: Path where the final cell-level AnnData object will be saved.
162
+ qc_metrics: Whether to calculate standard scanpy QC metrics for the new cell object.
163
+ """
164
+ # Input validation
165
+ bin_path = Path(bin_h5ad_path)
166
+ if not bin_path.exists():
167
+ return {"error": f"Bin h5ad file {bin_h5ad_path} does not exist."}
168
+
169
+ m_path = Path(mask_path)
170
+ if not m_path.exists():
171
+ return {"error": f"Mask file {mask_path} does not exist."}
172
+
173
+ out_cell_path = Path(output_cell_h5ad_path)
174
+ if not out_cell_path.parent.exists():
175
+ out_cell_path.parent.mkdir(parents=True, exist_ok=True)
176
+
177
+ python_script = f"""
178
+ import bin2cell as b2c
179
+ import scanpy as sc
180
+
181
+ try:
182
+ # Load the bin-level data
183
+ adata_bins = sc.read_h5ad('{bin_h5ad_path}')
184
+
185
+ # Extract cells based on the mask
186
+ adata_cells = b2c.tl.extract_cells(adata_bins, '{mask_path}')
187
+
188
+ # Calculate QC metrics if requested
189
+ if {qc_metrics}:
190
+ sc.pp.calculate_qc_metrics(adata_cells, inplace=True)
191
+
192
+ # Save the cell-level object
193
+ adata_cells.write('{output_cell_h5ad_path}')
194
+ print("Successfully extracted cells from bins.")
195
+ except Exception as e:
196
+ print(f"Error: {{str(e)}}")
197
+ exit(1)
198
+ """
199
+
200
+ try:
201
+ result = subprocess.run(
202
+ ["python", "-c", python_script],
203
+ capture_output=True,
204
+ text=True,
205
+ check=True
206
+ )
207
+ return {
208
+ "command_executed": f"bin2cell.tl.extract_cells using mask {mask_path}",
209
+ "stdout": result.stdout,
210
+ "stderr": result.stderr,
211
+ "output_files": [output_cell_h5ad_path]
212
+ }
213
+ except subprocess.CalledProcessError as e:
214
+ return {
215
+ "error": "Cell extraction failed",
216
+ "stdout": e.stdout,
217
+ "stderr": e.stderr,
218
+ "command_executed": e.cmd
219
+ }
220
+
221
+ @mcp.tool()
222
+ def bin2cell_visualize_segmentation(
223
+ bin_h5ad_path: str,
224
+ output_image_path: str,
225
+ basis: str = "spatial",
226
+ ):
227
+ """
228
+ Generates a visualization of the bin-to-cell assignments.
229
+
230
+ Args:
231
+ bin_h5ad_path: Path to the bin-level AnnData object containing cell assignments.
232
+ output_image_path: Path to save the visualization plot (e.g., .png or .pdf).
233
+ basis: The coordinate system to use for plotting (default: 'spatial').
234
+ """
235
+ bin_path = Path(bin_h5ad_path)
236
+ if not bin_path.exists():
237
+ return {"error": f"Bin h5ad file {bin_h5ad_path} does not exist."}
238
+
239
+ python_script = f"""
240
+ import scanpy as sc
241
+ import matplotlib.pyplot as plt
242
+ import bin2cell as b2c
243
+
244
+ try:
245
+ adata = sc.read_h5ad('{bin_h5ad_path}')
246
+ if 'cell_id' not in adata.obs.columns:
247
+ print("Error: cell_id not found in adata.obs. Run extract_cells first.")
248
+ exit(1)
249
+
250
+ # Plotting logic
251
+ sc.pl.embedding(adata, basis='{basis}', color='cell_id', show=False)
252
+ plt.savefig('{output_image_path}')
253
+ print("Successfully saved visualization.")
254
+ except Exception as e:
255
+ print(f"Error: {{str(e)}}")
256
+ exit(1)
257
+ """
258
+
259
+ try:
260
+ result = subprocess.run(
261
+ ["python", "-c", python_script],
262
+ capture_output=True,
263
+ text=True,
264
+ check=True
265
+ )
266
+ return {
267
+ "command_executed": f"Visualization of cell_id on {basis}",
268
+ "stdout": result.stdout,
269
+ "stderr": result.stderr,
270
+ "output_files": [output_image_path]
271
+ }
272
+ except subprocess.CalledProcessError as e:
273
+ return {
274
+ "error": "Visualization failed",
275
+ "stdout": e.stdout,
276
+ "stderr": e.stderr,
277
+ "command_executed": e.cmd
278
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_server.py')
11
+ SERVER_NAME = 'biosci_bin2cell'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bin2cell:
5
+ build: .
6
+ image: mcp-bin2cell:latest
7
+ container_name: mcp-bin2cell
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bin2cell
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bin2cell
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocbaseutils/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-biocbaseutils
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocbaseutils/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/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-curatedatlasqueryr via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-curatedatlasqueryr -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-curatedatlasqueryr_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-curatedatlasqueryr_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-curatedatlasqueryr_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/bioconductor-curatedatlasqueryr_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/bioconductor-curatedatlasqueryr_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_curatedatlasqueryr'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-curatedatlasqueryr:
5
+ build: .
6
+ image: mcp-bioconductor-curatedatlasqueryr:latest
7
+ container_name: mcp-bioconductor-curatedatlasqueryr
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-curatedatlasqueryr
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-delayedmatrixstats/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ebseq/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/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-experimentsubset via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-experimentsubset -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-experimentsubset_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-experimentsubset_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-experimentsubset_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_server.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Optional, Dict, List
5
+
6
+ # This is a placeholder for the MCP decorator.
7
+ # In a real MCP environment, this would be provided by the MCP framework.
8
+ def mcp_tool_placeholder(*args, **kwargs):
9
+ def decorator(func):
10
+ return func
11
+ return decorator
12
+
13
+ mcp = type('mcp', (), {'tool': mcp_tool_placeholder})
14
+
15
+
16
+ @mcp.tool()
17
+ def manage_experiment_subset(
18
+ input_rds: Path,
19
+ output_rds: Path,
20
+ subset_name: str = "mcp_subset",
21
+ row_subset: Optional[str] = None,
22
+ col_subset: Optional[str] = None,
23
+ row_subset_file: Optional[Path] = None,
24
+ col_subset_file: Optional[Path] = None,
25
+ ) -> Dict[str, any]:
26
+ """
27
+ Provides a command-line interface to the R/Bioconductor 'ExperimentSubset' package.
28
+
29
+ This tool subsets a Bioconductor experiment object (e.g., SummarizedExperiment,
30
+ SingleCellExperiment) stored in an RDS file based on provided row (e.g., genes)
31
+ or column (e.g., cells) identifiers.
32
+
33
+ Args:
34
+ input_rds: Path to the input RDS file containing a Bioconductor experiment object.
35
+ output_rds: Path to save the output subsetted RDS file.
36
+ subset_name: A name to assign to the created subset.
37
+ row_subset: A comma-separated string of row names to include in the subset.
38
+ col_subset: A comma-separated string of column names to include in the subset.
39
+ row_subset_file: Path to a file containing row names to include (one per line).
40
+ col_subset_file: Path to a file containing column names to include (one per line).
41
+
42
+ Returns:
43
+ A dictionary containing the execution command, stdout, stderr, and a list of output files.
44
+ """
45
+ # --- Input Validation ---
46
+ if not input_rds.exists():
47
+ raise FileNotFoundError(f"Input file not found: {input_rds}")
48
+
49
+ if row_subset and row_subset_file:
50
+ raise ValueError("Cannot specify both 'row_subset' and 'row_subset_file'.")
51
+
52
+ if col_subset and col_subset_file:
53
+ raise ValueError("Cannot specify both 'col_subset' and 'col_subset_file'.")
54
+
55
+ if not any([row_subset, col_subset, row_subset_file, col_subset_file]):
56
+ raise ValueError("At least one subsetting criterion must be provided "
57
+ "('row_subset', 'col_subset', 'row_subset_file', or 'col_subset_file').")
58
+
59
+ if not output_rds.parent.exists():
60
+ output_rds.parent.mkdir(parents=True, exist_ok=True)
61
+
62
+ # --- R Script Generation ---
63
+ r_script_content = f"""
64
+ # Load required libraries
65
+ if (!require("optparse", quietly = TRUE)) install.packages("optparse", repos = "http://cran.us.r-project.org")
66
+ if (!require("ExperimentSubset", quietly = TRUE)) {{
67
+ if (!require("BiocManager", quietly = TRUE)) install.packages("BiocManager", repos = "http://cran.us.r-project.org")
68
+ BiocManager::install("ExperimentSubset", update=FALSE)
69
+ }}
70
+ library(optparse)
71
+ library(ExperimentSubset)
72
+
73
+ # Define and parse command-line options
74
+ option_list <- list(
75
+ make_option(c("-i", "--input"), type="character", help="Input RDS file path"),
76
+ make_option(c("-o", "--output"), type="character", help="Output RDS file path"),
77
+ make_option(c("-n", "--name"), type="character", default="mcp_subset", help="Name for the subset"),
78
+ make_option(c("-r", "--rows"), type="character", default=NULL, help="Comma-separated row names"),
79
+ make_option(c("-c", "--cols"), type="character", default=NULL, help="Comma-separated column names"),
80
+ make_option(c("--row_file"), type="character", default=NULL, help="File with row names"),
81
+ make_option(c("--col_file"), type="character", default=NULL, help="File with column names")
82
+ )
83
+
84
+ opt_parser <- OptionParser(option_list=option_list)
85
+ opt <- parse_args(opt_parser)
86
+
87
+ if (is.null(opt$input) || is.null(opt$output)) {{
88
+ print_help(opt_parser)
89
+ stop("Input and output files must be supplied.", call.=FALSE)
90
+ }}
91
+
92
+ # Load the experiment object
93
+ cat("Loading input RDS file:", opt$input, "\\n")
94
+ exp_obj <- readRDS(opt$input)
95
+
96
+ # Create ExperimentSubset object
97
+ es <- ExperimentSubset(exp_obj)
98
+
99
+ # Determine row and column subsets
100
+ row_indices <- NULL
101
+ if (!is.null(opt$rows)) {{
102
+ row_indices <- trimws(strsplit(opt$rows, ",")[[1]])
103
+ }} else if (!is.null(opt$row_file)) {{
104
+ row_indices <- readLines(opt$row_file)
105
+ }}
106
+
107
+ col_indices <- NULL
108
+ if (!is.null(opt$cols)) {{
109
+ col_indices <- trimws(strsplit(opt$cols, ",")[[1]])
110
+ }} else if (!is.null(opt$col_file)) {{
111
+ col_indices <- readLines(opt$col_file)
112
+ }}
113
+
114
+ # Create the subset using the subsetData function
115
+ cat("Creating subset '", opt$name, "'...\\n", sep="")
116
+ es <- subsetData(es, subsetName = opt$name, rows = row_indices, cols = col_indices)
117
+
118
+ # Retrieve the actual subsetted object from the container
119
+ subset_obj <- getSubset(es, subsetName = opt$name)
120
+
121
+ # Save the subsetted object
122
+ cat("Saving subsetted object to:", opt$output, "\\n")
123
+ saveRDS(subset_obj, file = opt$output)
124
+
125
+ cat("Successfully created subset.\\n")
126
+ """
127
+
128
+ # --- Subprocess Execution ---
129
+ cmd: List[str] = []
130
+ try:
131
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file:
132
+ r_script_file.write(r_script_content)
133
+ r_script_path = r_script_file.name
134
+
135
+ cmd = [
136
+ "Rscript", r_script_path,
137
+ "--input", str(input_rds),
138
+ "--output", str(output_rds),
139
+ "--name", subset_name
140
+ ]
141
+
142
+ if row_subset:
143
+ cmd.extend(["--rows", row_subset])
144
+ if col_subset:
145
+ cmd.extend(["--cols", col_subset])
146
+ if row_subset_file:
147
+ cmd.extend(["--row_file", str(row_subset_file)])
148
+ if col_subset_file:
149
+ cmd.extend(["--col_file", str(col_subset_file)])
150
+
151
+ result = subprocess.run(
152
+ cmd,
153
+ capture_output=True,
154
+ text=True,
155
+ check=True
156
+ )
157
+
158
+ return {
159
+ "command_executed": " ".join(cmd),
160
+ "stdout": result.stdout,
161
+ "stderr": result.stderr,
162
+ "output_files": [str(output_rds)]
163
+ }
164
+
165
+ except FileNotFoundError:
166
+ raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.")
167
+ except subprocess.CalledProcessError as e:
168
+ return {
169
+ "command_executed": " ".join(cmd),
170
+ "stdout": e.stdout,
171
+ "stderr": e.stderr,
172
+ "error": "R script execution failed.",
173
+ "return_code": e.returncode
174
+ }
175
+ finally:
176
+ # Clean up the temporary R script
177
+ if 'r_script_path' in locals() and Path(r_script_path).exists():
178
+ Path(r_script_path).unlink()
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_experimentsubset'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-experimentsubset:
5
+ build: .
6
+ image: mcp-bioconductor-experimentsubset:latest
7
+ container_name: mcp-bioconductor-experimentsubset
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-experimentsubset
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/environment.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ name: mcp-tool
3
+ channels:
4
+ - bioconda
5
+ - conda-forge
6
+ - defaults
7
+ dependencies:
8
+ - bioconductor-experimentsubset
9
+ - python=3.10
10
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastmcp
2
+ mcp
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genefilter/app/requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genefilter/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-genefilter:
5
+ build: .
6
+ image: mcp-bioconductor-genefilter:latest
7
+ container_name: mcp-bioconductor-genefilter
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-genefilter
12
+ volumes:
13
+ - ./workspace:/app/workspace
14
+ - ./output:/app/output
15
+ restart: unless-stopped
16
+ healthcheck:
17
+ test: ["CMD", "python", "-c", "import sys; sys.exit(0)"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 3
21
+ start_period: 5s
22
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/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-ggsc via conda (e.g., from bioconda)
14
+ RUN conda install -c bioconda bioconductor-ggsc -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-ggsc_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-ggsc_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-ggsc_server.py"]
40
+
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_server.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import logging
3
+ from pathlib import Path
4
+ from typing import Optional, List
5
+
6
+ # This script assumes the presence of a companion R script (e.g., 'run_ggsc.R')
7
+ # that acts as a command-line interface for the 'ggsc' R package.
8
+ # The R environment must have 'ggsc', 'SingleCellExperiment', 'ggplot2',
9
+ # and an argument parser like 'optparse' installed.
10
+ R_SCRIPT_EXECUTABLE = "run_ggsc.R"
11
+
12
+ # Per instructions, the @mcp.tool decorator is used.
13
+ # A dummy decorator is defined here for syntax validity in a standalone context.
14
+ # In a true MCP environment, this would be provided by the MCP framework.
15
+ def tool(func):
16
+ """Dummy decorator to match the required syntax."""
17
+ return func
18
+
19
+ class MCP:
20
+ """Dummy class to hold the tool decorator."""
21
+ tool = staticmethod(tool)
22
+
23
+ mcp = MCP()
24
+
25
+
26
+ @mcp.tool()
27
+ def plot_reduced_dim(
28
+ input_rds: Path,
29
+ output_plot: Path,
30
+ dim_red: str,
31
+ color_by: str,
32
+ facet_by: Optional[str] = None,
33
+ plot_title: Optional[str] = None,
34
+ img_width: int = 7,
35
+ img_height: int = 7
36
+ ) -> dict:
37
+ """
38
+ Generates a reduced dimension plot (e.g., UMAP, t-SNE) from a SingleCellExperiment object.
39
+
40
+ This tool wraps the `plot_reduced_dim_sce` function from the R/Bioconductor package `ggsc`.
41
+ The input must be a .rds file containing a SingleCellExperiment object.
42
+
43
+ Args:
44
+ input_rds: Path to the input RDS file containing a SingleCellExperiment object.
45
+ output_plot: Path to save the output plot image (e.g., plot.png).
46
+ dim_red: Name of the dimension reduction to use (e.g., "UMAP", "TSNE").
47
+ color_by: Variable in colData to color the points by (e.g., "label", "cluster").
48
+ facet_by: Optional variable in colData to facet the plot by.
49
+ plot_title: Optional title for the plot.
50
+ img_width: Width of the output image in inches.
51
+ img_height: Height of the output image in inches.
52
+
53
+ Returns:
54
+ A dictionary containing the execution details and output file path.
55
+ """
56
+ # --- Input Validation ---
57
+ if not input_rds.is_file():
58
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds}")
59
+ if img_width <= 0 or img_height <= 0:
60
+ raise ValueError("Image width and height must be positive integers.")
61
+
62
+ # Ensure output directory exists
63
+ output_plot.parent.mkdir(parents=True, exist_ok=True)
64
+
65
+ # --- Command Construction ---
66
+ cmd = [
67
+ "Rscript",
68
+ R_SCRIPT_EXECUTABLE,
69
+ "reduced_dim",
70
+ "--input_rds", str(input_rds),
71
+ "--output_plot", str(output_plot),
72
+ "--dim_red", dim_red,
73
+ "--color_by", color_by,
74
+ "--img_width", str(img_width),
75
+ "--img_height", str(img_height)
76
+ ]
77
+
78
+ if facet_by:
79
+ cmd.extend(["--facet_by", facet_by])
80
+ if plot_title:
81
+ cmd.extend(["--plot_title", plot_title])
82
+
83
+ # --- Subprocess Execution ---
84
+ command_executed = " ".join(cmd)
85
+ logging.info(f"Executing command: {command_executed}")
86
+
87
+ try:
88
+ result = subprocess.run(
89
+ cmd,
90
+ capture_output=True,
91
+ text=True,
92
+ check=True,
93
+ encoding='utf-8'
94
+ )
95
+ except FileNotFoundError:
96
+ raise RuntimeError("Rscript or the wrapper script not found. Please ensure R and the tool's R script are in the system's PATH.")
97
+ except subprocess.CalledProcessError as e:
98
+ error_message = f"ggsc R script failed for reduced_dim plot.\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}"
99
+ logging.error(error_message)
100
+ raise RuntimeError(error_message) from e
101
+
102
+ return {
103
+ "command_executed": command_executed,
104
+ "stdout": result.stdout,
105
+ "stderr": result.stderr,
106
+ "output_files": [str(output_plot)]
107
+ }
108
+
109
+
110
+ @mcp.tool()
111
+ def plot_gene_expression(
112
+ input_rds: Path,
113
+ output_plot: Path,
114
+ gene: str,
115
+ dim_red: str,
116
+ assay: str = "logcounts",
117
+ plot_title: Optional[str] = None,
118
+ img_width: int = 7,
119
+ img_height: int = 7
120
+ ) -> dict:
121
+ """
122
+ Plots the expression of a single gene on a reduced dimension plot.
123
+
124
+ This tool wraps the `plot_gene_sce` function from the R/Bioconductor package `ggsc`.
125
+ The input must be a .rds file containing a SingleCellExperiment object.
126
+
127
+ Args:
128
+ input_rds: Path to the input RDS file containing a SingleCellExperiment object.
129
+ output_plot: Path to save the output plot image.
130
+ gene: The name of the gene to plot.
131
+ dim_red: Name of the dimension reduction to use (e.g., "UMAP").
132
+ assay: The assay to use for expression values (default: "logcounts").
133
+ plot_title: Optional title for the plot.
134
+ img_width: Width of the output image in inches.
135
+ img_height: Height of the output image in inches.
136
+
137
+ Returns:
138
+ A dictionary containing the execution details and output file path.
139
+ """
140
+ # --- Input Validation ---
141
+ if not input_rds.is_file():
142
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds}")
143
+ if not gene:
144
+ raise ValueError("A gene name must be provided.")
145
+ if img_width <= 0 or img_height <= 0:
146
+ raise ValueError("Image width and height must be positive integers.")
147
+
148
+ output_plot.parent.mkdir(parents=True, exist_ok=True)
149
+
150
+ # --- Command Construction ---
151
+ cmd = [
152
+ "Rscript",
153
+ R_SCRIPT_EXECUTABLE,
154
+ "gene_plot",
155
+ "--input_rds", str(input_rds),
156
+ "--output_plot", str(output_plot),
157
+ "--gene", gene,
158
+ "--dim_red", dim_red,
159
+ "--assay", assay,
160
+ "--img_width", str(img_width),
161
+ "--img_height", str(img_height)
162
+ ]
163
+
164
+ if plot_title:
165
+ cmd.extend(["--plot_title", plot_title])
166
+
167
+ # --- Subprocess Execution ---
168
+ command_executed = " ".join(cmd)
169
+ logging.info(f"Executing command: {command_executed}")
170
+
171
+ try:
172
+ result = subprocess.run(
173
+ cmd,
174
+ capture_output=True,
175
+ text=True,
176
+ check=True,
177
+ encoding='utf-8'
178
+ )
179
+ except FileNotFoundError:
180
+ raise RuntimeError("Rscript or the wrapper script not found. Please ensure R and the tool's R script are in the system's PATH.")
181
+ except subprocess.CalledProcessError as e:
182
+ error_message = f"ggsc R script failed for gene expression plot.\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}"
183
+ logging.error(error_message)
184
+ raise RuntimeError(error_message) from e
185
+
186
+ return {
187
+ "command_executed": command_executed,
188
+ "stdout": result.stdout,
189
+ "stderr": result.stderr,
190
+ "output_files": [str(output_plot)]
191
+ }
192
+
193
+
194
+ @mcp.tool()
195
+ def plot_expression_heatmap(
196
+ input_rds: Path,
197
+ output_plot: Path,
198
+ genes: List[str],
199
+ annotation_col: str,
200
+ assay: str = "logcounts",
201
+ plot_title: Optional[str] = None,
202
+ img_width: int = 7,
203
+ img_height: int = 10
204
+ ) -> dict:
205
+ """
206
+ Generates a heatmap of gene expression for a set of genes.
207
+
208
+ This tool wraps the `plot_heatmap_sce` function from the R/Bioconductor package `ggsc`.
209
+ The input must be a .rds file containing a SingleCellExperiment object.
210
+
211
+ Args:
212
+ input_rds: Path to the input RDS file containing a SingleCellExperiment object.
213
+ output_plot: Path to save the output plot image.
214
+ genes: A list of gene names to include in the heatmap.
215
+ annotation_col: Column in colData to use for cell annotation.
216
+ assay: The assay to use for expression values (default: "logcounts").
217
+ plot_title: Optional title for the plot.
218
+ img_width: Width of the output image in inches.
219
+ img_height: Height of the output image in inches.
220
+
221
+ Returns:
222
+ A dictionary containing the execution details and output file path.
223
+ """
224
+ # --- Input Validation ---
225
+ if not input_rds.is_file():
226
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds}")
227
+ if not genes:
228
+ raise ValueError("At least one gene must be provided in the list.")
229
+ if img_width <= 0 or img_height <= 0:
230
+ raise ValueError("Image width and height must be positive integers.")
231
+
232
+ output_plot.parent.mkdir(parents=True, exist_ok=True)
233
+
234
+ # --- Command Construction ---
235
+ genes_str = ",".join(genes)
236
+ cmd = [
237
+ "Rscript",
238
+ R_SCRIPT_EXECUTABLE,
239
+ "heatmap",
240
+ "--input_rds", str(input_rds),
241
+ "--output_plot", str(output_plot),
242
+ "--genes", genes_str,
243
+ "--annotation_col", annotation_col,
244
+ "--assay", assay,
245
+ "--img_width", str(img_width),
246
+ "--img_height", str(img_height)
247
+ ]
248
+
249
+ if plot_title:
250
+ cmd.extend(["--plot_title", plot_title])
251
+
252
+ # --- Subprocess Execution ---
253
+ command_executed = " ".join(cmd)
254
+ logging.info(f"Executing command: {command_executed}")
255
+
256
+ try:
257
+ result = subprocess.run(
258
+ cmd,
259
+ capture_output=True,
260
+ text=True,
261
+ check=True,
262
+ encoding='utf-8'
263
+ )
264
+ except FileNotFoundError:
265
+ raise RuntimeError("Rscript or the wrapper script not found. Please ensure R and the tool's R script are in the system's PATH.")
266
+ except subprocess.CalledProcessError as e:
267
+ error_message = f"ggsc R script failed for expression heatmap.\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}"
268
+ logging.error(error_message)
269
+ raise RuntimeError(error_message) from e
270
+
271
+ return {
272
+ "command_executed": command_executed,
273
+ "stdout": result.stdout,
274
+ "stderr": result.stderr,
275
+ "output_files": [str(output_plot)]
276
+ }
277
+
278
+
279
+ @mcp.tool()
280
+ def plot_expression_violin(
281
+ input_rds: Path,
282
+ output_plot: Path,
283
+ gene: str,
284
+ group_by: str,
285
+ assay: str = "logcounts",
286
+ plot_title: Optional[str] = None,
287
+ img_width: int = 7,
288
+ img_height: int = 5
289
+ ) -> dict:
290
+ """
291
+ Generates a violin plot of gene expression across different groups.
292
+
293
+ This tool wraps the `plot_violin_sce` function from the R/Bioconductor package `ggsc`.
294
+ The input must be a .rds file containing a SingleCellExperiment object.
295
+
296
+ Args:
297
+ input_rds: Path to the input RDS file containing a SingleCellExperiment object.
298
+ output_plot: Path to save the output plot image.
299
+ gene: The name of the gene to plot.
300
+ group_by: Variable in colData to group the violins by (e.g., "cluster").
301
+ assay: The assay to use for expression values (default: "logcounts").
302
+ plot_title: Optional title for the plot.
303
+ img_width: Width of the output image in inches.
304
+ img_height: Height of the output image in inches.
305
+
306
+ Returns:
307
+ A dictionary containing the execution details and output file path.
308
+ """
309
+ # --- Input Validation ---
310
+ if not input_rds.is_file():
311
+ raise FileNotFoundError(f"Input RDS file not found: {input_rds}")
312
+ if not gene:
313
+ raise ValueError("A gene name must be provided.")
314
+ if img_width <= 0 or img_height <= 0:
315
+ raise ValueError("Image width and height must be positive integers.")
316
+
317
+ output_plot.parent.mkdir(parents=True, exist_ok=True)
318
+
319
+ # --- Command Construction ---
320
+ cmd = [
321
+ "Rscript",
322
+ R_SCRIPT_EXECUTABLE,
323
+ "violin",
324
+ "--input_rds", str(input_rds),
325
+ "--output_plot", str(output_plot),
326
+ "--gene", gene,
327
+ "--group_by", group_by,
328
+ "--assay", assay,
329
+ "--img_width", str(img_width),
330
+ "--img_height", str(img_height)
331
+ ]
332
+
333
+ if plot_title:
334
+ cmd.extend(["--plot_title", plot_title])
335
+
336
+ # --- Subprocess Execution ---
337
+ command_executed = " ".join(cmd)
338
+ logging.info(f"Executing command: {command_executed}")
339
+
340
+ try:
341
+ result = subprocess.run(
342
+ cmd,
343
+ capture_output=True,
344
+ text=True,
345
+ check=True,
346
+ encoding='utf-8'
347
+ )
348
+ except FileNotFoundError:
349
+ raise RuntimeError("Rscript or the wrapper script not found. Please ensure R and the tool's R script are in the system's PATH.")
350
+ except subprocess.CalledProcessError as e:
351
+ error_message = f"ggsc R script failed for expression violin plot.\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}"
352
+ logging.error(error_message)
353
+ raise RuntimeError(error_message) from e
354
+
355
+ return {
356
+ "command_executed": command_executed,
357
+ "stdout": result.stdout,
358
+ "stderr": result.stderr,
359
+ "output_files": [str(output_plot)]
360
+ }
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_shim_server.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import ast
5
+ from pathlib import Path
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+
10
+ SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_server.py')
11
+ SERVER_NAME = 'biosci_bioconductor_ggsc'
12
+
13
+
14
+ class _ShimMCP:
15
+ @staticmethod
16
+ def tool():
17
+ def _decorator(fn):
18
+ return fn
19
+ return _decorator
20
+
21
+
22
+ def _load_functions():
23
+ code = SOURCE_SERVER.read_text(encoding="utf-8")
24
+ tree = ast.parse(code, filename=str(SOURCE_SERVER))
25
+ function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")]
26
+ namespace = {
27
+ "__name__": "__mcp_source__",
28
+ "mcp": _ShimMCP(),
29
+ }
30
+ exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace)
31
+ loaded = []
32
+ for name in function_names:
33
+ fn = namespace.get(name)
34
+ if callable(fn):
35
+ loaded.append(fn)
36
+ return loaded
37
+
38
+
39
+ mcp = FastMCP(SERVER_NAME)
40
+ for _fn in _load_functions():
41
+ mcp.tool()(_fn)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ mcp.run(transport="stdio")
BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-bioconductor-ggsc:
5
+ build: .
6
+ image: mcp-bioconductor-ggsc:latest
7
+ container_name: mcp-bioconductor-ggsc
8
+ ports:
9
+ - "8000:8000"
10
+ environment:
11
+ - MCP_SERVER_NAME=bioconductor-ggsc
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
+